How find all the associations of an instance - ruby-on-rails

This question RAILS: How to get has_many associations of a model tells how to find all the associations of a Class. I want to do this for an instance of the class. In particular I have a User model, and when I setup a User instance, it has a number of associations e.g. user.profile, user.plans etc. I want to check all the associations have been successfully set up for a particular user instance. How do you do this?

Based on the link you provided you should be able to accomplish what you want by doing this:
User.reflect_on_all_associations.map { |assoc| assoc.name }.each do |assoc|
association_object = user.send assoc
#note this is the user instance not the class.
# do whatever you want with association_object. check if nil?
end
What the code does, it to iterate through the list of association name keys returned the link you provided and then use it to call the "method" (meaning the association) by using send.
Hope that helps

Related

How to initialize record from session variable in rails so that we need not to execute query for initializing a record?

Is there any way to initialize a record from session. for e.g I have a organization object and I put this in session object like
session[:organization] = organization
Now I made a custom method current_organization (I know about devise) like
def current_organization
Organization.new(session[:organization])
end
This will return organization object. My organization belongs_to a team a devise model and team has_many :organizations but when I call
current_team.organizations.includes?(current_organization)
in view. It is returning false even if it is included in team's organizations but doing this
current_team.organizations.reload.includes?(current_organization)
is returning true. I set the session variable with organization object before calling view where i am using above method. Is there any thing which I missed like I am not able to figure out the reason for not returning true even it is included?
Try saving the record first.
Until you save it into the database, it is likely not to show up when you query for the team's organization children.
session[:organization_id] = organization.id
def current_organization
Organization.find session[:organization_id]
end
Ok after some googling i found that instead of using
Organization.new(session[:organization])
I should use
Organization.instantiate(session[:organization])
From apidock I found that
instantiate(attributes, column_types = {}) public
Given an attributes hash, instantiate returns a new instance of the
appropriate class. Accepts only keys as strings.
For example, Post.all may return Comments, Messages, and Emails by
storing the record’s subclass in a type attribute. By calling
instantiate instead of new, finder methods ensure they get new
instances of the appropriate class for each record.

Having trouble in tracing code in Rails

I am currently struggling with this piece of code
#play = current_user.playlist.find_by_id(params[:id])
What does current_user.playlist.find_by_id() mean? How can I trace this code to find current_user, playlist and find_by_id() function?
You could be using devise, if that is the case, current_user returns an instance of class User which is the currently logged in user. Or nil if there is no logged in user.
playlist is a method defined in class User, you should find this class in app/models/user.rb, usually this method would be defined with:
has_one :playlist
or:
belongs_to :playlist
find_by_id is a method defined by Rails for class User, you won't see this directly in file. It is created when you have something like this:
class User < ActiveRecord::Base
ActiveRecord::Base creates a lot of methods more.
Debug your code
I would print each part of the sentence
p current_user
p current_user.playlist
p params[:id]
p current_user.playlist.find_by_id(params[:id])
and check results in my server console, you could spot which of these is the first nil.
In Ruby, everything is an object, and objects have methods. Objects also have types.
Your top-level object there is current_user. This is an instance of some class - you can find out what kind by looking at current_user.class. I suspect you're going to find that it's an instance of User.
So, you find where your User is defined. This is likely a model, defined in app/models/user.rb. This model will specify a number of attributes and associations. In this case, you likely have a has_many :playlists association. What this does is set up an association between a User instance and a number of Playlist instances. Given a user instance, user_instance.playlists accesses this association. Your Playlist model will have a user_id field that associates a playlist with a user record. You can read more about associations in the relevant documentation.
Finally, this association will have a number of methods from Rails. ActiveRecord has a standard set of finders, as well as some "magic" finders like find_by_id, which infer the field to find from based on the method name. find_by_id(params[:id]) is functionally equivalent to something like find_by(:id => params[:id]), but it's a little more English-y. You can read more about this in the Dynamic Finders method of the documentation.
find_by_id will generate the SQL necessary to find the playlist records with that ID that also have a user_id matching current_user's ID. If it finds a matching record, it will instantiate a Playlist record with the data it retrieved and return it. If no matching record is found, it will return nil.

