mass updating attributes - ruby-on-rails

I am using Sinatra but I am guessing this also applies to Rails (if not, please remove the tag or let me know and I will remove it).
I have a ActiveRecord::Base class User. It has tons of attributes and I am displaying a page that will allow someone to update the a particular user. Problem is, I have a hard time implementing the update functionality in a DRY manner. What I mean is, when I get the params with a POST request, I can do:
a_user.update_attributes params
because params contains bnch of other crap too (like :splat - what's that?) and it will throw an unknown attribute error. What I instead have to do is this:
a_user.update_attributes {:attrA => params[:attrA],
:attrB => params[:attrB], ...etc }
(keep in mind there are A LOT of attributes)
Is this how I should do this? To me, for some reason...it doesn't feel right. If for example, I have another Model that needs to be updated in a similar manner, I have to rewrite manually all attributes again.
What I am looking for is somethign like:
a_user.filter_and_update_attributes params
where filter_and_update_attributes automatically filters params of any bad/unknown attributes and I can use this anywhere with any models with have to rewrite so much useless code.
Thanks

If you structure your form like this:
<form action="/users" method="post">
<input id="user_email" name="user[email]" type="text">
<input id="user_name" name="user[name]" type="text">
<input id="user_phone_number" name="user[phone_number]" type="text">
...
<input id="user_email" name="user[email]" type="text">
<input name="commit" type="submit" value="Submit">
</form>
you should be able to use the params like this:
#user.update_attributes params[:user]
When you name your html fields like user[email], the params hash looks like:
{ user: { email: "example#example.com", name: "Example" } }
So using params[:user] gets you that nested hash of parameters that belong to the user.

You can filter a hash using select. Find a list of all attributes of your model and test if the keys are in that list:
attrs = a_user.attributes.keys - User.protected_attributes.to_a
a_user.update_attributes params.select {|k,v| attrs.include?(k)}

Related

undefined method `map' for "1,2":String

I need to pass an array in a params, possible? Values can be, for example, ["1","2","3","4","5"] and these are strings but needs to eb converted to integers later.
I use a react_component in between a rails form_for. The html is like this:
<input type="hidden" name="people_id" id="people_id" value={this.state.people} />
The people array looks like this:
How can I pass the array in the value of the hidden field? The server error I got was
Im trying to do something like this in a model:
ids = params[:people_id]
ids.map do |b|
Foo.create!(people_id: b.to_i)
end
If I ids.split(",").map I get symbol to int error.
Edit:
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
Still not sure what the issue is as nothing works. Here is a minimal reproduction of my code:
This answer is my react component and that's how I add to the array. Still in the component, I have the hidden field:
<input type="hidden" name="[people_id][]" id="people_id" value={this.state.people} />
_form.html.erb:
<%= form_for resource, as: resource_name, url: registration_path(resource_name), :html => { :data => {:abide => ''}, :multipart => true } do |f| %>
<!-- react component goes here -->
<%= f.submit "Go", class: "large button" %>
<% end %>
The story is, guest can select few people during registration in one go. Those people will be notified when registration is complete. Think of it as "I am inviting these people to bid on my tender". Those numbers, in the array, are user_ids.
users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# POST /resource
def create
super do |resource|
ids = params[:people_id].pop # logs now as "people_id"=>["1,2"]
resource.save!(ids.split(",").map |b| Foo.create!(people_id: b.to_i) end)
end
end
end
New error on line resource.save:
no implicit conversion of Symbol into Integer
Edit #2
If I only have, in the create method:
ids.split(",").map do |b|
resource.save!(Foo.create!(people_id: b.to_i))
end
It works! Foo is created two times each with the correct people_id.
Because I am creating more objects: Bar, I do not know how to do that in:
resource.save!(<the loop for Foo> && Bar.create!())
The flow must be:
Device creates the User
Foo is created with the loop
Bar is created
etc
It has to be done that way as an User object is created on the fly.
In Rails you use parameter keys with brackets on the end to pass arrays.
However you should not concatenate the values as a comma seperated list but rather send each value as a seperate param:
GET /foo?people_ids[]=1&people_ids[]=2&people_ids[]=3
That way Rails will unpack the parameters into an array:
Parameters: {"people_ids"=>["1", "2", "3"]}
The same principle applies to POST except that the params are sent as formdata.
If you want a good example of how this works then look at the rails collection_check_boxes helper and the inputs it generates.
<input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
<label for="post_author_ids_1">D. Heinemeier Hansson</label>
<input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
<label for="post_author_ids_2">D. Thomas</label>
<input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
<label for="post_author_ids_3">M. Clark</label>
<input name="post[author_ids][]" type="hidden" value="" />
Updated:
If you intend to implement you own array parameters by splitting a string you should not end the input with brackets:
<input type="hidden" name="[people_id][]" value="1,2,3">
{"people_id"=>["1,2,3"]}
Notice how people_id is treated as an array and the input value is the first element.
While you could do params[:people_id].first.split(",") it makes more sense to use the correct key from the get go:
<input type="hidden" name="people_id" value="1,2,3">
Also you don't really want to wrap the "root" key in brackets. Thats used in rails to nest a param key in a hash eg. user[name].

Query string not being passed to controller

I have what should be a relatively simple form in Rails that I'm using to send an email for two different previews, a desktop preview and a mobile preview.
<form id="email-form" role="form" action="<%= action_name == 'desktop_preview' ? email_preview_newsletter_path(#newsletter) : email_preview_newsletter_path(#newsletter, mobile: 'true') %>">
<label for="email_address">Email</label>
<input type="email" id="email_address" name="email_address" value="<%= params[:email_address] %>" placeholder="PLEASE ENTER EMAIL">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" value="Send" class="btn btn-primary"></input>
</form>
Right now I have it setup so that both previews get sent to the same endpoint, '/email_preview', but in the case of the mobile_preview I want to pass in a 'mobile' query string so that it ends up looking like this:
'/email_preview?mobile=true'
When I inspect the form on the page everything looks in order, however when it gets passed to the controller the 'mobile' part of the query string disappears and only the 'email_address' exists.
I suppose I could pass in the mobile value as a hidden field, but something about that just doesn't feel right to me. What is the best way to setup this form so that both the 'mobile' and 'email_address' key value pairs are passed as query strings when sent to the controller?
In the process of writing out this question I realized exactly what the problem was:
I had the form setup as a GET request as opposed to a POST request.
This was causing any pre-established query strings to get erased in the process of setting up the GET params being defined in the form (in this case, the 'email_address' param). Changing the form from GET to POST, (i.e. form method="POST")
Took care of this issue. Please note that if you are going to manually setup a form like this in rails then you also need to explicitly take care of the csrf token. This can be done by inserting the following input with the helper method into your form:
input type="hidden" name="authenticity_token" value="<%=form_authenticity_token%>"

Simple way to add a parameter to current URL in Rails 4?

On a particular page in my website, there are a variety of potential URL parameters:
http://www.example.com/my_webpage?at=2014-01-01&page=5
Now I want a simple way to add a parameter to that like this:
http://www.example.com/my_webpage?at=2014-01-01&page=5&records=100
So I tried the following HTML with embedded Ruby:
<form action="<%= request.original_url %>" method="get"># of records <input type="text" name="records"/></form>
The problem is the resulting page that opens is:
http://www.example.com/my_webpage?records=100
Essentially, the old parameters get wiped away. What's an easy way to retain them? I could loop through the params hash and add hidden_tags (I'd have to selectively exclude params not part of the request params), but I would expect with such a common use case scenario there's a better easier way.
While there isn't an easy Rails way of doing this automatically, Rails does provide access to the request parameters through the request.query_parameters hash. So I simply needed to add this in the form:
<% request.query_parameters.each do |key, val|%>
<input type="hidden" name="<%= key %>" value="<%= val %>"/>
<% end %>

How I use post method in Ruby on Rails

when I submit one page to another, the sites shows the information!
Someone told me to use post to avoid it. however, when I use post method it doesn't work.
<form action="\look\at", method="post">
Select your new car's color.
<br>
<input type="radio" name="radios1" value="red">red
<input type="radio" name="radios1" value="green">green
<input type="radio" name="radios1" value="blue">blue
<br>
<br>
<input type="submit"/>
</form>
When I submit I can not see \look\at page. but I delete method="post" It works well.
How I use post method?
Check your routes (in config/routes.rb).
Probably you will have a line
get '/look/at', to: 'look#at'
replace get by post and it will accept a post.
Since you are not using the rails form helper or view mechanism (erb/haml) (please start using them, why write plain html????), you have to skip the authenticity token check (please note that this check is there for a reason: it is a protection against cross-site request forgery).
Just add the following line at the top of your controller:
skip_before_filter :verify_authenticity_token

How to assign a value to a accepts_nested_attributes_for model

This is a follow-up to this question where's the appropriate place to handle writing the current_user.id to an object I have the following model. An item which has_many assets. I'm am using accepts_nested_attributes_for :assets in the item.
I'd like to assign the current_user.id value to each asset. Normally I just do #item.update_attributes(params[:item]) to save it. Is there a simple one line way of setting the user_id for each asset in this scenario?
Looking in dev log, I see the value here:
item[assets_attributes][10][asset]
Should I just iterate through all of these and set a user_id value?
thx
here's some more of the html (items -> menu_item; had left out above to simplify). The proposed controller sol'n below does not seem to work. I'm fine with doing at the level of controller. Any help appreciated.
<div class='image-row'>
<input id="menu_item_assets_attributes_18_asset" name="menu_item[assets_attributes][18][asset]" type="file" />
<input id="menu_item_assets_attributes_18_description" name="menu_item[assets_attributes][18][description]" size="30" type="text" />
</div>
<div class='image-row'>
<input id="menu_item_assets_attributes_19_asset" name="menu_item[assets_attributes][19][asset]" type="file" />
<input id="menu_item_assets_attributes_19_description" name="menu_item[assets_attributes][19][description]" size="30" type="text" />
</div>
<div class='image-row'>
<img alt="D5cc413a1748fb43b0baa2e32e29b10ac2efda10_huntbch_thumb" src="/images/371/d5cc413a1748fb43b0baa2e32e29b10ac2efda10_huntbch_thumb.jpg?1329917713" />
<div class='img-row-description'>
<label for="menu_item_assets_attributes_20_description">Description</label>
<input id="menu_item_assets_attributes_20_description" name="menu_item[assets_attributes][20][description]" size="60" type="text" value="here is my comment" />
<label for="menu_item_assets_attributes_20_destroy">Destroy</label>
<input name="menu_item[assets_attributes][20][_destroy]" type="hidden" value="0" /><input id="menu_item_assets_attributes_20__destroy" name="menu_item[assets_attributes][20][_destroy]" type="checkbox" value="1" />
</div>
This answer is probably a little bit muddier than your previous one.
If each asset must have an item, then it might be more sensible to remove the idea of an owning user from an asset entirely: you can always find the owning user by querying the attached item, something like #asset.item.user. However, if users can own assets independently of items, I don't think this will work for you.
If assets are always created in a nested manner for items, a before_create for the asset could assign the value you want. Something like this in asset.rb:
before_create :assign_user
def assign_user
self.user = self.item.user if self.item && self.item.user
end
Finally, if you just want to do it in the controller, Wolfgang's answer is really good and will add the user_id to each asset_attributes.
How about iterating over the params array and setting the value like so:
params[:item][:assets_attributes].map! do |asset_params|
asset_params[:user_id] = current_user.id
end
Now you can use update_attributes( params[:item] ) as before.

Resources