update only attributes that are not empty - ruby-on-rails

I have a mass edit form. A user selects multiple existing records. And then clicks on a button which displays a new record form (all values are empty). User enters some data for each attribute and then clicks submit, which then updates all the selected records with the values specified.
The params hash submitted to the server looks like this:
{
'lead_ids' => [2,4]
'lead' => {
'name' => 'donato',
'email' => "",
'phone' => ""
}
}
So in this example, only the name attribute should be updated for lead with an id of 2 and 4.
This is what I came up with:
lead = Lead.new lead_params
leads = Lead.where(id: params[:lead_ids])
changed_attrs = lead_params.select {|param| !param.values.first.blank?}
leads.each do |lead|
lead.update_attributes changed_attrs
end
Is there a more Railsy way to do this?

I would refactor the code into something a bit cleaner, like this:
leads = Lead.where(id: params[:lead_ids])
hash = lead_params.reject { |k, v| v.blank? }
leads.update_all(hash)
You search for the leads, remove the pairs with empty values, then update all the leads with that hash.
Could even make it a two-liner:
hash = lead_params.reject { |k, v| v.blank? }
Lead.where(id: params[:lead_ids]).update_all(hash)

Related

i want to add numbering at the beginning of the name in the dropdown list in ruby on rails

in my controller i have written like this
#agendas = #agendas.each{ |e| puts e.name }
#agenda_alpha = #agendas.each_with_index.map { |agenda_key, i| ["#{i+1}.#{agenda_key}"] }
in my slim file i wrote this
label_method: ->(agenda) { #agenda_alpha },
This are the attributes in my rails console
i have to fetch the name attribute into the dropdown
It probably makes sense to translate the options into the required format in the controller or a helper first.
#options = %w[Apple Ball Cat]
#options = #options.each_with_index.map { |value, i| ["#{i}.#{value}", value] }
#=> [["0.Apple", "Apple"], ["1.Ball", "Ball"], ["2.Cat", "Cat"]]
Then just use this #options in your view:
f.select(:attribute_name, #options)

How to access hashes whose keys are arrays in Rails?

I am trying to access data that has a structure like this (the status of each user on specific dates). As you can see, the hash keys are all arrays. This data has been retrieved from the DB using group_by.
data = {
["active", "Rick"]=>["2019-07-09", "2019-07-10"],
["active", "Morty"]=>["2019-07-09", "2019-07-10"],
["active", "Summer"]=>["2019-07-09", "2019-07-10"],
["inactive", "Rick"]=> ["2019-07-01", "2019-07-02", "2019-07-03"],
["inactive", "Summer"]=>["2019-07-15"]
}
I would rather have this data be a nested hash, like below. Is there a way to restructure it?
I know that each item in the hash can be accessed like this: data[["active", "Summer"]]. I tried doing something like data[["active", "*"]] (to get the active status data for all users) but this did not work.
data = {
"active"=>{
"Rick"=>["2019-07-09", "2019-07-10"],
"Morty"=>["2019-07-09", "2019-07-10"],
"Summer"=>["2019-07-09", "2019-07-10"]
},
"inactive"=>{
"Rick"=>["2019-07-01", "2019-07-02", "2019-07-03"],
"Summer"=>["2019-07-15"]
}
}
This should work:
new_data = {}
data.each do |k, v|
new_data[k.first] ||= []
new_data[k.first] << { k.last => v}
end
But if you are in control of the db/query, perhaps it is better to retrieve your data from the db in the right format right away if possible.
You can do something like this -
new_data = { 'active' => [], 'inactive' => [] }
data.each do |key, value|
if key.first == 'active'
new_data['active'] << { key.last => value }
elsif key.first == 'inactive'
new_data['inactive'] << { key.last => value }
end
end

How do I get the count of nil and not nil values from a list of specific attributes from a table in a Rails 5 app?

Rails 5 app, User model is set up with Devise
I have a User model that was created with the Devise gem. I want to get the count of specific columns in the model that are nil and also not nil. For example, I am using this to get the count of ALL of the columns:
Nil: <%= user.attributes.values.count { |v| v.nil? } %>
Not Nil: <%= user.attributes.values.count { |v| !v.nil? } %>
But, I only want to find the count for certain attributes. I only want
name
cel
city
state
If I create a model method like this
def profile_attributes
[:name, :cel, :city, :state]
end
Then in my view:
= volunteer.profile_attributes.count => 4
Now, if city and state are nil I would want => 2
How do I get this to work?
If I understand the question correctly, given a volunteer as User object having profile_attributes method:
# not nil
(volunteer.profile_attributes.select { |attr| !volunteer.send(attr).nil? }).count
# nil
(volunteer.profile_attributes.select { |attr| volunteer.send(attr).nil? }).count
It was actually pretty obvious. I probably didn't explain myself well in the question. The way to do this is just to add select. So, you would have the following:
<% User.select(:name, :cel, :city, :state).each do |user| %>
Nil count
<%= user.attributes.values.count { |v| v.nil? } %>
Not nil count
<%= user.attributes.values.count { |v| !v.nil? } %>
<% end %>

Passing hashes into create action in Ruby on Rails

I'm trying to create a workout routine that gets created when a workout gets created by passing this in via ajax:
Parameters: {"utf8"=>"✓", "workout"=>{"name"=>"cool workout", "desc"=>"super cool"}, "exerciseorder"=>["4", "2"], "repssets"=>{"0"=>["40", "4"], "1"=>["60", "6"]}}
Here is what my Create action looks like in my Workout Controller:
exercise_order = params[:exerciseorder]
repssets = params[:repssets]
#workout = Workout.new(workout_params)
if #workout.save
WorkoutRoutine.create(
[
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
]
)
render :nothing => true
else
render json: #workout.errors.full_messages, status: :unprocessable_entity
end
If I use an opening and closing '[]' within the WorkoutRoutine.create, it tells me:
ArgumentError: When assigning attributes, you must pass a hash as an argument.
And when I change them to '{}' it tells me:
syntax error, unexpected ',', expecting =>
I've tried a myriad of different combinations and work-arounds but can't seem to figure out why it won't correctly parse the data and save it to the database, any help is very appreciated.
EDIT:
When I remove the initial {} and [] from the WorkoutRoutine.create:
WorkoutRoutine.create(
exercise_order.each_with_index.map { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => 20
}
},
repssets.map { |x| x.last }.each { |y|
{
:reps => y.first,
:sets => y.last
}
}
)
I get this error message:
ArgumentError: wrong number of arguments (2 for 0..1)
Edit2:
This is the jQuery code that sents to the data field via ajax:
var getId = $(".exercise-list").sortable('toArray');
ary = []
$(getId).each(function () {
id = $(this[0]).selector;
var reps = $("#reps" + id).val();
var sets = $("#sets" + id).val();
ary.push([reps, sets])
});
var orderRepsSets = { exerciseorder: getId, repssets: ary }
var formData = $('#new_workout').serialize();
var data = formData + '&' + $.param(orderRepsSets);
$.ajax({
url: $("#new_workout").attr('action'),
method: 'POST',
data: data,
success: (function() {
....
});
Did I get it correctly that you want to create multiple WorkloadRoutine objects, one for each exercise with the corresponding repetitions, the position, etc. If yes, then in order to do that you will have to pass an array of hashes (one hash for each object) to the WorkoutRoutine.create() function. As engineersmnky correctly stated in his comment, the data structure you are currently passing is more like [[{...},{...},{...}],[{...},{...},{...}]], but instead it should be just [{...},{...},...]. Do achieve that, something like this should do the trick:
WorkoutRoutine.create(
exercise_order.map.with_index { |x,i|
{
:exercise_id => x,
:position => i,
:workout_id => #workout.id,
:reps => repssets[i.to_s].first,
:sets => repssets[i.to_s].last
}
}
)
If you could change repssets to an array like exercise_order you could even remove the string cast for getting the reps and sets, which would simplify the whole think even more.
If it comes for errors they are quite self explanatory. But let's start from beginning..
I assume that WorkoutRoutine is an ActiveRecord::Base model. The WorkoutRoutine.create method gets 0 or 1 argument which should be a Hash or a block.
In the first iteration you were passing an Array instead of Hash, so it looked like:
WorkoutRoutine.create([a, b]) # => ArgumentError: When assigning attributes, you must pass a hash as an argument.
On the second iteration you stripped away the square brackets, what gave you 2 arguments instead of one Hash:
WorkoutRoutine.create(a, b) # => ArgumentError: wrong number of arguments (2 for 0..1)
If you read errors carefully you will start getting the idea what's happening.
About the workout routine itself
From what you specified I would assume that you want something like:
Workout has many Routines
Routine belongs to Workout and Exercise
Routine is composed of fields like
position/order,
number of repetitions,
number of sets
If my assumption is correct, then you want to use nested_attributes and then have parameters and controller like
# given parameters as workout_params
{
workout: {
name: "workout name",
desc: "workout description",
routines_attributes: [
{ position: 1, exercise_id: 4, repetitions_number: 40, sets_number: 4 },
{ position: 2, exercise_id: 2, repetitions_number: 60, sets_number: 6 }
]
}
}
# Controller
def create
#workout = Workout.new workout_params
if #workout.save
redirect_to # ...
else
render :new
end
end
private
def workout_params
params.require(:workout).permit(:name, :desc, routines_attributes: [:position, :exercise_id, :repetitions_number, :sets_number])
end
It should be strait forward how to then create a view with fields_for and update models with proper associations

Search ignoring certain blank parameters

I have a simple search action that has 3 parameters and a where method to search a model. If I search and some of the parameters are nil, it will not return the records I want. I want it to search the database by only using the parameters that are not nil/blank. So if only one category is entered and sent in the parameters, I want my controller to ignore the other two parameters. However, if the other parameters are present, I want them to be included in the search.
I've tried many approaches but I can't get it to work properly. Here's my code.
hash = []
cat = :category_id => params[:category_id]
col = :color_id => params[:color_id]
brand = :brand_id => params[:brand_id]
if params[:category_id].present?
hash += cat
end
if params[:color_id].present?
hash += col
end
if params[:brand_id].present?
hash += brand
end
#results = Piece.where(hash).preload(:item).preload(:user).group(:item_id).paginate(:page => params[:page], :per_page => 9)
I've put the variables into strings and hashs, called to_a, joined them with (","). Nothing works.
Thanks
Try this code.
criteria = { :category_id => params[:category_id], :color_id => params[:color_id],
:brand_id => params[:brand_id] }.select { |key,value| value.present? }
#results = Piece.where(criteria).preload(:item).preload(:user).group(:item_id).
paginate(:page => params[:page], :per_page => 9)

Resources