How to obtain BEFORE and AFTER state of my object's related attributes?

In my application I have the following relationship:
Document has_and_belongs_to_many Users
User has_and_belongs_to_many Documents
What I am trying to figure out is how to perform the following:
Let's say a document has 3 users which belong to it. If after an update they become for ex. 4, I would like to send an email
message (document_updated) to the first 3 and a different email message (document_assigned) to the 4th.
So I have to know the users belonging to my Document BEFORE and AFTER the Document update occurs.
My approach so far has been to create an Observer like this:
class DocumentObserver < ActiveRecord::Observer
def after_update(document)
# this works because of ActiveModel::Dirty
# #old_subject=document.subject_was #subject is a Document attribute (string)
# this is not working - I get an 'undefined method' error
#old_users=document.users_was
#new_users=document.users.all.dup
# perform calculations to find out who the new users are and send emails....
end
end
I knew that my chances of #old_users taking a valid value were slim because I guess it is populated dynamically by rails via the has_and_belongs_to_many relation.
So my question is:
How do I get all my related users before an update occurs?
(some other things I've tried so far:)
A. Obtaining document.users.all inside DocumentController::edit. This returns a valid array, however I do not know how to pass this array to
DocumentObserver.after_update in order to perform the calculations (just setting an instance variable inside DocumentController is of course not working)
B. Trying to save document.users inside DocumentObserver::before_update. This is not working either. I still get the new user values
Thanks in advance
George
Ruby 1.9.2p320
Rails 3.1.0
You could use a before_add callback
class Document
has_and_belongs_to_many :users, :before_add => :do_stuff
def do_stuff(user)
end
end
When you add a user to a document that callback will be called and at that point self.users will still it yet contain the user you are adding.
If you need something more complicated it might be simpler to have a set_users method on document
def set_users(new_user_set)
existing = users
new_users = users - new_user_set
# send your emails
self.users = new_user_set
end

Assigning rails association using strings

Say I have two models and one belongs to another. Now normaly you would assign an object to the association when populating the fields. Does rails allow overriding the set method so that the association assignment can be customised?
E.g
class Person
# something about shirts
end
class Shirt
belongs_to :person
def person=(p)
self.person = Person.find_or_create_by_name(p)
end
end
And then use something like so auto bind the association but using a string to do the searching and binding automatically. Is this possible?
s = Shirt.new
s.person = "Test Person"
Thanks
ROR Guides cover the association extension you need.
UPDATE:
Actually, overriding setter is not that bad, once you understand what you're doing. But you have to be careful, since it can cause infinite loop (as in your example). So if you're using Rails 3.2, you have to use super, in other case you have to use alias_method_chain.

whats the difference between these create methods

Hey I am stuck with my orientation in rails.
I got a User model, a Course Model and a CourseEnrollment Model.
When I want to add a link in my Course Index View like
link_to 'join' CourseEnrollment.create(:course_id => course.id, :user_id => current_user)
Does this create method belong to my Model? I am confused because in my User Model I defined a method that uses role_assignments.create(.....). What is the difference between these 2 create methods? I cant use course_enrollments.create by the way. Thx for your time
I'm a bit confused as to what you're asking, but I'll try my best.
(First of all, in your example, current_user should probably be current_user.id.)
When you call CourseEnrollment.create, you are simply creating a new CourseEntrollment model with the specified attributes.
Assuming that your User model has_many :role_assignments:
When you call #role_assignments.create from within your User model, Rails automatically creates the association for you (e.g. sets the user_id to the id of the user). This doesn't have to be done within the model itself, though:
current_user.role_assignments.create(...) # automatically sets the association
Assuming that your User model also has_many :course_enrollments, the following will create a CourseEnrollment model and automatically associate it with the current user:
current_user.course_enrollments.create(...)

Resources