Rails 3.1 associations and counting? - ruby-on-rails-3

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 %>

Related

Necessary to include all attributes in view?

In a new action in a rails I have this:
#client_order = ClientOrder.where(:client_id => #client.id, :order_date =>
params[:order_date] || Date.today).first_or_initialize
This works perfectly and initializes a #client_order with in that #client_order a filled in client_id
Now I have noticed that in the view I'm obligated to include this line (simple_forms)
<%= f.hidden_field :client_id %>
In order to retrieve that filled in attribute in the create action. Is this normal? What will happen when the user changes that field (through debugging the form)?
It'll get the client ID given in the request.
It's normal if that's how the client is retrieved.
"Authorization" is the part of authentication/authorization that controls whether or not a user has access to a given resource; things like cancan address this.
What does your model for ClientOrder and Client look like? It may be an association problem.
ClientOrder
belongs_to :client
Client
has_many :client_orders

Ordering records when using has_and_belongs_to_many relation in Rails 3?

I am, for the first time, trying to use a HABTM relationship in my Rails application. The following image shows the models and their relations to each other:
Currently I am displaying all members within a region with the following:
<% #region.members.each do |member| %>
<%= link_to member.name, member %>
<% end %>
I am trying to sort/order the list of members by their respective level. I then would like to have the members ordered in descending alphabetical order.
So, the members_controller code is currently the default:
#members = Member.all
I can order the results into alphabetical order:
#members = Member.order("name DESC").all
but I can't figure out how to use a related model's data to order the member records. There are likely going to be three levels; Charity, Basic and Subscription. I am planning on having a block at the top of the page which only shows subscription members. The subscription members should not then be shown again on the page.
#subscribed_members = Member.where(:level == 1).order("name DESC").all
but, as you can see, I have no idea how to filter that block by level.
Can anyone point me in the right direction, or maybe to a tutorial for this? I've read through the Rails guide for HABTM relationships but it only seems to cover the models.
EDIT
After trying the suggestion, I now have the following:
#members = Member.all
#subscribedmembers = Member.include(:levels)
.where("levels.name == 'subscriber'")
How do I use that method in the regions view?
<% #region.subscribedmembers.each do |member| %>
<%= member.name %>
<% end %>
That won't work because it's looking for a related model called subscrivedmembers which doesn't exist.
For this kind of task, i recommend you to take a look at joining models with ActiveRecord. The methods are include and joins. Lemme demonstrate with a piece of code:
#subscribed_members = Member.include(:levels)
.where("members.url == 'test_url'")
.order("levels.name DESC")
If you check your console after this query is run, you can see the include makes a SQL join with the two tables. Here i am assuming member :has_many :levels, so you include the :levels table and use a prefixed column name on your order clause.
It is not that hard once you get the idea, so i encourage you to try these two methods on the console and check the results.
EDIT
Create a scope with you query code, then use it on any place. Example:
# On your model
scope :ultimate_level, include(:levels).where("name = 'test'").order("levels.name DESC")
# On your controller
#subscribed = Member.ultimate_level
# On your view
<% #subscribed.each do |s| %>
You can create different scopes too, each one that makes a single operation, and then reuse on our controllers, etc.
Obs.: Check the syntax, i didn't test the code myself.

The different ways of using CanCan abilities

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.

Rails 3 How to access user data from user_id column in belongs_to :user association

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.

Rails adding resource id to another resource via HABTM

I have 3 pertinent models:
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :galleries
end
class Gallery < ActiveRecord::Base
belongs_to :group
end
I want to be able to create users and galleries within a group so that only users who are members of the group can view the galleries that belong to that group. I also want users to be able to view galleries of other groups they belong to (hence the HABTM association).
I'm having difficulty in conceptualizing how this works with controllers, and perhaps I'm over thinking the problem. If I create a Group, and then I go to create a user, what is the best way to go about adding the current group_id to the user model? Same thing goes for the gallery model...
Does that make sense?
Let me know if I need to clarify or add code samples.
Thank you very much for your help.
EDIT: Clarification
I definitely didn't make any sense in my initial question, but I did manage to find the answer, with help from a friend.
What I ended up doing is passing the group_id to the form via the params hash like so:
<%= link_to "Add User", new_admin_user_path(:group_id => #group.id) %>
<%= link_to "Add Gallery", new_gallery_path(:group_id => #group.id) %>
Then using a hidden field in my form, assigning the group_id to the "group_id" hidden field:
<%= hidden_field_tag :group_id, params[:group_id] %>
And, finally, in my create methods, adding these lines before the save assigns the group_id perfectly:
# Gallery only has one group
#gallery.group_id = params[:group_id]
# Users can belong to many groups
#user.groups << Group.find(params[:group_id])
I'll still need to sit down and wrap my head around the answers you both provided. Thank you very much for taking the time to help me out. I really appreciate it.
When you are using find method from your controller you can make it like this:
Gallery.find :all, :joins => "INNER JOIN groups ON groups.gallery_id = galleries.id INNER JOIN users ON users.group_id = groups.id", :conditions => "users.id = #{#your_current_user_id}"
It must find all galleries of groups which the user belongs.
I would not define this in the controller as Sebes suggests, but rather in the User model.
Adapting his idea:
def galleries
Gallery.joins(:groups => :users).where("users.id = ?", self.id)
end
Then to get a collection of the galleries for the current_user object:
current_user.galleries