How to use "current_user" with devise in Rails? - ruby-on-rails

The code in question is in the create action of the Article controller.
I want each new article created to belong to the currently signed in User.
I checked that there is a user_id column in the Articles table and the correct association statements are in both models.
When I try and submit the form for a new Article, I get this error:
undefined method 'article' for nil:NilClass
in reference to this line of code:
#article = #current_user.article.new(article_params)
Am I using current_user incorrectly? How would one go about making the new item belong to the currently signed in user?

You should use current_user method:
#article = current_user.articles.new(article_params)
#current_user instance variable isn't set (thus it evaluates to nil) until current_user is called.

current_user is basically a convenience method/variable which takes the user id from the session hash and grabs that user (which is the current user that is active on that specific request)
so, when you want to have something only on the current user, just called current_user
however, there are some things you should watch out
make sure that the current user is logged in.
so make sure that when calling current_user.article.new(params) that current_user isn't nil, or it will throw an exception
when you want something to be only on that specific logged in user make sure to do as you did in the question
such as:
current_user.articles instead of Article.where(user_id: ...)
this will help you avoid problems in the future, and it may be faster in some situations
and 3. I think it should be current_user.articles.new(...)

It should be current_user, not #current_user.
current_user is a method, not an instance-variable.

Related

How to write find clause using where user_id =current_user.id

I have a one to many relationship between 1 user and many challenges. I am using rails 4 with Devise
I would like to list the challenges for the current user.
So far I have
#challenge = User.find(current_user.id).challenges
and I've also tried
#challenge = User.find(current_user).challenges
and I also tried #challenge = current_user.challenges
but it doesn't work. I get the error "undefined methodeach' for nil:NilClass"` which usually means #challenge isn't being passed over to the view Have I made some kind of syntax error?
if I do User.find(1).challenges in the rails console it works fine so presumably I'm not using current_user.id correctly
It looks like when you are trying to access the page user is not logged in and hence current_user is set to nil.
current_user.challenges will do the job of fetching all challenges associated to this user. You can use user_signed_in? method from devise before calling this code to ensure current_user is always available.

Unable to edit database entry

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.

Rails/devise current_user accessed in model but it's nil

I needed to access the current_user in my another model. I googled and found a few solutions and tried this.
But just one difference: I didn't set the User.current_user in the controller. WHY?
Like in the answer I followed I'm not adding data to my second model from views but by rendering data via a url (with open-uri, it's csv data I'm fetching).
So in my second model I do:
Person.create(username: User.current_user.username)
It gives:
NoMethodError: undefined method `username' for nil:NilClass
which isn't working (which is obvious I guess). In console when I do User.current_user it shows:
1.9.3p448 :002 > User.current_user
=> nil
I think why it isn't working is because I'm accessing the User.current_user directly from model and model cannot get the current_user unless it is given that. (right?)
But this would definitely work if I access it via a login page and set the User.current_user in the controller.
But as I'm directly fetching the data from url, I'm directly making new entries for my Person model in model itself.
So how do I set the User.current_user?
Is there any workaround for this? Edit's for the question's title are required.
current_user is available by default as a helper method within Devise. From the documentation:
# For the current signed-in user, this helper is available:
current_user
The current_user isn't accessed directly via the model, per se, but rather, though the Devise module, which looks up the User object that is logged into the current session, and then returns to current_user.
Thus, the current user isn't accessed via User.current_user (there is no current_user method on User, as the error message is saying). You access it purely by invoking the current_user helper method within a controller or view.
UPDATE:
You're well advised to keep your controller and model layers separate. One way of doing what you've proposed is to create a class function on the Person model wherein you explicitly pass the username of your User object from within your controller:
# in your controller
Person.create_with_username(:username => current_user.username)
# app/models/person.rb
def self.create_with_username(username)
self.create(username)
end

ActiveRecord callback access to session data

I am trying to add current user information to record using ActiveRecord callback, but I don't see way how to do that.
I tried Thread.current[:user], but in results I see that thread value is accessed from another user sessions.
I am using Passanger in production, but in the same time I am using acts_as_audited who get user value correctly.
Whats the best/safest way how to do that?
The current user is not accessible from within an ActiveRecord model. This is due to the separation of concerns in Rails--the current user and the session are concepts that pertain to the realm of the controller.
You need to get the relevant data and pass it into the model in order for your callback to work. One way to do this is with an accessor method:
# model
attr_accessor :current_user
def my_callback
# do something with current_user
self.some_attribute = current_user
end
# controller
#model = MyModel.find(params[:id])
#model.current_user = current_user # assuming you have a controller method that does this
#model.save!
You should rename the current_user accessor to be meaningful: eg. if you are tracking the author of a blog post, call it author or responsible_user or some such, since current_user means nothing once the model is saved.

Where do I put the Current user query so as to not repeat per controller?

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.

Resources