How to achieve simple scoping in your views such as:
<% if #user.admin %>
where "admin" is the following scope in user.rb:
scope :admin, where(role: "admin")
there is a column Role which is a string in the Users table
I've done the same thing before with another Model (but not a devise user model) to which I could later call
<% if objective.completed %>
right after calling an each method in the objectives.
However
When I do the exact same thing to a user model I get an
undefined method `admin' for #<User:0x00000107e39038>
How could I make it work? I've been digging for hours.
For a no scope workaround try:
<% if #user.role == "admin" %>
You simply can't use scopes this way. Scopes are used as class methods, so if you run
User.admin
it returns list of users matching given condition. What you need is an instance method. Add it to your user.rb file:
def admin?
admin == 'admin'
end
and you will be able to use it in your view:
- if #user.admin?
anyways, you should definitely reconsider storing roles as string in users table. Try to create another table called roles.
Scopes are usually class level methods, having said that, how can you access it with an instance. I think, that is why the error says undefined method.
Related
My main difficulty comes from understanding the relationship that the _follow and _unfollow partials have with the create and destroy methods defined in the RelationshipsController from Chapter 11.2.3. I'll just focus on the act of unfollowing a user for now (since the act of following is mostly analogous).
Hartl defines the partial for unfollow as such:
<%= form_for(current_user.relationships.find_by_followed_id(#user), html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>
and the corresponding destroy action as such:
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
redirect_to #user
end
What I am having trouble understanding is:
The #user variable in the first line of the unfollow partial .. is this a) defined in the show action that currently displays the page, or b) defined in the destroy action? It appears that the form_for helper already finds the #user to be destroyed, so why does the destroy action needs to find the #user all over again to be destroyed in the controller?
In the destroy method, the #user is found by first finding the Relationship id. I don't see how the Relationship id is passed into the URI in the first place (since seeing a particular user to unfollow shows up as /users/2), much less how it is used to find the #user to destroy. I understand that each Relationship table has an id, a followed_id, and a follower_id, but do not see how the id element itself comes into play here.
Thanks, for reading, and for answering my questions!!
1.) If the partial is rendered within the show action, the variable #user must be defined in that action. Rails won't execute the destroy method so the variable definition in there will never be executed.
Since HTTP is a stateless protocol, the server needs to create the necessary state on every request. That's why #user must be defined in every action.
2.) Where did you check that the url is "/users/2"? As I don't see the whole code I can only make guesses but the following line current_user.relationships.find_by_followed_id(#user) should return a Relationship object. It will be translated into something like "/relationships/8", where 8 is the ID of the Relationship. Because you specify `method: :delete, the destroy action will be invoked.
I think the url "/users/2" is after the destroy action performed the deletion was performed. There could be a redirect in the destroy action. (eg. redirect_to current_user). You can see all the invoked actions in the log file. Try scrolling through the log and see if you can find RelationshipsController#destroy. You will see the invoked url there. Also you could inspect the HTML to see where the <form> tag's "action" attribute points to.
I have four scenarios I want to get some feedback on so I can better understand how CanCan works with abilities.
1 - Is using the object from an iteration acceptable for passing the model to CanCan. It seems to work fine. It seems this will define a very specific ability from the object passed.
#users.each do |user|
link_to user.name, user_path(user) if can? :edit, user
2 - #user is not a variable that is set in the controller. This, I'm assuming, is the same as #1 since load_and_authorize_resource creates the #user instance.
#users.each do |user|
link_to user.name, user_path(user) if can? :edit, #user
But, what about when an instance variable exists in a view that is not related to the current model and is not deliberately set in the controller. For instance the following inside the show user page when the #account instance is NOT set in the show action. How does CanCan use #account when it shouldnt know what it even is?
link_to "view user account", account_path(1) if can? :read, #account
3 - What about defining the ability using the model? I assume this is a very broad "can edit users" ability...when would you ever use this?
#users.each do |user|
link_to user.name, user_path(user) if can? :edit, User
4 - Using an association. Doesn't break the page...but, Im not sure if it is working as expected.
#users.each do |user|
link_to "view user account", account_path(1) if can? :read, user.account
Oooh, a multi-part question.
Case #1 and #2
These two are nearly identical. The difference is that the #user variable is being set up in your controller before hand with the load_and_authorize_call or some other filter that runs before your actions.
This is useful if you want to check permissions on a single object, i.e. a user can only edit their own record.
For the end of case #2:
You will need to ensure that the variables are set within the controller before you can access them within your controller or view to do permission checking. Same as if you wanted to access them in other instances that have nothing to do with permission checking.
Case #3
Mainly used in cases where a user can edit all objects of a particular class.
This is useful if you want a user to edit all objects of a particular class, i.e. an admin can edit all user's details
Case #4
Using an association will grab that record out and pass it to CanCan, just like using if can? :read, user would. Not really any different to Case #1.
I am trying to create an activity feed with the most recent activities from my TrainingSession model.
class User
has_many :training_sessions
end
class TrainingSession
belongs_to :user
end
The problem is that I am trying to access a user's data in the view page (mainly the user's name) by instantiating an object from the TrainingSessions database table, as shown below:
<% #training_sessions.each do |training_session| %>
<%= training_session.user_id %>
The problem is that, although I successfully get the user's id, I cannot call, for example:
training_session.user_id.name
... otherwise I get the NoMethodError shown below:
undefined method `first_name' for 2:Fixnum
so my question is ... how can I access the user's data from the TrainingSession's object?
any help would be much appreciated. Pretty stumped on this one.
The reason that you get a "undefined method `name' for nil:NilClass"-error is that some training sessions do not belong to a user. The solution is to cleanup your database:
DELETE FROM training_sessions WHERE user_id IS NULL
If it is expected behavior to have training sessions that don't belong to a user, you have to check that the user is not nil in your loop:
<% #training_sessions.each do |training_session| %>
<% unless training_session.user.nil? %>
<%= training_session.user.name %>
<% end %>
<% end %>
First of all, you need to rename your model name (TreningSessions) into singular name (TreningSession). That's the convention rails uses. Rename only model, leave has_many without change.
Now the user association,you should call it via user object. user_id is just a attribute that represents field in database and it's value, while user is an association object. Try this:
training_session.user.name
More on ActiveRecord relations
Here is what I ended up doing, creating a local user variable containing the user_id and using that variable with the find method on the user model to instantiate an instance variable #training_session_user in my controller, like the following:
#training_sessions.each do |training_session|
user = training_session.user_id
#training_session_user = User.find(user)
end
then I call this in my view:
#training_session_user.first_name
and it retrieves the name with no errors.
If anyone has a better solution please feel free, but I will mark this as correct for now.
I am looking at factories in rails and have hit a problem in my understanding of this code:
Factory.define :user do |f|
f.username "foo"
f.password "foobar"
f.password_confirmation { |u| u.password }
end
I understand the mechanics of everything except for
f.password_confirmation { |u| u.password }
How would this know to assign "foobar" to f.password_confirmation in the case where I used "foobar" as a custom password. Or in other words what does 'u' reference. Thanks in advance.
The "u" in that case actually represents an "Evaluator" class, which is an internal proxy used by factory_girl. It's a dynamically defined class that responds to methods for the attributes you're defining on your factory.
The Evaluator allows you to access previously defined attribute values, and it will generate, cache, and return the correct value if the attributes are out of order. For example, swapping the order of "password" and "password_confirmation" will still work because of the way the Evaluator works.
You can see how Evaluator works here: https://github.com/thoughtbot/factory_girl/blob/master/lib/factory_girl/evaluator.rb
You mostly don't need to worry about the Evaluator when defining factories. You can generally "u" there like it's an instance of a User, because it will delegate missing methods to the instance that it's building.
I have two models in my Rails application, Users and Calls.
The model associations are set as follows:
user model
has_many :calls
call model
belongs_to :user
I am trying to call, within the application.html.erb layout, the number of calls that the current user has.
Currently, I am using the following string:
<%= Call.count %>
Which works but it's obviously counting all calls, not just the calls that the current user has.
So I swapped that for the following:
<%= current_user.Call.calls.count %>
I am confused as to how to do this. I need to be able to call the count from anywhere so I can then start working on counting based on the last 30 days etc.
Do this:
current_user.calls.count
You can do the same thing with any User object:
user = User.find(1)
user.calls.count
You then can chain more conditions to do the date-based counts:
user.calls.where("calls.created_at > ?", 30.days.ago).count
You don't really want to be putting database calls within views, you really want to put that into a controller. You'd be looking for something like User.find(current_user.id).calls.count as this will then use the association of which you've set up, or if you want to disregard the model relation you could do Call.where(:user_id => current_user.id).count
So put the below into the relevant controller (in the correct action), and likewise for the view.
Controller
#count = Call.where(:user_id => current_user.id).count
View
<%= #count %>