So yesterday I asked a question about getting the projects for a current user. Well I ran into an issue. See that thread here. So today I was working on displaying categories and tasks for the user along with any discussions. when I loaded up the page to view this I got:
NoMethodError in UsersController#show
undefined method `projects' for #<User:0xad5823f4>
Rails.root: /home/adam/Documents/Aptana Studio 3 Workspace/StartPoint
Application Trace | Framework Trace | Full Trace
app/controllers/users_controller.rb:25:in `show'
Request
Parameters:
{"id"=>"4"}
This is currently whats in my users#show method:
def show
#projects = current_user.projects
#tasks = current_user.tasks
#categories = current_user.categories
#discussions = current_user.discussions
end
the table names are projects, tasks, categories and discussions. I have no problem using this any where else so why am I seeing this error?
if you need more code I can show it. the error is pretty clear but again I use this type of call else where to display project specific information such as a projects categories or tasks. could it be something with my relationship between a user and categories, discussions, tasks and so on? because I do have user_id in each table that is listed in the show method, among others.
Also I thought of this, this morning since were on this topic: current_user seems wrong because if I click on Bobs profile I will see all my stuff no? should it not be something more specific to the users id?
Have you established the association in each model?
class User < ActiveRecord::Base
has_many :projects
end
Also be sure that current_user is actually a User object and not some thin proxy.
Generally you need to distinguish between the session user and the displayed user. This is why names like current_user are harmful. I'd recommend using session_user to refer to the logged in user and #user to refer to the user being displayed.
Some login systems define only a current_user method, but to alias this isn't hard. It's just defective by design in that case.
Related
I'm using Ruby on Rails 5 and I want that depending of the role of a User, the user can post 5 Posts or 10 or 15, and that is just a part of the several possible situations that I have to check for Authorization (for things that are not Posts for instance I have other situations that are a bit more complex), so I started using Cancancan (v 1.15.0) few days ago to don't make the User model too huge, so this way I have one class for each Role and in ability.rb I just merge the classes depending on the Role.
The problem is that using Cancancan apparently it checkes the Authorization only once. For example in Creating a Post, In Post#create the first line of code is:
authorize! :create, Post
In the Role class of the user, I have this code:
if user.posts.size < 10
Rails.logger.debug "If-size: #{user.posts.size}"
can :create, Post
else
Rails.logger.debug "Else-size: #{user.posts.size}"
cannot :create, Post
end
I have some tests with RSpec and I see the first time the current_user (the one with that specific role) creates a Post using the controller (I mean, not by FactoryGirl or any other way that is not using the controller), it appears in log/test.log:
If-size: 0
But neither the Else nor the If appears ever again in the log, it doesn't matter how many Posts I create by the Controller, it only evaluates this condition the first time and the User get authorized to creates as many Posts as he wants because the first time the condition in the If is true and it is not evaluated each time the method create of the controller Post is called.
EDIT: Solved by the method suggested by MarsAtomic. Thanks! :)
CanCanCan isn't meant to dynamically evaluate roles based on conditions within your application. That type of functionality is best handled by your application's own logic, rather than an authorization tool.
You should consider adding a column to your roles table that indicates how many posts a particular role is allowed. This column would allow you to check on the number of posts each user (via role) is allowed to create.
Then, in your posts_controller.rb, you can wrap your post creation logic in a block that runs only if the user has not exceeded the maximum number of posts allowed:
def create
if user.posts.size < user.role.max_posts
#post = Post.new(post_params)
#post.save
redirect_to #post
else
# flash an error on the page or redirect to an error page
end
end
Task: Showing the profile of an employee straight away after his login.
Issue:
class WelcomeController < ApplicationController
def index
#employee = Employee.find_by_email(params[#current_user.email])
end
end
I tried to code in many ways to associate the email of the current user with his respective details from the employees table and the farthest that I could get was it:
I am sure that I am writing something wrong in this line inside the index thing, but I am researching and all things that I found and tried did not get the employee related to the current user.
Try with this code:
class WelcomeController < ApplicationController
def index
#employee = Employee.where(email: current_user.email).first
end
end
When using Devise, the current user is an instance variable, so you don't need to prefix it with #.
If you are going to have a lot of users, is a good practice to create an index in your database for the email column.
I like kjmagic13's answer. Use the
#current_user.id
It pulls all the info associated with the user from the database
Mori's answer is also good.
The following SQL line in your logs corresponds to the Employee.find_by_email call:
SELECT "employees".* FROM "employees" WHERE "employees"."email" IS NULL LIMIT 1
As Mori pointed out, this means you're finding the employee with a nil email, which means that params[#current_user.email] is nil. Since you have no parameters, there's no need to refer to the params hash regardless. You should refer just to the #current_user.email:
Employee.find_by_email #current_user.email
As Mori's answer states, you probably didn't intent do use #current_user.email as a hash key into params. I think you're trying to look up the employee record for the current user by email (not by an email submitted as a parameter), like so (also avoiding deprecated find_by_* helpers):
#employee = Employee.find_by(email: #current_user.email)
I don't think you want to try to do Employee.find(#current_user.id) - that's just going to look up the Employee whose id matches the current_user's id - unless Employee and User use the same table that's not going to be meaningful
Why not just find by the ID? find_by_* are old.
#employee = Employee.find(#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.
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.
I have a rails app that involves users, essays, and rankings. It's pretty straightforward but I'm very new to rails. The part I'm having trouble with right now is the create method for the rankings.
The Essay class has_many :rankings and the Ranking class belongs_to :essay
in the rankings controller I have:
def create
#ranking = #essay.rankings.build(params[:ranking])
flash[:success] = "Ranking created!"
redirect_to root_path
end
but I get the error: undefined method `rankings' for nil:NilClass
I need each ranking to have an essay_id, and I believe build updates this for me.
I thought that rails give me the rankings method because of the relation I set, and why is #essay nil?
Thanks in advance
Build doesn't save. You should be using new and then save. I'd provide you sample code, but you haven't really given us a clear picture of what you have going on. That #essay instance variable is being used before it's defined, and I'm not really sure how your application is determining which essay the ranking belongs to.
You might wanna give Rails Guides a read.
I think this is what you intend to do:
# EssayController#new
def new
#essay = Essay.new(params[:essay])
#ranking = #essay.rankings.build(params[:ranking])
#...
end
Take a look at nested model forms , it should get you in the right direction.