I have three classes: Person, Position, and Directory.
What I would like to be able to do is add data to the join model, such as the title field, every time I add a person to a Directory.people collection. The usual << operator won't cut it. Id est:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
directory.people << person
This will add a person to the people collection of a Directory object, but will not allow me the chance to assign data to the join model. So, after doing a lot of research on this site, I found another way to add the Person to the people collection and add data to the Position that links the Person and the Directory, id est:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
position = person.positions.build(:directory_id => directory.id, :title => "Administrative Assistant")
position.save
This is cumbersome. An equally cumbersome way would be:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
position = Position.new(directory_id: directory.id, person_id: person.id, title: "Administrative Assistant")
Again, seems wrong because I'd like to be able to emphasize the relationship between Person and Directory, which I believe is what makes using has_many :through appropriate.
What I'd like to be able to do is use the << operator, and just pass the additional data, e.g.:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
directory.people << person, :position => {:title => "Administrative Assistant"}
I have overloaded the << operator in my has_many :through declaration, as follows:
has_many :people, :through => :positions do
def << *args
arg = args.first
if arg.is_a?(Person)
self.push([arg])
elsif arg.is_a?(Hash)
# Don't know what to do in here (see below)
else
raise "Invalid Value" # There's a better error to raise here, but good enough for now.
end
end
end
The advantage of getting this working is that it works well syntactically, and allows me to concisely assign data to the join object (a Position) while adding the Person to the people collection of the Directory object.
But I cannot make it work because I would need to be able to access the Directory object of which the people collection on the left side of the << operator is an attribute, in order to build a Position and save it to the database.
So, my questions are:
Thanks very much for your help and thoughtful responses. I've been hacking away at this for half a day to no avail.
Answer Thanks to PinnyM, who answered this question, I was able to come up with this implementation:
module AddToPeopleAndPositionExtension
def << *args
arg = args.first
if arg.is_a?(Person)
self.push([arg])
return self
elsif arg.is_a?(Hash)
directory = proxy_association.owner
person = arg[:person]
position = person.positions.build(:directory_id => directory.id, :title => arg[:position][:title])
position.save
else
raise "Invalid Value"
end
end
end
class Directory < ActiveRecord::Base
# Relationships
has_many :positions
has_many :people, :through => :positions, :extend => AddToPeopleAndPositionExtension
end
This allowed me to call the << operator in the standard way if I did not care what happens on the join model, like:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
directory.people << person
And, I could also call it in a way that specified attributes of the join model like:
directory = Directory.last # Let's assume that retrieves a Directory object
person = Person.last # Let's assume that retrieves a Person object
directory.people << {:person => person, :position => {:title => "Administrative Assistant"}}
What is ActiveRecord? ActiveRecord is an ORM. It's a layer of Ruby code that runs between your database and your logic code. When you need to make changes to the database, you'll write Ruby code, and then run "migrations" which makes the actual changes to the database.
A has_many association is similar to has_one , but indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a belongs_to association. This association indicates that each instance of the model has zero or more instances of another model.
what is dependent :destroy. Dependent is an option of Rails collection association declaration to cascade the delete action. The :destroy is to cause the associated object to also be destroyed when its owner is destroyed.
You can use the proxy_association
helper inside the block to get the association and proxy_association.owner
to get the Directory object itself. See here for some more info on this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With