I've just upgraded from Rails 4.0.0 to 4.1.0.
Now I got this error:
When assigning attributes, you must pass a hash as an argument.
Here's the relevant part of my controller action:
# app/models/users_controller.rb
def create_user
#user = User.new()
#user.update_attributes(params[:user])
How can I solve this?
There are similar problems posted here on SO, byt my problem is different to the posted ones, because the hints which were given there aren't a solution.
If you want to allow params[:user] to be passed in empty, you can do this to prevent throwing an exception:
#user.update_attributes(params[:user]) unless params[:user].blank?
However, this might be a strange behaviour, since the controller action is meant to create a User.
In Rails, there is also a method present?, that is the inverse of blank?. You can use whichever one is more legible to you.
#user.update_attributes(params[:user]) if params[:user].present?
Related
In every example for create or update action that I see, they have something like this.
def create
#user = User.new(params)
if #user.save
redirect_to #user
else
render 'new'
end
end
Here how the redirect_to #user goes to show action of the controller. Can anybody explain me this?
Let's start from the redirect_to documentation.
redirect_to post_url(#post)
is used to redirect to a specific URL generated using one of the Rails route helpers. In your case, it means you can write
redirect_to user_url(#user)
However, redirect_to also accepts a single model instance. Behind the scenes, redirect_to relies on url_for to generate an URL from the input when the input is not an object.
url_for, in turns, when you pass an instance of a model by default will compute the corresponding GET action to view the model.
In conclusion, the following code:
redirect_to #post
is equivalent to
redirect_to post_url(#post)
However, personally I prefer the explicit version. Even if it's a little bit longer, I've noticed it tends to produce more maintainable code in the long run. Writing the full route will allow you to easily search your code base when you need to debug or rename routes.
It's all in the documentation.
Record - The URL will be generated by calling url_for with the options, which will reference a named URL for that record.
So, url_for will be called on your #user which will produce the url for redirection. (/users/1234 or something)
This is just one of many ways to do redirection, by the way.
In Ruby (the language which supports Rails), you set #instance_variables to store data for that request. Whilst you can store many types of data in a variable, Rails often assigns #model objects to them...
#user = User.find 1
#-> #user = <User id: "1", name: "john" .... >
This means that whenever you use a helper (such as redirect_to, or even a path_helper), you're actually able to pass the object to it and Rails will extract the data it requires.
For example...
user_path(#user)
edit_user_path(#user)
In the instance of a path, the helper extracts the id of the object; redirect_to extrapolates the functionality to route the request to the show path for that user.
Passing redirect_to accepts an object, invoking the show action for that object.
The reason why this is important is to understand that Ruby (& by virtue Rails) is object orientated.
Object orientated programming means that you should be dealing with objects (not variables).
In the case of Rails, each model should be an object. Every time you load the model, or create a new instance of it, you should be dealing with the object rather than the data.
Therefore, allowing you to pass #objects to methods such as redirect_to is just another way to make Rails more object-orientated.
So I have this application, where you create a user and then you can add movies and shows into a database. Like a bad version on IMDB?
Now.. I have this controller: https://github.com/Veske/form/blob/ryhm/app/controllers/movies_controller.rb
I have set up routes for movies and also it has all the necessary view files.. but when I attempt to go on a page to edit one of the movies: http://whatever.com/shows/1/edit for example, it gives me a error:
Couldn't find User with id=1
def correct_user
#user = User.find(params[:id])
redirect_to root_url unless current_user?(#user)
end
end
params
{"id"=>"1"}
Now.. why is it thinking that the param I throw at it, is a #user param when I have a update and edit controller made specially for Movies?
You don't seem to understand routes. The context in which you are using params[:id] is the movies controller, hence, the id would be the movie id. At the same time, you're authenticating (?) with the same param, giving you the error.
For basic authentication you could use the session hash, and for a more advanced one there are lots of gems, being devise the most popular.
PS: use rake routes to check your available routes and its URL params.
Your shows_controller.rb file calls correct_user before running the edit action you are calling, and it is specifically looking for a user on line 70. So it would make sense that you are getting this error if there is no user with an ID of 1.
Why is it thinking that the param I throw at it, is a #user param when I have a update and edit controller made specially for Movies?
Because you have a before_action filter at the top of your controller that is being called on the edit action.
You get into the correct_user method, which is using finding a user based on params[:id] . To test that this is your actual problem, you might want to try to change line 68 in your controller to:
#user = User.last #quick fix
The above could be used as a quick fix -you shouldn't get that error you posted about any more, as long as your user is signed in. If this allows you to avoid the error, you then need to concern yourself with properly assigning this User#id value when this correct_user method is called by your controller.
This is a MoviesController, so the params[:id] is actually the movie_id, i.e., the number "1" in your url "http://whatever.com/shows/1/edit". Not the user_id. So it throws the exception at line #user = User.find(params[:id]).
I went through your code but can't find where the correct user_id should come from. The Movie model doesn't belongs_to user. You should check out where the user come from.
So I have many places in my program where I use the: #user = User.find(params[:id])
Now, I wanted to make a new method in ApplicationController so that all my controllers would be able to use the method so that I would not have to repeat my self so much.
def find_user(params[:id])
#user = User.find(params[:id])
end
So now when I want to display users in some controller, I just type the find_user(params[:id]) in an action. But this doesn't seem to work for some reason.
First I'd like to say that although it's a common call, I would not make a method for it, because it is already essentially just calling one method, but here:
You're making it a little too complicated:
In the application controller
def find_user(user_id)
#user = User.find(user_id)
end
In the controller you're using it in
find_user(params[:id])
Alternatively if for some reason you don't want to write params everytime
In the application controller
def find_user(paramsicle) # params might be reserved
#user = User.find(paramsicle[:id])
end
In the controller you're using it in
find_user(params)
EDIT: It'd probably be helpful if I explained why yours doesn't work...
Yours is fine, except that method arguments (the stuff in the parenthesis) should just be a identifier except in special cases (optional arguments, the *args things). Read more about it here. The problem wasn't with params, but with trying to access id while still in the arguments section. That's why it was find to call paramsicle[:id] afterwards.
I have a form that allows a user to update their profile information, but I would like to prevent some information from being changed. I also would like to keep my controller code very simple. In the update action of my Users Controller, I have the following code:
def update
#user = Users.find params[:id]
if #user.update_attributes(params[:user])
flash[:notice] = 'Update successful.'
redirect_to user_path(#user)
else
render :action => :edit
end
end
This is very clean and simple, and I like that. What I don't like, however, is that a user can add a field to the form, with the same name as an attribute, and use it to modify forbidden attributes. Is there a simple way to do this, or do I need to devise a way to do this myself?
One method I was considering was to generate a hash value, using a hash-based message authentication code, of all the form's element names. This message access code would be a hidden value in the form. Then, once the form is submitted, I would calculate the message access code (MAC) again using the names of the parameter Hash's keys. If the two MACs are different, or if the first MAC is missing from the parameter Hash, I would throw an error. I would rather not spend the time implementing this if there was already and easy solution out there.
Thanks.
On your model you can use attr_protected or attr_accessible to blacklist or whitelist attributes when being set via mass assignment (like when a form is submitted).
Rails will prevent mass assignment if you use attr_protected :protectedcolumn (blacklist) or attr_accessible :safecolumn (whitelist) within your model. More information on this topic can be found in the Ruby on Rails Security Guide (Section 6.1)
I have a standard query that gets the current user object:
#user = User.find_by_email(session[:email])
but I'm putting it as the first line in every single controller action which is obviously not the best way to do this. What is the best way to refactor this?
Do I put this as a method in the Application controller (and if so, can you just show me a quick example)?
Do I put the entire #user object into the session (has about 50 columns and some sensitive ones like is_admin)?
Or is there another way to remove this kind of redundancy?
I suggest making it into a helper placed in the ApplicationHelper module
def current_user
return nil if #user === false
#This ensures that the find method is only called once
#user = #user || User.find_by_email(session[:email]) || false
end
I prefer the above usage instead of the standard #user ||= User.find... because it prevents repetitive queries if the user record isn't found the first time. You could also just bang the find method: find_by_email! to make it throw an exception when the user can't be found
You could specify a before_filter, which is automatically called at the beginning of every controller action. Read up on it to see how to use it.