rails / rails

Ruby on Rails

This URL has Read+Write access

rails / activerecord / lib / active_record / associations.rb
823554ea » dhh 2005-01-15 Added support for associati... 1 require 'active_record/associations/association_proxy'
db045dbb » dhh 2004-11-23 Initial 2 require 'active_record/associations/association_collection'
823554ea » dhh 2005-01-15 Added support for associati... 3 require 'active_record/associations/belongs_to_association'
57b7532b » dhh 2005-12-01 Work-in progress for provid... 4 require 'active_record/associations/belongs_to_polymorphic_association'
823554ea » dhh 2005-01-15 Added support for associati... 5 require 'active_record/associations/has_one_association'
db045dbb » dhh 2004-11-23 Initial 6 require 'active_record/associations/has_many_association'
6abda696 » dhh 2005-12-02 Added preliminary support f... 7 require 'active_record/associations/has_many_through_association'
db045dbb » dhh 2004-11-23 Initial 8 require 'active_record/associations/has_and_belongs_to_many_association'
9
10 module ActiveRecord
fed7d334 » dhh 2006-03-27 Fixed documentation 11 class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
291adbd3 » technoweenie 2006-07-19 fix association exception m... 12 def initialize(owner_class_name, reflection)
13 super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 14 end
15 end
16
fed7d334 » dhh 2006-03-27 Fixed documentation 17 class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc:
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 18 def initialize(owner_class_name, reflection, source_reflection)
291adbd3 » technoweenie 2006-07-19 fix association exception m... 19 super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 20 end
21 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 22
e3dab67c » technoweenie 2007-03-12 Allow a polymorphic :source... 23 class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
24 def initialize(owner_class_name, reflection, source_reflection)
25 super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
26 end
27 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 28
fed7d334 » dhh 2006-03-27 Fixed documentation 29 class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 30 def initialize(reflection)
291adbd3 » technoweenie 2006-07-19 fix association exception m... 31 through_reflection = reflection.through_reflection
32 source_reflection_names = reflection.source_reflection_names
33 source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
34 super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence :connector => 'or'}?")
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 35 end
36 end
37
0aa0c84c » dhh 2007-01-26 Nodoc the irrelevant (from ... 38 class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc:
4d232025 » technoweenie 2006-04-05 Added descriptive error mes... 39 def initialize(reflection)
291adbd3 » technoweenie 2006-07-19 fix association exception m... 40 through_reflection = reflection.through_reflection
41 source_reflection = reflection.source_reflection
42 super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}. Use :source to specify the source reflection.")
4d232025 » technoweenie 2006-04-05 Added descriptive error mes... 43 end
44 end
45
0da426be » jeremy 2006-08-18 Add records to has_many :th... 46 class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
47 def initialize(owner, reflection)
48 super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
49 end
50 end
51
4b639904 » jeremy 2007-10-27 Fix has_many :through delet... 52 class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
53 def initialize(owner, reflection)
54 super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
55 end
56 end
57
fed7d334 » dhh 2006-03-27 Fixed documentation 58 class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 59 def initialize(reflection)
291adbd3 » technoweenie 2006-07-19 fix association exception m... 60 super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 61 end
62 end
63
c61b10b6 » technoweenie 2006-04-24 Raise error when trying to ... 64 class ReadOnlyAssociation < ActiveRecordError #:nodoc:
65 def initialize(reflection)
291adbd3 » technoweenie 2006-07-19 fix association exception m... 66 super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
c61b10b6 » technoweenie 2006-04-24 Raise error when trying to ... 67 end
68 end
69
db045dbb » dhh 2004-11-23 Initial 70 module Associations # :nodoc:
61864909 » Marcel Molina 2006-04-29 Replace Ruby's deprecated a... 71 def self.included(base)
7c8d2f28 » dhh 2005-04-02 Removed broken attempt to D... 72 base.extend(ClassMethods)
73 end
74
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 75 # Clears out the association cache
4b229d10 » dhh 2004-12-22 Added Base#clear_associatio... 76 def clear_association_cache #:nodoc:
77 self.class.reflect_on_all_associations.to_a.each do |assoc|
78 instance_variable_set "@#{assoc.name}", nil
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 79 end unless self.new_record?
4b229d10 » dhh 2004-12-22 Added Base#clear_associatio... 80 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 81
82 # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
83 # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
84 # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
db045dbb » dhh 2004-11-23 Initial 85 # methods. Example:
86 #
87 # class Project < ActiveRecord::Base
88 # belongs_to :portfolio
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 89 # has_one :project_manager
db045dbb » dhh 2004-11-23 Initial 90 # has_many :milestones
91 # has_and_belongs_to_many :categories
92 # end
93 #
94 # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
823554ea » dhh 2005-01-15 Added support for associati... 95 # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
db045dbb » dhh 2004-11-23 Initial 96 # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
97 # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
7aa9eed8 » jeremy 2006-09-01 Deprecation: update docs. C... 98 # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
db045dbb » dhh 2004-11-23 Initial 99 # <tt>Project#milestones.build, Project#milestones.create</tt>
100 # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
101 # <tt>Project#categories.delete(category1)</tt>
102 #
7288fd3e » jeremy 2007-05-18 Docs: warn that association... 103 # === A word of warning
104 #
105 # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
106 # adds a method with that name to its model, it will override the inherited method and break things.
107 # For instance, #attributes and #connection would be bad choices for association names.
108 #
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 109 # == Auto-generated methods
db045dbb » dhh 2004-11-23 Initial 110 #
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 111 # ===Singular associations (one-to-one)
112 # | | belongs_to |
113 # generated methods | belongs_to | :polymorphic | has_one
114 # ----------------------------------+------------+--------------+---------
115 # #other | X | X | X
116 # #other=(other) | X | X | X
117 # #build_other(attributes={}) | X | | X
118 # #create_other(attributes={}) | X | | X
119 # #other.create!(attributes={}) | | | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 120 # #other.nil? | X | X |
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 121 #
122 # ===Collection associations (one-to-many / many-to-many)
123 # | | | has_many
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 124 # generated methods | habtm | has_many | :through
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 125 # ----------------------------------+-------+----------+----------
126 # #others | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 127 # #others=(other,other,...) | X | X |
a291ea2b » Marcel Molina 2007-11-06 Update association/method m... 128 # #other_ids | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 129 # #other_ids=(id,id,...) | X | X |
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 130 # #others<< | X | X | X
131 # #others.push | X | X | X
132 # #others.concat | X | X | X
133 # #others.build(attributes={}) | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 134 # #others.create(attributes={}) | X | X |
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 135 # #others.create!(attributes={}) | X | X | X
a291ea2b » Marcel Molina 2007-11-06 Update association/method m... 136 # #others.size | X | X | X
137 # #others.length | X | X | X
138 # #others.count | | X | X
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 139 # #others.sum(args*,&block) | X | X | X
a291ea2b » Marcel Molina 2007-11-06 Update association/method m... 140 # #others.empty? | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 141 # #others.clear | X | X |
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 142 # #others.delete(other,other,...) | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 143 # #others.delete_all | X | X |
a291ea2b » Marcel Molina 2007-11-06 Update association/method m... 144 # #others.destroy_all | X | X | X
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 145 # #others.find(*args) | X | X | X
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 146 # #others.find_first | X | |
147 # #others.uniq | X | X |
ff6d2aae » jeremy 2007-05-30 Quickref for association me... 148 # #others.reset | X | X | X
db045dbb » dhh 2004-11-23 Initial 149 #
51d840e2 » NZKoz 2007-01-14 Improve association documen... 150 # == Cardinality and associations
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 151 #
51d840e2 » NZKoz 2007-01-14 Improve association documen... 152 # ActiveRecord associations can be used to describe relations with one-to-one, one-to-many
153 # and many-to-many cardinality. Each model uses an association to describe its role in
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 154 # the relation. In each case, the +belongs_to+ association is used in the model that has
8296c680 » technoweenie 2007-01-22 Remove useless code in #att... 155 # the foreign key.
51d840e2 » NZKoz 2007-01-14 Improve association documen... 156 #
157 # === One-to-one
158 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 159 # Use +has_one+ in the base, and +belongs_to+ in the associated model.
51d840e2 » NZKoz 2007-01-14 Improve association documen... 160 #
161 # class Employee < ActiveRecord::Base
162 # has_one :office
163 # end
164 # class Office < ActiveRecord::Base
165 # belongs_to :employee # foreign key - employee_id
166 # end
167 #
168 # === One-to-many
169 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 170 # Use +has_many+ in the base, and +belongs_to+ in the associated model.
51d840e2 » NZKoz 2007-01-14 Improve association documen... 171 #
172 # class Manager < ActiveRecord::Base
173 # has_many :employees
174 # end
175 # class Employee < ActiveRecord::Base
8296c680 » technoweenie 2007-01-22 Remove useless code in #att... 176 # belongs_to :manager # foreign key - manager_id
51d840e2 » NZKoz 2007-01-14 Improve association documen... 177 # end
178 #
179 # === Many-to-many
180 #
181 # There are two ways to build a many-to-many relationship.
182 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 183 # The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
51d840e2 » NZKoz 2007-01-14 Improve association documen... 184 # there are two stages of associations.
185 #
186 # class Assignment < ActiveRecord::Base
187 # belongs_to :programmer # foreign key - programmer_id
188 # belongs_to :project # foreign key - project_id
189 # end
190 # class Programmer < ActiveRecord::Base
191 # has_many :assignments
192 # has_many :projects, :through => :assignments
193 # end
194 # class Project < ActiveRecord::Base
195 # has_many :assignments
196 # has_many :programmers, :through => :assignments
197 # end
198 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 199 # For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
51d840e2 » NZKoz 2007-01-14 Improve association documen... 200 # that has no corresponding model or primary key.
201 #
202 # class Programmer < ActiveRecord::Base
203 # has_and_belongs_to_many :projects # foreign keys in the join table
204 # end
205 # class Project < ActiveRecord::Base
206 # has_and_belongs_to_many :programmers # foreign keys in the join table
207 # end
208 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 209 # Choosing which way to build a many-to-many relationship is not always simple.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 210 # If you need to work with the relationship model as its own entity,
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 211 # use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
51d840e2 » NZKoz 2007-01-14 Improve association documen... 212 # you never work directly with the relationship itself.
213 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 214 # == Is it a +belongs_to+ or +has_one+ association?
db045dbb » dhh 2004-11-23 Initial 215 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 216 # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
217 # declaring the +belongs_to+ relationship. Example:
db045dbb » dhh 2004-11-23 Initial 218 #
69d8ca4c » jeremy 2006-07-07 Clearer has_one/belongs_to ... 219 # class User < ActiveRecord::Base
220 # # I reference an account.
221 # belongs_to :account
db045dbb » dhh 2004-11-23 Initial 222 # end
223 #
69d8ca4c » jeremy 2006-07-07 Clearer has_one/belongs_to ... 224 # class Account < ActiveRecord::Base
225 # # One user references me.
226 # has_one :user
db045dbb » dhh 2004-11-23 Initial 227 # end
228 #
229 # The tables for these classes could look something like:
230 #
69d8ca4c » jeremy 2006-07-07 Clearer has_one/belongs_to ... 231 # CREATE TABLE users (
db045dbb » dhh 2004-11-23 Initial 232 # id int(11) NOT NULL auto_increment,
69d8ca4c » jeremy 2006-07-07 Clearer has_one/belongs_to ... 233 # account_id int(11) default NULL,
234 # name varchar default NULL,
db045dbb » dhh 2004-11-23 Initial 235 # PRIMARY KEY (id)
236 # )
237 #
69d8ca4c » jeremy 2006-07-07 Clearer has_one/belongs_to ... 238 # CREATE TABLE accounts (
db045dbb » dhh 2004-11-23 Initial 239 # id int(11) NOT NULL auto_increment,
240 # name varchar default NULL,
241 # PRIMARY KEY (id)
242 # )
243 #
823554ea » dhh 2005-01-15 Added support for associati... 244 # == Unsaved objects and associations
245 #
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 246 # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
823554ea » dhh 2005-01-15 Added support for associati... 247 # aware of, mostly involving the saving of associated objects.
248 #
249 # === One-to-one associations
250 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 251 # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
252 # order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
253 # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
823554ea » dhh 2005-01-15 Added support for associati... 254 # is cancelled.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 255 # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>#association.build</tt> method (documented below).
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 256 # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 257 # does not save the parent either.
823554ea » dhh 2005-01-15 Added support for associati... 258 #
259 # === Collections
260 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 261 # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
823554ea » dhh 2005-01-15 Added support for associati... 262 # (the owner of the collection) is not yet stored in the database.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 263 # * If saving any of the objects being added to a collection (via <tt>#push</tt> or similar) fails, then <tt>#push</tt> returns +false+.
264 # * You can add an object to a collection without automatically saving it by using the <tt>#collection.build</tt> method (documented below).
265 # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
823554ea » dhh 2005-01-15 Added support for associati... 266 #
17f7f8a0 » dhh 2005-07-06 Made documentation ready fo... 267 # === Association callbacks
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 268 #
2af36bbb » dhh 2007-12-05 Fix typos (closes #10378) 269 # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 270 # triggered when you add an object to or remove an object from an association collection. Example:
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 271 #
272 # class Project
273 # has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
274 #
275 # def evaluate_velocity(developer)
276 # ...
277 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 278 # end
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 279 #
280 # It's possible to stack callbacks by passing them as an array. Example:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 281 #
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 282 # class Project
17f7f8a0 » dhh 2005-07-06 Made documentation ready fo... 283 # has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 284 # end
285 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 286 # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 287 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 288 # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
289 # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 290 #
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 291 # === Association extensions
292 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 293 # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
3c01e01d » Marcel Molina 2005-12-19 Fix typo in association doc... 294 # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 295 # Example:
296 #
297 # class Account < ActiveRecord::Base
c8dd66fd » dhh 2005-11-06 Made association extensions... 298 # has_many :people do
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 299 # def find_or_create_by_name(name)
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 300 # first_name, last_name = name.split(" ", 2)
da7752e5 » dhh 2005-11-06 Sharper example 301 # find_or_create_by_first_name_and_last_name(first_name, last_name)
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 302 # end
c8dd66fd » dhh 2005-11-06 Made association extensions... 303 # end
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 304 # end
305 #
306 # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
307 # person.first_name # => "David"
308 # person.last_name # => "Heinemeier Hansson"
309 #
c8dd66fd » dhh 2005-11-06 Made association extensions... 310 # If you need to share the same extensions between many associations, you can use a named extension module. Example:
311 #
312 # module FindOrCreateByNameExtension
313 # def find_or_create_by_name(name)
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 314 # first_name, last_name = name.split(" ", 2)
da7752e5 » dhh 2005-11-06 Sharper example 315 # find_or_create_by_first_name_and_last_name(first_name, last_name)
c8dd66fd » dhh 2005-11-06 Made association extensions... 316 # end
317 # end
318 #
319 # class Account < ActiveRecord::Base
320 # has_many :people, :extend => FindOrCreateByNameExtension
321 # end
322 #
323 # class Company < ActiveRecord::Base
324 # has_many :people, :extend => FindOrCreateByNameExtension
325 # end
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 326 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 327 # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option.
7e76740d » technoweenie 2006-04-10 Allow multiple association... 328 # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
329 # those earlier in the array. Example:
330 #
331 # class Account < ActiveRecord::Base
332 # has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
333 # end
334 #
ea51d72e » technoweenie 2006-05-28 Provide Association Extensi... 335 # Some extensions can only be made to work with knowledge of the association proxy's internals.
336 # Extensions can access relevant state using accessors on the association proxy:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 337 #
ea51d72e » technoweenie 2006-05-28 Provide Association Extensi... 338 # * +proxy_owner+ - Returns the object the association is part of.
339 # * +proxy_reflection+ - Returns the reflection object that describes the association.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 340 # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
ea51d72e » technoweenie 2006-05-28 Provide Association Extensi... 341 #
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 342 # === Association Join Models
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 343 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 344 # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data. This
345 # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 346 # callbacks, and extra attributes on the join model. Consider the following schema:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 347 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 348 # class Author < ActiveRecord::Base
349 # has_many :authorships
350 # has_many :books, :through => :authorships
351 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 352 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 353 # class Authorship < ActiveRecord::Base
354 # belongs_to :author
355 # belongs_to :book
356 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 357 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 358 # @author = Author.find :first
359 # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
360 # @author.books # selects all books by using the Authorship join model
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 361 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 362 # You can also go through a +has_many+ association on the join model:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 363 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 364 # class Firm < ActiveRecord::Base
365 # has_many :clients
366 # has_many :invoices, :through => :clients
367 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 368 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 369 # class Client < ActiveRecord::Base
370 # belongs_to :firm
371 # has_many :invoices
372 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 373 #
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 374 # class Invoice < ActiveRecord::Base
375 # belongs_to :client
376 # end
377 #
378 # @firm = Firm.find :first
379 # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
380 # @firm.invoices # selects all invoices by going through the Client join model.
381 #
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 382 # === Polymorphic Associations
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 383 #
384 # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 385 # specify an interface that a +has_many+ association must adhere to.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 386 #
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 387 # class Asset < ActiveRecord::Base
388 # belongs_to :attachable, :polymorphic => true
389 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 390 #
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 391 # class Post < ActiveRecord::Base
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 392 # has_many :assets, :as => :attachable # The :as option specifies the polymorphic interface to use.
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 393 # end
394 #
395 # @asset.attachable = @post
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 396 #
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 397 # This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 398 # an +attachable_id+ integer column and an +attachable_type+ string column.
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 399 #
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 400 # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 401 # for the associations to work as expected, ensure that you store the base model for the STI models in the
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 402 # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 403 # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 404 #
405 # class Asset < ActiveRecord::Base
406 # belongs_to :attachable, :polymorphic => true
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 407 #
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 408 # def attachable_type=(sType)
409 # super(sType.to_s.classify.constantize.base_class.to_s)
410 # end
411 # end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 412 #
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 413 # class Post < ActiveRecord::Base
414 # # because we store "Post" in attachable_type now :dependent => :destroy will work
415 # has_many :assets, :as => :attachable, :dependent => :destroy
416 # end
417 #
aa9ed408 » Marcel Molina 2007-12-05 Fix typo in documentation f... 418 # class GuestPost < Post
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 419 # end
420 #
aa9ed408 » Marcel Molina 2007-12-05 Fix typo in documentation f... 421 # class MemberPost < Post
fc7fd4c5 » dhh 2006-10-08 Docfix (closes #6040) 422 # end
423 #
db045dbb » dhh 2004-11-23 Initial 424 # == Caching
425 #
426 # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 427 # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without
db045dbb » dhh 2004-11-23 Initial 428 # worrying too much about performance at the first go. Example:
429 #
430 # project.milestones # fetches milestones from the database
431 # project.milestones.size # uses the milestone cache
432 # project.milestones.empty? # uses the milestone cache
433 # project.milestones(true).size # fetches milestones from the database
434 # project.milestones # uses the milestone cache
435 #
515886a5 » dhh 2005-04-18 Added documentation for new... 436 # == Eager loading of associations
437 #
438 # Eager loading is a way to find objects of a certain class and a number of named associations along with it in a single SQL call. This is
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 439 # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
515886a5 » dhh 2005-04-18 Added documentation for new... 440 # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 1. Example:
441 #
442 # class Post < ActiveRecord::Base
443 # belongs_to :author
444 # has_many :comments
445 # end
446 #
447 # Consider the following loop using the class above:
448 #
4e2f7bec » dhh 2005-04-19 Added documentation about :... 449 # for post in Post.find(:all)
515886a5 » dhh 2005-04-18 Added documentation for new... 450 # puts "Post: " + post.title
451 # puts "Written by: " + post.author.name
452 # puts "Last comment on: " + post.comments.first.created_on
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 453 # end
515886a5 » dhh 2005-04-18 Added documentation for new... 454 #
455 # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
456 #
4e2f7bec » dhh 2005-04-19 Added documentation about :... 457 # for post in Post.find(:all, :include => :author)
515886a5 » dhh 2005-04-18 Added documentation for new... 458 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 459 # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol, so the find will now weave in a join something
460 # like this: <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Doing so will cut down the number of queries from 201 to 101.
515886a5 » dhh 2005-04-18 Added documentation for new... 461 #
462 # We can improve upon the situation further by referencing both associations in the finder with:
463 #
4e2f7bec » dhh 2005-04-19 Added documentation about :... 464 # for post in Post.find(:all, :include => [ :author, :comments ])
515886a5 » dhh 2005-04-18 Added documentation for new... 465 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 466 # That'll add another join along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt>. And we'll be down to 1 query.
5b414190 » jeremy 2007-05-19 Document deep eager include... 467 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 468 # To include a deep hierarchy of associations, use a hash:
5b414190 » jeremy 2007-05-19 Document deep eager include... 469 #
470 # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
471 #
472 # That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match
473 # symbols, arrays and hashes in any combination to describe the associations you want to load.
474 #
475 # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
a8eea0b0 » dhh 2005-10-26 Fix docs (closes #2491) 476 # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
477 # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 478 #
8e3bf70b » technoweenie 2006-10-08 Removes the ability for eag... 479 # Since the eager loading pulls from multiple tables, you'll have to disambiguate any column references in both conditions and orders. So
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 480 # <tt>:order => "posts.id DESC"</tt> will work while <tt>:order => "id DESC"</tt> will not. Because eager loading generates the +SELECT+ statement too, the
481 # <tt>:select</tt> option is ignored.
515886a5 » dhh 2005-04-18 Added documentation for new... 482 #
8e3bf70b » technoweenie 2006-10-08 Removes the ability for eag... 483 # You can use eager loading on multiple associations from the same table, but you cannot use those associations in orders and conditions
484 # as there is currently not any way to disambiguate them. Eager loading will not pull additional attributes on join tables, so "rich
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 485 # associations" with +has_and_belongs_to_many+ are not a good fit for eager loading.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 486 #
78704946 » technoweenie 2006-10-11 Restore eager condition int... 487 # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
488 # before the actual model exists.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 489 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 490 # == Table Aliasing
491 #
492 # ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 493 # the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 494 # for any more successive uses of the table name.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 495 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 496 # Post.find :all, :include => :comments
497 # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ...
498 # Post.find :all, :include => :special_comments # STI
499 # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... AND comments.type = 'SpecialComment'
500 # Post.find :all, :include => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
501 # # => SELECT ... FROM posts LEFT OUTER JOIN comments ON ... LEFT OUTER JOIN comments special_comments_posts
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 502 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 503 # Acts as tree example:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 504 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 505 # TreeMixin.find :all, :include => :children
506 # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
507 # TreeMixin.find :all, :include => {:children => :parent} # using cascading eager includes
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 508 # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
509 # LEFT OUTER JOIN parents_mixins ...
510 # TreeMixin.find :all, :include => {:children => {:parent => :children}}
511 # # => SELECT ... FROM mixins LEFT OUTER JOIN mixins childrens_mixins ...
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 512 # LEFT OUTER JOIN parents_mixins ...
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 513 # LEFT OUTER JOIN mixins childrens_mixins_2
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 514 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 515 # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 516 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 517 # Post.find :all, :include => :categories
518 # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
519 # Post.find :all, :include => {:categories => :posts}
520 # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
521 # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
522 # Post.find :all, :include => {:categories => {:posts => :categories}}
523 # # => SELECT ... FROM posts LEFT OUTER JOIN categories_posts ... LEFT OUTER JOIN categories ...
524 # LEFT OUTER JOIN categories_posts posts_categories_join LEFT OUTER JOIN posts posts_categories
525 # LEFT OUTER JOIN categories_posts categories_posts_join LEFT OUTER JOIN categories categories_posts
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 526 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 527 # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 528 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 529 # Post.find :all, :include => :comments, :joins => "inner join comments ..."
530 # # => SELECT ... FROM posts LEFT OUTER JOIN comments_posts ON ... INNER JOIN comments ...
531 # Post.find :all, :include => [:comments, :special_comments], :joins => "inner join comments ..."
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 532 # # => SELECT ... FROM posts LEFT OUTER JOIN comments comments_posts ON ...
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 533 # LEFT OUTER JOIN comments special_comments_posts ...
534 # INNER JOIN comments ...
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 535 #
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 536 # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 537 #
db045dbb » dhh 2004-11-23 Initial 538 # == Modules
539 #
540 # By default, associations will look for objects within the current module scope. Consider:
541 #
542 # module MyApplication
543 # module Business
544 # class Firm < ActiveRecord::Base
545 # has_many :clients
546 # end
547 #
548 # class Company < ActiveRecord::Base; end
549 # end
550 # end
551 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 552 # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
553 # with a class in another module scope, this can be done by specifying the complete class name. Example:
db045dbb » dhh 2004-11-23 Initial 554 #
555 # module MyApplication
556 # module Business
557 # class Firm < ActiveRecord::Base; end
558 # end
559 #
560 # module Billing
561 # class Account < ActiveRecord::Base
562 # belongs_to :firm, :class_name => "MyApplication::Business::Firm"
563 # end
564 # end
565 # end
566 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 567 # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
db045dbb » dhh 2004-11-23 Initial 568 #
569 # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 570 # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
db045dbb » dhh 2004-11-23 Initial 571 #
572 # == Options
573 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 574 # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
db045dbb » dhh 2004-11-23 Initial 575 # possible.
576 module ClassMethods
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 577 # Adds the following methods for retrieval and query of collections of associated objects:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 578 # +collection+ is replaced with the symbol passed as the first argument, so
823554ea » dhh 2005-01-15 Added support for associati... 579 # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
db045dbb » dhh 2004-11-23 Initial 580 # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects.
581 # An empty array is returned if none are found.
582 # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 583 # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by setting their foreign keys to NULL.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 584 # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 585 # * <tt>collection=objects</tt> - replaces the collections content by deleting and adding objects as appropriate.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 586 # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 587 # * <tt>collection_singular_ids=ids</tt> - replace the collection with the objects identified by the primary keys in +ids+
efaf2af0 » jeremy 2005-09-27 r3653@asus: jeremy | 2005... 588 # * <tt>collection.clear</tt> - removes every object from the collection. This destroys the associated objects if they
6ad9d1db » Marcel Molina 2007-05-05 Update documentation for :d... 589 # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 590 # otherwise sets their foreign keys to NULL.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 591 # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects.
db045dbb » dhh 2004-11-23 Initial 592 # * <tt>collection.size</tt> - returns the number of associated objects.
c1611a70 » dhh 2005-04-18 Updated documentation here ... 593 # * <tt>collection.find</tt> - finds an associated object according to the same rules as Base.find.
b49fcde7 » technoweenie 2007-07-24 tiny doc patches [lifo] 594 # * <tt>collection.build(attributes = {}, ...)</tt> - returns one or more new objects of the collection type that have been instantiated
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 595 # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 596 # associated object already exists, not if it's +nil+!
db045dbb » dhh 2004-11-23 Initial 597 # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 598 # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
599 # *Note:* This only works if an associated object already exists, not if it's +nil+!
db045dbb » dhh 2004-11-23 Initial 600 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 601 # Example: A +Firm+ class declares <tt>has_many :clients</tt>, which will add:
c1611a70 » dhh 2005-04-18 Updated documentation here ... 602 # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
db045dbb » dhh 2004-11-23 Initial 603 # * <tt>Firm#clients<<</tt>
604 # * <tt>Firm#clients.delete</tt>
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 605 # * <tt>Firm#clients=</tt>
cb0837a2 » dhh 2006-10-08 Doc fixes (closes #6325) 606 # * <tt>Firm#client_ids</tt>
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 607 # * <tt>Firm#client_ids=</tt>
db045dbb » dhh 2004-11-23 Initial 608 # * <tt>Firm#clients.clear</tt>
609 # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
610 # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
c1611a70 » dhh 2005-04-18 Updated documentation here ... 611 # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
db045dbb » dhh 2004-11-23 Initial 612 # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
1d4d7217 » dhh 2005-06-21 Fixed docs #856 613 # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
db045dbb » dhh 2004-11-23 Initial 614 # The declaration can also include an options hash to specialize the behavior of the association.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 615 #
db045dbb » dhh 2004-11-23 Initial 616 # Options are:
098fa943 » dhh 2005-02-07 Fixed documentation snafus ... 617 # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
db045dbb » dhh 2004-11-23 Initial 618 # from the association name. So <tt>has_many :products</tt> will by default be linked to the +Product+ class, but
619 # if the real class name is +SpecialProduct+, you'll have to specify it with this option.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 620 # * <tt>:conditions</tt> - specify the conditions that the associated objects must meet in order to be included as a +WHERE+
621 # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>.
622 # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
623 # such as <tt>last_name, first_name DESC</tt>
db045dbb » dhh 2004-11-23 Initial 624 # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 625 # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_many+ association will use +person_id+
626 # as the default +foreign_key+.
627 # * <tt>:dependent</tt> - if set to <tt>:destroy</tt> all the associated objects are destroyed
628 # alongside this object by calling their destroy method. If set to <tt>:delete_all</tt> all associated
629 # objects are deleted *without* calling their destroy method. If set to <tt>:nullify</tt> all associated
630 # objects' foreign keys are set to +NULL+ *without* calling their save callbacks.
db045dbb » dhh 2004-11-23 Initial 631 # * <tt>:finder_sql</tt> - specify a complete SQL statement to fetch the association. This is a good way to go for complex
a8eea0b0 » dhh 2005-10-26 Fix docs (closes #2491) 632 # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 633 # * <tt>:counter_sql</tt> - specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 634 # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 635 # * <tt>:extend</tt> - specify a named module for extending the proxy. See "Association extensions".
49c801b7 » dhh 2005-11-06 Added :include as an option... 636 # * <tt>:include</tt> - specify second-order associations that should be eager loaded when the collection is loaded.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 637 # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 638 # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
639 # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 640 # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 641 # but not include the joined columns.
642 # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>).
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 643 # * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
9f51eb24 » jeremy 2007-05-22 Fix :through docs wrecked u... 644 # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
645 # or <tt>has_many</tt> association on the join model.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 646 # * <tt>:source</tt>: Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 647 # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
648 # <tt>:subscriber</tt> on +Subscription+, unless a <tt>:source</tt> is given.
649 # * <tt>:source_type</tt>: Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
650 # association is a polymorphic +belongs_to+.
651 # * <tt>:uniq</tt> - if set to +true+, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
db045dbb » dhh 2004-11-23 Initial 652 #
653 # Option examples:
654 # has_many :comments, :order => "posted_on"
49c801b7 » dhh 2005-11-06 Added :include as an option... 655 # has_many :comments, :include => :author
db045dbb » dhh 2004-11-23 Initial 656 # has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
57565b35 » NZKoz 2006-03-09 format fix for locking [Mic... 657 # has_many :tracks, :order => "position", :dependent => :destroy
658 # has_many :comments, :dependent => :nullify
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 659 # has_many :tags, :as => :taggable
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 660 # has_many :subscribers, :through => :subscriptions, :source => :user
db045dbb » dhh 2004-11-23 Initial 661 # has_many :subscribers, :class_name => "Person", :finder_sql =>
662 # 'SELECT DISTINCT people.* ' +
663 # 'FROM people p, post_subscriptions ps ' +
664 # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
665 # 'ORDER BY p.first_name'
c8dd66fd » dhh 2005-11-06 Made association extensions... 666 def has_many(association_id, options = {}, &extension)
6abda696 » dhh 2005-12-02 Added preliminary support f... 667 reflection = create_has_many_reflection(association_id, options, &extension)
04397693 » dhh 2005-09-09 Refactored away all the leg... 668
6abda696 » dhh 2005-12-02 Added preliminary support f... 669 configure_dependency_for_has_many(reflection)
c8dd66fd » dhh 2005-11-06 Made association extensions... 670
6abda696 » dhh 2005-12-02 Added preliminary support f... 671 if options[:through]
b5b16a80 » jeremy 2007-06-27 Define collection singular ... 672 collection_accessor_methods(reflection, HasManyThroughAssociation, false)
6abda696 » dhh 2005-12-02 Added preliminary support f... 673 else
674 add_multiple_associated_save_callbacks(reflection.name)
675 add_association_callbacks(reflection.name, reflection.options)
676 collection_accessor_methods(reflection, HasManyAssociation)
db045dbb » dhh 2004-11-23 Initial 677 end
678 end
679
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 680 # Adds the following methods for retrieval and query of a single associated object:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 681 # +association+ is replaced with the symbol passed as the first argument, so
823554ea » dhh 2005-01-15 Added support for associati... 682 # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 683 # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 684 # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, sets it as the foreign key,
db045dbb » dhh 2004-11-23 Initial 685 # and saves the associate object.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 686 # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 687 # * <tt>build_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 688 # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if
689 # an association already exists. It will NOT work if the association is +nil+.
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 690 # * <tt>create_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 691 # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
db045dbb » dhh 2004-11-23 Initial 692 #
693 # Example: An Account class declares <tt>has_one :beneficiary</tt>, which will add:
3dfa56cc » dhh 2005-06-26 Updated all references to t... 694 # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
db045dbb » dhh 2004-11-23 Initial 695 # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
696 # * <tt>Account#beneficiary.nil?</tt>
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 697 # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
698 # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
699 #
db045dbb » dhh 2004-11-23 Initial 700 # The declaration can also include an options hash to specialize the behavior of the association.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 701 #
db045dbb » dhh 2004-11-23 Initial 702 # Options are:
098fa943 » dhh 2005-02-07 Fixed documentation snafus ... 703 # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
db045dbb » dhh 2004-11-23 Initial 704 # from the association name. So <tt>has_one :manager</tt> will by default be linked to the +Manager+ class, but
705 # if the real class name is +Person+, you'll have to specify it with this option.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 706 # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
707 # SQL fragment, such as <tt>rank = 5</tt>.
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 708 # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
709 # such as <tt>last_name, first_name DESC</tt>
aa04635d » jeremy 2007-10-08 Update :dependent docs and ... 710 # * <tt>:dependent</tt> - if set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 711 # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
712 # object's foreign key is set to +NULL+. Also, association is assigned.
db045dbb » dhh 2004-11-23 Initial 713 # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 714 # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_one+ association will use +person_id+
715 # as the default +foreign_key+.
49c801b7 » dhh 2005-11-06 Added :include as an option... 716 # * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 717 # * <tt>:as</tt>: Specifies a polymorphic interface (See <tt>#belongs_to</tt>).
21399219 » technoweenie 2006-05-22 Add docs for the :as option... 718 #
db045dbb » dhh 2004-11-23 Initial 719 # Option examples:
57565b35 » NZKoz 2006-03-09 format fix for locking [Mic... 720 # has_one :credit_card, :dependent => :destroy # destroys the associated credit card
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 721 # has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it
db045dbb » dhh 2004-11-23 Initial 722 # has_one :last_comment, :class_name => "Comment", :order => "posted_on"
723 # has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
21399219 » technoweenie 2006-05-22 Add docs for the :as option... 724 # has_one :attachment, :as => :attachable
db045dbb » dhh 2004-11-23 Initial 725 def has_one(association_id, options = {})
6abda696 » dhh 2005-12-02 Added preliminary support f... 726 reflection = create_has_one_reflection(association_id, options)
db045dbb » dhh 2004-11-23 Initial 727
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 728 ivar = "@#{reflection.name}"
729
823554ea » dhh 2005-01-15 Added support for associati... 730 module_eval do
731 after_save <<-EOF
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 732 association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
733
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 734 if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id)
6abda696 » dhh 2005-12-02 Added preliminary support f... 735 association["#{reflection.primary_key_name}"] = id
823554ea » dhh 2005-01-15 Added support for associati... 736 association.save(true)
737 end
738 EOF
739 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 740
6abda696 » dhh 2005-12-02 Added preliminary support f... 741 association_accessor_methods(reflection, HasOneAssociation)
742 association_constructor_method(:build, reflection, HasOneAssociation)
743 association_constructor_method(:create, reflection, HasOneAssociation)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 744
6abda696 » dhh 2005-12-02 Added preliminary support f... 745 configure_dependency_for_has_one(reflection)
db045dbb » dhh 2004-11-23 Initial 746 end
747
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 748 # Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 749 # +association+ is replaced with the symbol passed as the first argument, so
823554ea » dhh 2005-01-15 Added support for associati... 750 # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 751 # * <tt>association(force_reload = false)</tt> - returns the associated object. +nil+ is returned if none is found.
db045dbb » dhh 2004-11-23 Initial 752 # * <tt>association=(associate)</tt> - assigns the associate object, extracts the primary key, and sets it as the foreign key.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 753 # * <tt>association.nil?</tt> - returns +true+ if there is no associated object.
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 754 # * <tt>build_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 755 # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 756 # * <tt>create_association(attributes = {})</tt> - returns a new object of the associated type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 757 # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
db045dbb » dhh 2004-11-23 Initial 758 #
e58f2675 » dhh 2005-02-23 Documentation fixes #694 759 # Example: A Post class declares <tt>belongs_to :author</tt>, which will add:
db045dbb » dhh 2004-11-23 Initial 760 # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
761 # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
762 # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
763 # * <tt>Post#author.nil?</tt>
1d4d7217 » dhh 2005-06-21 Fixed docs #856 764 # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
765 # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
db045dbb » dhh 2004-11-23 Initial 766 # The declaration can also include an options hash to specialize the behavior of the association.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 767 #
db045dbb » dhh 2004-11-23 Initial 768 # Options are:
098fa943 » dhh 2005-02-07 Fixed documentation snafus ... 769 # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
db045dbb » dhh 2004-11-23 Initial 770 # from the association name. So <tt>has_one :author</tt> will by default be linked to the +Author+ class, but
771 # if the real class name is +Person+, you'll have to specify it with this option.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 772 # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
773 # SQL fragment, such as <tt>authorized = 1</tt>.
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 774 # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
775 # such as <tt>last_name, first_name DESC</tt>
db045dbb » dhh 2004-11-23 Initial 776 # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
f6379bfe » jeremy 2007-12-20 Eager belongs_to :include i... 777 # of the association with an +_id+ suffix. So a class that defines a +belongs_to :person+ association will use +person_id+ as the default +foreign_key+.
778 # Similarly, +belongs_to :favorite_person, :class_name => "Person"+ will use a foreign key of +favorite_person_id+.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 779 # * <tt>:counter_cache</tt> - caches the number of belonging objects on the associate class through the use of +increment_counter+
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 780 # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
781 # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging +Comment+ class)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 782 # is used on the associate class (such as a +Post+ class). You can also specify a custom counter cache column by providing
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 783 # a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
c1bc61cb » technoweenie 2007-10-13 Add notes to documentation ... 784 # Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
49c801b7 » dhh 2005-11-06 Added :include as an option... 785 # * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 786 # * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 787 # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
c1bc61cb » technoweenie 2007-10-13 Add notes to documentation ... 788 # to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
db045dbb » dhh 2004-11-23 Initial 789 #
790 # Option examples:
791 # belongs_to :firm, :foreign_key => "client_of"
792 # belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 793 # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
db045dbb » dhh 2004-11-23 Initial 794 # :conditions => 'discounts > #{payments_count}'
2597bd69 » technoweenie 2006-03-27 documentation for polymorph... 795 # belongs_to :attachable, :polymorphic => true
823554ea » dhh 2005-01-15 Added support for associati... 796 def belongs_to(association_id, options = {})
6abda696 » dhh 2005-12-02 Added preliminary support f... 797 reflection = create_belongs_to_reflection(association_id, options)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 798
799 ivar = "@#{reflection.name}"
800
6abda696 » dhh 2005-12-02 Added preliminary support f... 801 if reflection.options[:polymorphic]
802 association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
57b7532b » dhh 2005-12-01 Work-in progress for provid... 803
804 module_eval do
805 before_save <<-EOF
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 806 association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
807
94a13091 » technoweenie 2006-08-08 Cache nil results for has_o... 808 if association && association.target
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 809 if association.new_record?
57b7532b » dhh 2005-12-01 Work-in progress for provid... 810 association.save(true)
811 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 812
57b7532b » dhh 2005-12-01 Work-in progress for provid... 813 if association.updated?
6abda696 » dhh 2005-12-02 Added preliminary support f... 814 self["#{reflection.primary_key_name}"] = association.id
0859779d » technoweenie 2006-03-15 Allow :dependent options to... 815 self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s
57b7532b » dhh 2005-12-01 Work-in progress for provid... 816 end
5213a1f7 » jamis 2005-09-20 Fixed saving a record with ... 817 end
57b7532b » dhh 2005-12-01 Work-in progress for provid... 818 EOF
819 end
820 else
6abda696 » dhh 2005-12-02 Added preliminary support f... 821 association_accessor_methods(reflection, BelongsToAssociation)
822 association_constructor_method(:build, reflection, BelongsToAssociation)
823 association_constructor_method(:create, reflection, BelongsToAssociation)
57b7532b » dhh 2005-12-01 Work-in progress for provid... 824
825 module_eval do
826 before_save <<-EOF
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 827 association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
828
829 if !association.nil?
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 830 if association.new_record?
57b7532b » dhh 2005-12-01 Work-in progress for provid... 831 association.save(true)
832 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 833
57b7532b » dhh 2005-12-01 Work-in progress for provid... 834 if association.updated?
6abda696 » dhh 2005-12-02 Added preliminary support f... 835 self["#{reflection.primary_key_name}"] = association.id
57b7532b » dhh 2005-12-01 Work-in progress for provid... 836 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 837 end
57b7532b » dhh 2005-12-01 Work-in progress for provid... 838 EOF
839 end
840 end
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 841
2a305949 » technoweenie 2007-03-27 documentation project patch... 842 # Create the callbacks to update counter cache
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 843 if options[:counter_cache]
87898bad » jamis 2006-03-09 Allow counter_cache to acce... 844 cache_column = options[:counter_cache] == true ?
845 "#{self.to_s.underscore.pluralize}_count" :
846 options[:counter_cache]
847
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 848 module_eval(
87898bad » jamis 2006-03-09 Allow counter_cache to acce... 849 "after_create '#{reflection.name}.class.increment_counter(\"#{cache_column}\", #{reflection.primary_key_name})" +
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 850 " unless #{reflection.name}.nil?'"
851 )
852
853 module_eval(
87898bad » jamis 2006-03-09 Allow counter_cache to acce... 854 "before_destroy '#{reflection.name}.class.decrement_counter(\"#{cache_column}\", #{reflection.primary_key_name})" +
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 855 " unless #{reflection.name}.nil?'"
66d05f5e » technoweenie 2007-09-30 Add attr_readonly to specif... 856 )
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 857
66d05f5e » technoweenie 2007-09-30 Add attr_readonly to specif... 858 module_eval(
24c2457a » technoweenie 2007-10-05 Don't call attr_readonly on... 859 "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
66d05f5e » technoweenie 2007-09-30 Add attr_readonly to specif... 860 )
964b67dd » jamis 2006-03-04 Make counter_cache work wit... 861 end
823554ea » dhh 2005-01-15 Added support for associati... 862 end
863
db045dbb » dhh 2004-11-23 Initial 864 # Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 865 # an option, it is guessed using the lexical order of the class names. So a join between +Developer+ and +Project+
866 # will give the default join table name of +developers_projects+ because "D" outranks "P". Note that this precedence
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 867 # is calculated using the <tt><</tt> operator for <tt>String</tt>. This means that if the strings are of different lengths,
56c55354 » technoweenie 2007-01-15 [DOC] clear up some ambigui... 868 # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 869 # lexical precedence than the shorter one. For example, one would expect the tables <tt>paper_boxes</tt> and <tt>papers</tt>
56c55354 » technoweenie 2007-01-15 [DOC] clear up some ambigui... 870 # to generate a join table name of <tt>papers_paper_boxes</tt> because of the length of the name <tt>paper_boxes</tt>,
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 871 # but it in fact generates a join table name of <tt>paper_boxes_papers</tt>. Be aware of this caveat, and use the
56c55354 » technoweenie 2007-01-15 [DOC] clear up some ambigui... 872 # custom <tt>join_table</tt> option if you need to.
db045dbb » dhh 2004-11-23 Initial 873 #
53aa8da1 » dhh 2006-04-01 Fixed that records returned... 874 # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 875 # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
e3b49c05 » dhh 2007-09-28 Fixed spelling errors (clos... 876 # +ReadOnly+ (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
53aa8da1 » dhh 2006-04-01 Fixed that records returned... 877 # associations with attributes to a real join model (see introduction).
db045dbb » dhh 2004-11-23 Initial 878 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 879 # Adds the following methods for retrieval and query:
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 880 # +collection+ is replaced with the symbol passed as the first argument, so
4eab3758 » dhh 2005-02-23 Finished polishing API docs 881 # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
db045dbb » dhh 2004-11-23 Initial 882 # * <tt>collection(force_reload = false)</tt> - returns an array of all the associated objects.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 883 # An empty array is returned if none are found.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 884 # * <tt>collection<<(object, ...)</tt> - adds one or more objects to the collection by creating associations in the join table
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 885 # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 886 # * <tt>collection.delete(object, ...)</tt> - removes one or more objects from the collection by removing their associations from the join table.
db045dbb » dhh 2004-11-23 Initial 887 # This does not destroy the objects.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 888 # * <tt>collection=objects</tt> - replaces the collection's content by deleting and adding objects as appropriate.
889 # * <tt>collection_singular_ids</tt> - returns an array of the associated objects' ids
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 890 # * <tt>collection_singular_ids=ids</tt> - replace the collection by the objects identified by the primary keys in +ids+
db045dbb » dhh 2004-11-23 Initial 891 # * <tt>collection.clear</tt> - removes every object from the collection. This does not destroy the objects.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 892 # * <tt>collection.empty?</tt> - returns +true+ if there are no associated objects.
db045dbb » dhh 2004-11-23 Initial 893 # * <tt>collection.size</tt> - returns the number of associated objects.
823554ea » dhh 2005-01-15 Added support for associati... 894 # * <tt>collection.find(id)</tt> - finds an associated object responding to the +id+ and that
895 # meets the condition that it has to be associated with this object.
06075a9e » technoweenie 2006-05-28 Fix the has_and_belongs_to_... 896 # * <tt>collection.build(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 897 # with +attributes+ and linked to this object through the join table, but has not yet been saved.
06075a9e » technoweenie 2006-05-28 Fix the has_and_belongs_to_... 898 # * <tt>collection.create(attributes = {})</tt> - returns a new object of the collection type that has been instantiated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 899 # with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
db045dbb » dhh 2004-11-23 Initial 900 #
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 901 # Example: A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
db045dbb » dhh 2004-11-23 Initial 902 # * <tt>Developer#projects</tt>
903 # * <tt>Developer#projects<<</tt>
904 # * <tt>Developer#projects.delete</tt>
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 905 # * <tt>Developer#projects=</tt>
cb0837a2 » dhh 2006-10-08 Doc fixes (closes #6325) 906 # * <tt>Developer#project_ids</tt>
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 907 # * <tt>Developer#project_ids=</tt>
db045dbb » dhh 2004-11-23 Initial 908 # * <tt>Developer#projects.clear</tt>
909 # * <tt>Developer#projects.empty?</tt>
910 # * <tt>Developer#projects.size</tt>
911 # * <tt>Developer#projects.find(id)</tt>
06075a9e » technoweenie 2006-05-28 Fix the has_and_belongs_to_... 912 # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
913 # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
db045dbb » dhh 2004-11-23 Initial 914 # The declaration may include an options hash to specialize the behavior of the association.
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 915 #
db045dbb » dhh 2004-11-23 Initial 916 # Options are:
098fa943 » dhh 2005-02-07 Fixed documentation snafus ... 917 # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 918 # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
db045dbb » dhh 2004-11-23 Initial 919 # +Project+ class, but if the real class name is +SuperProject+, you'll have to specify it with this option.
920 # * <tt>:join_table</tt> - specify the name of the join table if the default based on lexical order isn't what you want.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 921 # WARNING: If you're overwriting the table name of either class, the +table_name+ method MUST be declared underneath any
922 # +has_and_belongs_to_many+ declaration in order to work.
db045dbb » dhh 2004-11-23 Initial 923 # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 924 # of this class in lower-case and +_id+ suffixed. So a +Person+ class that makes a +has_and_belongs_to_many+ association
925 # will use +person_id+ as the default +foreign_key+.
db045dbb » dhh 2004-11-23 Initial 926 # * <tt>:association_foreign_key</tt> - specify the association foreign key used for the association. By default this is
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 927 # guessed to be the name of the associated class in lower-case and +_id+ suffixed. So if the associated class is +Project+,
928 # the +has_and_belongs_to_many+ association will use +project_id+ as the default association +foreign_key+.
929 # * <tt>:conditions</tt> - specify the conditions that the associated object must meet in order to be included as a +WHERE+
930 # SQL fragment, such as <tt>authorized = 1</tt>.
7143d801 » Marcel Molina 2007-11-07 Smattering of grammatical f... 931 # * <tt>:order</tt> - specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
932 # such as <tt>last_name, first_name DESC</tt>
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 933 # * <tt>:uniq</tt> - if set to +true+, duplicate associated objects will be ignored by accessors and query methods
934 # * <tt>:finder_sql</tt> - overwrite the default generated SQL statement used to fetch the association with a manual statement
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 935 # * <tt>:delete_sql</tt> - overwrite the default generated SQL statement used to remove links between the associated
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 936 # classes with a manual statement
937 # * <tt>:insert_sql</tt> - overwrite the default generated SQL statement used to add links between the associated classes
938 # with a manual statement
8c512a1c » dhh 2005-11-03 Added extension capabilitie... 939 # * <tt>:extend</tt> - anonymous module for extending the proxy, see "Association extensions".
49c801b7 » dhh 2005-11-06 Added :include as an option... 940 # * <tt>:include</tt> - specify second-order associations that should be eager loaded when the collection is loaded.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 941 # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 942 # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
943 # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
18a3333a » NZKoz 2007-08-28 Formatting, grammar and spe... 944 # * <tt>:select</tt>: By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
945 # but not include the joined columns.
db045dbb » dhh 2004-11-23 Initial 946 #
947 # Option examples:
948 # has_and_belongs_to_many :projects
49c801b7 » dhh 2005-11-06 Added :include as an option... 949 # has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
db045dbb » dhh 2004-11-23 Initial 950 # has_and_belongs_to_many :nations, :class_name => "Country"
951 # has_and_belongs_to_many :categories, :join_table => "prods_cats"
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 952 # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
190e0464 » dhh 2005-05-19 Fixed that :delete_sql in h... 953 # 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
c8dd66fd » dhh 2005-11-06 Made association extensions... 954 def has_and_belongs_to_many(association_id, options = {}, &extension)
6abda696 » dhh 2005-12-02 Added preliminary support f... 955 reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 956
6abda696 » dhh 2005-12-02 Added preliminary support f... 957 add_multiple_associated_save_callbacks(reflection.name)
958 collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
db045dbb » dhh 2004-11-23 Initial 959
35b4bdcf » jeremy 2005-11-08 Destroy associated has_and_... 960 # Don't use a before_destroy callback since users' before_destroy
961 # callbacks will be executed after the association is wiped out.
6abda696 » dhh 2005-12-02 Added preliminary support f... 962 old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
88f951a5 » jeremy 2007-10-27 Allow association redefinit... 963 class_eval <<-end_eval unless method_defined?(old_method)
35b4bdcf » jeremy 2005-11-08 Destroy associated has_and_... 964 alias_method :#{old_method}, :destroy_without_callbacks
965 def destroy_without_callbacks
6abda696 » dhh 2005-12-02 Added preliminary support f... 966 #{reflection.name}.clear
35b4bdcf » jeremy 2005-11-08 Destroy associated has_and_... 967 #{old_method}
968 end
969 end_eval
970
6abda696 » dhh 2005-12-02 Added preliminary support f... 971 add_association_callbacks(reflection.name, options)
db045dbb » dhh 2004-11-23 Initial 972 end
973
974 private
2a305949 » technoweenie 2007-03-27 documentation project patch... 975 # Generate a join table name from two provided tables names.
976 # The order of names in join name is determined by lexical precedence.
977 # join_table_name("members", "clubs")
978 # => "clubs_members"
979 # join_table_name("members", "special_clubs")
980 # => "members_special_clubs"
db045dbb » dhh 2004-11-23 Initial 981 def join_table_name(first_table_name, second_table_name)
982 if first_table_name < second_table_name
983 join_table = "#{first_table_name}_#{second_table_name}"
984 else
985 join_table = "#{second_table_name}_#{first_table_name}"
986 end
987
988 table_name_prefix + join_table + table_name_suffix
989 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 990
6abda696 » dhh 2005-12-02 Added preliminary support f... 991 def association_accessor_methods(reflection, association_proxy_class)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 992 ivar = "@#{reflection.name}"
993
6abda696 » dhh 2005-12-02 Added preliminary support f... 994 define_method(reflection.name) do |*params|
823554ea » dhh 2005-01-15 Added support for associati... 995 force_reload = params.first unless params.empty?
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 996
997 association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
6abda696 » dhh 2005-12-02 Added preliminary support f... 998
999 if association.nil? || force_reload
1000 association = association_proxy_class.new(self, reflection)
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1001 retval = association.reload
94a13091 » technoweenie 2006-08-08 Cache nil results for has_o... 1002 if retval.nil? and association_proxy_class == BelongsToAssociation
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1003 instance_variable_set(ivar, nil)
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1004 return nil
1005 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1006 instance_variable_set(ivar, association)
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1007 end
94a13091 » technoweenie 2006-08-08 Cache nil results for has_o... 1008
1009 association.target.nil? ? nil : association
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1010 end
1011
6abda696 » dhh 2005-12-02 Added preliminary support f... 1012 define_method("#{reflection.name}=") do |new_value|
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1013 association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
1014
4afd6c9f » jeremy 2007-05-21 belongs_to assignment creat... 1015 if association.nil? || association.target != new_value
6abda696 » dhh 2005-12-02 Added preliminary support f... 1016 association = association_proxy_class.new(self, reflection)
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1017 end
6abda696 » dhh 2005-12-02 Added preliminary support f... 1018
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1019 association.replace(new_value)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1020
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1021 instance_variable_set(ivar, new_value.nil? ? nil : association)
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1022 end
f8783abf » dhh 2005-04-03 Made eager loading work eve... 1023
6abda696 » dhh 2005-12-02 Added preliminary support f... 1024 define_method("set_#{reflection.name}_target") do |target|
18057d2f » jeremy 2006-08-17 Cache nil results for :incl... 1025 return if target.nil? and association_proxy_class == BelongsToAssociation
6abda696 » dhh 2005-12-02 Added preliminary support f... 1026 association = association_proxy_class.new(self, reflection)
f8783abf » dhh 2005-04-03 Made eager loading work eve... 1027 association.target = target
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1028 instance_variable_set(ivar, association)
f8783abf » dhh 2005-04-03 Made eager loading work eve... 1029 end
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1030 end
1031
6abda696 » dhh 2005-12-02 Added preliminary support f... 1032 def collection_reader_method(reflection, association_proxy_class)
1033 define_method(reflection.name) do |*params|
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1034 ivar = "@#{reflection.name}"
1035
bce0e149 » dhh 2005-01-18 Fixed that the belongs_to a... 1036 force_reload = params.first unless params.empty?
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1037 association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1038
823554ea » dhh 2005-01-15 Added support for associati... 1039 unless association.respond_to?(:loaded?)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1040 association = association_proxy_class.new(self, reflection)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1041 instance_variable_set(ivar, association)
db045dbb » dhh 2004-11-23 Initial 1042 end
6abda696 » dhh 2005-12-02 Added preliminary support f... 1043
823554ea » dhh 2005-01-15 Added support for associati... 1044 association.reload if force_reload
6abda696 » dhh 2005-12-02 Added preliminary support f... 1045
823554ea » dhh 2005-01-15 Added support for associati... 1046 association
1047 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1048
1049 define_method("#{reflection.name.to_s.singularize}_ids") do
1050 send(reflection.name).map(&:id)
1051 end
6abda696 » dhh 2005-12-02 Added preliminary support f... 1052 end
db045dbb » dhh 2004-11-23 Initial 1053
b5b16a80 » jeremy 2007-06-27 Define collection singular ... 1054 def collection_accessor_methods(reflection, association_proxy_class, writer = true)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1055 collection_reader_method(reflection, association_proxy_class)
1056
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1057 if writer
1058 define_method("#{reflection.name}=") do |new_value|
1059 # Loads proxy class instance (defined in collection_reader_method) if not already loaded
1060 association = send(reflection.name)
1061 association.replace(new_value)
1062 association
1063 end
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 1064
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1065 define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
1066 ids = (new_value || []).reject { |nid| nid.blank? }
1067 send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
1068 end
0092d0ac » jeremy 2006-10-01 Association collections hav... 1069 end
db045dbb » dhh 2004-11-23 Initial 1070 end
1071
823554ea » dhh 2005-01-15 Added support for associati... 1072 def add_multiple_associated_save_callbacks(association_name)
e19bd169 » jeremy 2005-10-01 Association validation does... 1073 method_name = "validate_associated_records_for_#{association_name}".to_sym
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1074 ivar = "@#{association_name}"
1075
e19bd169 » jeremy 2005-10-01 Association validation does... 1076 define_method(method_name) do
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1077 association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
1078
e19bd169 » jeremy 2005-10-01 Association validation does... 1079 if association.respond_to?(:loaded?)
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 1080 if new_record?
e19bd169 » jeremy 2005-10-01 Association validation does... 1081 association
1082 else
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 1083 association.select { |record| record.new_record? }
e19bd169 » jeremy 2005-10-01 Association validation does... 1084 end.each do |record|
1085 errors.add "#{association_name}" unless record.valid?
db045dbb » dhh 2004-11-23 Initial 1086 end
e19bd169 » jeremy 2005-10-01 Association validation does... 1087 end
823554ea » dhh 2005-01-15 Added support for associati... 1088 end
db045dbb » dhh 2004-11-23 Initial 1089
e19bd169 » jeremy 2005-10-01 Association validation does... 1090 validate method_name
85fbb22f » dhh 2006-09-05 Backed out of new_record? t... 1091 before_save("@new_record_before_save = new_record?; true")
e19bd169 » jeremy 2005-10-01 Association validation does... 1092
1093 after_callback = <<-end_eval
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1094 association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
989332c7 » jeremy 2007-06-21 Save associated records onl... 1095
1c881ca5 » NZKoz 2007-10-09 Ensure that 'autosaving' wo... 1096 records_to_save = if @new_record_before_save
1097 association
1098 elsif association.respond_to?(:loaded?) && association.loaded?
1099 association.select { |record| record.new_record? }
1100 else
1101 []
1102 end
1103
cfbd790a » jeremy 2007-10-16 Fix regression where the as... 1104 records_to_save.each { |record| association.send(:insert_record, record) } unless records_to_save.blank?
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1105
cfbd790a » jeremy 2007-10-16 Fix regression where the as... 1106 # reconstruct the SQL queries now that we know the owner's id
1107 association.send(:construct_sql) if association.respond_to?(:construct_sql)
e19bd169 » jeremy 2005-10-01 Association validation does... 1108 end_eval
989332c7 » jeremy 2007-06-21 Save associated records onl... 1109
e19bd169 » jeremy 2005-10-01 Association validation does... 1110 # Doesn't use after_save as that would save associations added in after_create/after_update twice
1111 after_create(after_callback)
1112 after_update(after_callback)
db045dbb » dhh 2004-11-23 Initial 1113 end
abc895b8 » dhh 2005-04-03 Added new Base.find API and... 1114
6abda696 » dhh 2005-12-02 Added preliminary support f... 1115 def association_constructor_method(constructor, reflection, association_proxy_class)
1116 define_method("#{constructor}_#{reflection.name}") do |*params|
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1117 ivar = "@#{reflection.name}"
1118
2bdaff4a » dhh 2005-06-06 Added a second parameter to... 1119 attributees = params.first unless params.empty?
1120 replace_existing = params[1].nil? ? true : params[1]
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1121 association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 1122
1123 if association.nil?
6abda696 » dhh 2005-12-02 Added preliminary support f... 1124 association = association_proxy_class.new(self, reflection)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1125 instance_variable_set(ivar, association)
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 1126 end
1127
bfe6a759 » dhh 2005-06-15 Added actual database-chang... 1128 if association_proxy_class == HasOneAssociation
1129 association.send(constructor, attributees, replace_existing)
1130 else
1131 association.send(constructor, attributees)
1132 end
3b9e90a4 » dhh 2005-04-11 Moved build_association and... 1133 end
1134 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1135
abc895b8 » dhh 2005-04-03 Added new Base.find API and... 1136 def find_with_associations(options = {})
31d8169e » technoweenie 2006-04-05 Fixed that loading includin... 1137 catch :invalid_query do
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1138 join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
31d8169e » technoweenie 2006-04-05 Fixed that loading includin... 1139 rows = select_all_rows(options, join_dependency)
1140 return join_dependency.instantiate(rows)
1141 end
1142 []
3a7be80f » dhh 2006-03-15 Change LEFT OUTER JOIN auth... 1143 end
6f344000 » dhh 2005-04-19 Fixed order of loading in e... 1144
aa04635d » jeremy 2007-10-08 Update :dependent docs and ... 1145 # See HasManyAssociation#delete_records. Dependent associations
1146 # delete children, otherwise foreign key is set to NULL.
6abda696 » dhh 2005-12-02 Added preliminary support f... 1147 def configure_dependency_for_has_many(reflection)
aa04635d » jeremy 2007-10-08 Update :dependent docs and ... 1148 if reflection.options.include?(:dependent)
1149 # Add polymorphic type if the :as option is present
1150 dependent_conditions = []
1151 dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
1152 dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
1153 dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
1154 dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
0859779d » technoweenie 2006-03-15 Allow :dependent options to... 1155
aa04635d » jeremy 2007-10-08 Update :dependent docs and ... 1156 case reflection.options[:dependent]
1157 when :destroy
1158 module_eval "before_destroy '#{reflection.name}.each { |o| o.destroy }'"
1159 when :delete_all
1160 module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
1161 when :nullify
1162 module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
1163 else
1164 raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
1165 end
6abda696 » dhh 2005-12-02 Added preliminary support f... 1166 end
1167 end
3f1acf49 » jeremy 2006-09-15 Deprecation tests. Remove w... 1168
6abda696 » dhh 2005-12-02 Added preliminary support f... 1169 def configure_dependency_for_has_one(reflection)
aa04635d » jeremy 2007-10-08 Update :dependent docs and ... 1170 if reflection.options.include?(:dependent)
1171 case reflection.options[:dependent]
1172 when :destroy
1173 module_eval "before_destroy '#{reflection.name}.destroy unless #{reflection.name}.nil?'"
1174 when :delete
1175 module_eval "before_destroy '#{reflection.class_name}.delete(#{reflection.name}.id) unless #{reflection.name}.nil?'"
1176 when :nullify
1177 module_eval "before_destroy '#{reflection.name}.update_attribute(\"#{reflection.primary_key_name}\", nil) unless #{reflection.name}.nil?'"
1178 else
1179 raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
1180 end
6abda696 » dhh 2005-12-02 Added preliminary support f... 1181 end
1182 end
1183
1184 def create_has_many_reflection(association_id, options, &extension)
1185 options.assert_valid_keys(
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 1186 :class_name, :table_name, :foreign_key,
6246fad1 » NZKoz 2007-09-02 Remove deprecated functiona... 1187 :dependent,
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 1188 :select, :conditions, :include, :order, :group, :limit, :offset,
e3dab67c » technoweenie 2007-03-12 Allow a polymorphic :source... 1189 :as, :through, :source, :source_type,
50f538b7 » jeremy 2006-05-06 Allow :uniq => true with ha... 1190 :uniq,
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1191 :finder_sql, :counter_sql,
1192 :before_add, :after_add, :before_remove, :after_remove,
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 1193 :extend
6abda696 » dhh 2005-12-02 Added preliminary support f... 1194 )
1195
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1196 options[:extend] = create_extension_modules(association_id, extension, options[:extend])
6abda696 » dhh 2005-12-02 Added preliminary support f... 1197
8203a2af » dhh 2006-02-26 Dont require association cl... 1198 create_reflection(:has_many, association_id, options, self)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1199 end
1200
1201 def create_has_one_reflection(association_id, options)
1202 options.assert_valid_keys(
b3065a51 » jeremy 2006-02-09 Polymorphic join support fo... 1203 :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as
6abda696 » dhh 2005-12-02 Added preliminary support f... 1204 )
1205
8203a2af » dhh 2006-02-26 Dont require association cl... 1206 create_reflection(:has_one, association_id, options, self)
6abda696 » dhh 2005-12-02 Added preliminary support f... 1207 end
1208
1209 def create_belongs_to_reflection(association_id, options)
1210 options.assert_valid_keys(
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1211 :class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent,
6abda696 » dhh 2005-12-02 Added preliminary support f... 1212 :counter_cache, :extend, :polymorphic
1213 )
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1214
6abda696 » dhh 2005-12-02 Added preliminary support f... 1215 reflection = create_reflection(:belongs_to, association_id, options, self)
1216
1217 if options[:polymorphic]
1218 reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
1219 end
1220
1221 reflection
1222 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1223
6abda696 » dhh 2005-12-02 Added preliminary support f... 1224 def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1225 options.assert_valid_keys(
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1226 :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 1227 :select, :conditions, :include, :order, :group, :limit, :offset,
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1228 :uniq,
50f538b7 » jeremy 2006-05-06 Allow :uniq => true with ha... 1229 :finder_sql, :delete_sql, :insert_sql,
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1230 :before_add, :after_add, :before_remove, :after_remove,
e5d9ad3e » dhh 2005-12-12 Added option inheritance fo... 1231 :extend
6abda696 » dhh 2005-12-02 Added preliminary support f... 1232 )
1233
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1234 options[:extend] = create_extension_modules(association_id, extension, options[:extend])
6abda696 » dhh 2005-12-02 Added preliminary support f... 1235
1236 reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
1237
1238 reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1239
6abda696 » dhh 2005-12-02 Added preliminary support f... 1240 reflection
1241 end
1242
49d0f0cb » dhh 2005-04-18 Speeded up eager loading a ... 1243 def reflect_on_included_associations(associations)
251a5d45 » seckar 2005-09-18 Fix eager loading error mes... 1244 [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) }
49d0f0cb » dhh 2005-04-18 Speeded up eager loading a ... 1245 end
1246
5b9b904f » dhh 2005-07-10 Added support for limit and... 1247 def guard_against_unlimitable_reflections(reflections, options)
1248 if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections)
1249 raise(
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1250 ConfigurationError,
5b9b904f » dhh 2005-07-10 Added support for limit and... 1251 "You can not use offset and limit together with has_many or has_and_belongs_to_many associations"
1252 )
1253 end
1254 end
1255
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1256 def select_all_rows(options, join_dependency)
49d0f0cb » dhh 2005-04-18 Speeded up eager loading a ... 1257 connection.select_all(
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1258 construct_finder_sql_with_included_associations(options, join_dependency),
49d0f0cb » dhh 2005-04-18 Speeded up eager loading a ... 1259 "#{name} Load Including Associations"
1260 )
1261 end
057cf491 » dhh 2005-04-10 Added support for has_and_b... 1262
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1263 def construct_finder_sql_with_included_associations(options, join_dependency)
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1264 scope = scope(:find)
a5fded3e » dhh 2007-12-04 Fix that options[:from] tab... 1265 sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1266 sql << join_dependency.join_associations.collect{|join| join.association_join }.join
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1267
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1268 add_joins!(sql, options, scope)
1269 add_conditions!(sql, options[:conditions], scope)
d016d9a6 » dhh 2006-06-03 Fixed usage of :limit and w... 1270 add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
851dd080 » dhh 2005-10-18 Added support for using lim... 1271
3566be47 » dhh 2007-08-21 Fixed that eager loading qu... 1272 add_group!(sql, options[:group], scope)
9d2da046 » jeremy 2006-11-09 Cache inheritance_column. C... 1273 add_order!(sql, options[:order], scope)
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1274 add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
8dea60b0 » jeremy 2006-12-05 find supports :lock with :i... 1275 add_lock!(sql, options, scope)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1276
abc895b8 » dhh 2005-04-03 Added new Base.find API and... 1277 return sanitize_sql(sql)
1278 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1279
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1280 def add_limited_ids_condition!(sql, options, join_dependency)
1281 unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1282 sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
31d8169e » technoweenie 2006-04-05 Fixed that loading includin... 1283 else
1284 throw :invalid_query
851dd080 » dhh 2005-10-18 Added support for using lim... 1285 end
1286 end
4965b1b9 » jeremy 2007-09-22 Correctly quote id list for... 1287
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1288 def select_limited_ids_list(options, join_dependency)
4965b1b9 » jeremy 2007-09-22 Correctly quote id list for... 1289 pk = columns_hash[primary_key]
1290
5ea76fab » technoweenie 2006-04-18 Associations#select_limited... 1291 connection.select_all(
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1292 construct_finder_sql_for_association_limiting(options, join_dependency),
851dd080 » dhh 2005-10-18 Added support for using lim... 1293 "#{name} Load IDs For Limited Eager Loading"
4965b1b9 » jeremy 2007-09-22 Correctly quote id list for... 1294 ).collect { |row| connection.quote(row[primary_key], pk) }.join(", ")
851dd080 » dhh 2005-10-18 Added support for using lim... 1295 end
c0bce43e » jeremy 2006-11-10 Oracle: fix limited id sele... 1296
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1297 def construct_finder_sql_for_association_limiting(options, join_dependency)
e789b26e » technoweenie 2006-10-13 fix select_limited_ids_list... 1298 scope = scope(:find)
b8657089 » jeremy 2007-09-15 Eager loading respects expl... 1299 is_distinct = !options[:joins].blank? || include_eager_conditions?(options) || include_eager_order?(options)
d49a5fcb » NZKoz 2006-02-09 * Fix pagination problems w... 1300 sql = "SELECT "
e789b26e » technoweenie 2006-10-13 fix select_limited_ids_list... 1301 if is_distinct
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1302 sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", options[:order])
e789b26e » technoweenie 2006-10-13 fix select_limited_ids_list... 1303 else
1304 sql << primary_key
1305 end
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1306 sql << " FROM #{connection.quote_table_name table_name} "
c0bce43e » jeremy 2006-11-10 Oracle: fix limited id sele... 1307
e789b26e » technoweenie 2006-10-13 fix select_limited_ids_list... 1308 if is_distinct
c98b471c » technoweenie 2006-10-08 Reverted old select_limited... 1309 sql << join_dependency.join_associations.collect(&:association_join).join
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1310 add_joins!(sql, options, scope)
d49a5fcb » NZKoz 2006-02-09 * Fix pagination problems w... 1311 end
c0bce43e » jeremy 2006-11-10 Oracle: fix limited id sele... 1312
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1313 add_conditions!(sql, options[:conditions], scope)
3566be47 » dhh 2007-08-21 Fixed that eager loading qu... 1314 add_group!(sql, options[:group], scope)
0e452bb0 » dhh 2007-08-21 Fixed that eager loading qu... 1315
c8b6b482 » Marcel Molina 2007-10-23 Limited eager loading no lo... 1316 if options[:order] && is_distinct
1317 connection.add_order_by_for_association_limiting!(sql, options)
1318 else
1319 add_order!(sql, options[:order], scope)
ef4ac31d » jeremy 2007-01-11 PostgreSQL: use a subselect... 1320 end
0e452bb0 » dhh 2007-08-21 Fixed that eager loading qu... 1321
c9c18520 » dhh 2006-03-27 Making ActiveRecord faster ... 1322 add_limit!(sql, options, scope)
0e452bb0 » dhh 2007-08-21 Fixed that eager loading qu... 1323
851dd080 » dhh 2005-10-18 Added support for using lim... 1324 return sanitize_sql(sql)
1325 end
c98b471c » technoweenie 2006-10-08 Reverted old select_limited... 1326
2a2afca0 » technoweenie 2006-04-19 Ensure that Associations#in... 1327 # Checks if the conditions reference a table other than the current model table
851dd080 » dhh 2005-10-18 Added support for using lim... 1328 def include_eager_conditions?(options)
2a2afca0 » technoweenie 2006-04-19 Ensure that Associations#in... 1329 # look in both sets of conditions
1330 conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
1331 case cond
1332 when nil then all
1333 when Array then all << cond.first
1334 else all << cond
1335 end
1336 end
1337 return false unless conditions.any?
d22f9c94 » Marcel Molina 2006-05-21 Fix Oracle boolean support ... 1338 conditions.join(' ').scan(/([\.\w]+)\.\w+/).flatten.any? do |condition_table_name|
851dd080 » dhh 2005-10-18 Added support for using lim... 1339 condition_table_name != table_name
1340 end
1341 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1342
2a2afca0 » technoweenie 2006-04-19 Ensure that Associations#in... 1343 # Checks if the query order references a table other than the current model's table.
d49a5fcb » NZKoz 2006-02-09 * Fix pagination problems w... 1344 def include_eager_order?(options)
1345 order = options[:order]
1346 return false unless order
d22f9c94 » Marcel Molina 2006-05-21 Fix Oracle boolean support ... 1347 order.scan(/([\.\w]+)\.\w+/).flatten.any? do |order_table_name|
d49a5fcb » NZKoz 2006-02-09 * Fix pagination problems w... 1348 order_table_name != table_name
1349 end
1350 end
851dd080 » dhh 2005-10-18 Added support for using lim... 1351
5b9b904f » dhh 2005-07-10 Added support for limit and... 1352 def using_limitable_reflections?(reflections)
1353 reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero?
1354 end
1355
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1356 def column_aliases(join_dependency)
1357 join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1358 "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
057cf491 » dhh 2005-04-10 Added support for has_and_b... 1359 end
1360
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 1361 def add_association_callbacks(association_name, options)
e19bd169 » jeremy 2005-10-01 Association validation does... 1362 callbacks = %w(before_add after_add before_remove after_remove)
1363 callbacks.each do |callback_name|
7cc446ac » Marcel Molina 2006-04-15 DRY up association collecti... 1364 full_callback_name = "#{callback_name}_for_#{association_name}"
e19bd169 » jeremy 2005-10-01 Association validation does... 1365 defined_callbacks = options[callback_name.to_sym]
1366 if options.has_key?(callback_name.to_sym)
1367 class_inheritable_reader full_callback_name.to_sym
88f951a5 » jeremy 2007-10-27 Allow association redefinit... 1368 write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
1369 else
1370 write_inheritable_attribute(full_callback_name.to_sym, [])
e19bd169 » jeremy 2005-10-01 Association validation does... 1371 end
1372 end
4180e57b » dhh 2005-07-04 Added callback hooks to ass... 1373 end
057cf491 » dhh 2005-04-10 Added support for has_and_b... 1374
851dd080 » dhh 2005-10-18 Added support for using lim... 1375 def condition_word(sql)
1376 sql =~ /where/i ? " AND " : "WHERE "
1377 end
e19bd169 » jeremy 2005-10-01 Association validation does... 1378
81d619ea » jeremy 2007-09-17 Associations macros accept ... 1379 def create_extension_modules(association_id, block_extension, extensions)
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1380 if block_extension
1381 extension_module_name = "#{self.to_s}#{association_id.to_s.camelize}AssociationExtension"
c8dd66fd » dhh 2005-11-06 Made association extensions... 1382
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1383 silence_warnings do
1384 Object.const_set(extension_module_name, Module.new(&block_extension))
1385 end
1386 Array(extensions).push(extension_module_name.constantize)
1387 else
1388 Array(extensions)
c8dd66fd » dhh 2005-11-06 Made association extensions... 1389 end
1390 end
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1391
f384622a » dhh 2006-07-04 Doc fixes 1392 class JoinDependency # :nodoc:
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1393 attr_reader :joins, :reflections, :table_aliases
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1394
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1395 def initialize(base, associations, joins)
ea25e246 » technoweenie 2006-03-19 Quit ignoring default :incl... 1396 @joins = [JoinBase.new(base, joins)]
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1397 @associations = associations
1398 @reflections = []
1399 @base_records_hash = {}
1400 @base_records_in_order = []
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1401 @table_aliases = Hash.new { |aliases, table| aliases[table] = 0 }
1402 @table_aliases[base.table_name] = 1
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1403 build(associations)
1404 end
1405
1406 def join_associations
1407 @joins[1..-1].to_a
1408 end
1409
1410 def join_base
1411 @joins[0]
1412 end
1413
1414 def instantiate(rows)
1415 rows.each_with_index do |row, i|
1416 primary_id = join_base.record_id(row)
1417 unless @base_records_hash[primary_id]
1418 @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row))
1419 end
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1420 construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1421 end
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1422 remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1423 return @base_records_in_order
1424 end
1425
204c2755 » jeremy 2007-10-28 Associations: speedup dupli... 1426 def remove_duplicate_results!(base, records, associations)
1427 case associations
1428 when Symbol, String
1429 reflection = base.reflections[associations]
1430 if reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
1431 records.each { |record| record.send(reflection.name).target.uniq! }
1432 end
1433 when Array
1434 associations.each do |association|
1435 remove_duplicate_results!(base, records, association)
1436 end
1437 when Hash
1438 associations.keys.each do |name|
1439 reflection = base.reflections[name]
1440 is_collection = [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
1441
1442 parent_records = records.map do |record|
1443 next unless record.send(reflection.name)
1444 is_collection ? record.send(reflection.name).target.uniq! : record.send(reflection.name)
1445 end.flatten.compact
1446
1447 remove_duplicate_results!(reflection.class_name.constantize, parent_records, associations[name]) unless parent_records.empty?
1448 end
1449 end
1450 end
1451
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1452 protected
1453 def build(associations, parent = nil)
1454 parent ||= @joins.last
1455 case associations
1456 when Symbol, String
1457 reflection = parent.reflections[associations.to_s.intern] or
1458 raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
1459 @reflections << reflection
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1460 @joins << build_join_association(reflection, parent)
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1461 when Array
1462 associations.each do |association|
1463 build(association, parent)
1464 end
1465 when Hash
1466 associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1467 build(name, parent)
1468 build(associations[name])
1469 end
1470 else
1471 raise ConfigurationError, associations.inspect
1472 end
1473 end
1474
37adea6f » dhh 2007-11-07 Address shortcomings of cha... 1475 # overridden in InnerJoinDependency subclass
1476 def build_join_association(reflection, parent)
1477 JoinAssociation.new(reflection, self, parent)
1478 end
1479
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1480 def construct(parent, associations, joins, row)
1481 case associations
1482 when Symbol, String
1483 while (join = joins.shift).reflection.name.to_s != associations.to_s
1484 raise ConfigurationError, "Not Enough Associations" if joins.empty?
1485 end
1486 construct_association(parent, join, row)
1487 when Array
1488 associations.each do |association|
1489 construct(parent, association, joins, row)
1490 end
1491 when Hash
1492 associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1493 association = construct_association(parent, joins.shift, row)
1494 construct(association, associations[name], joins, row) if association
1495 end
1496 else
1497 raise ConfigurationError, associations.inspect
1498 end
1499 end
1500
1501 def construct_association(record, join, row)
1502 case join.reflection.macro
1503 when :has_many, :has_and_belongs_to_many
1504 collection = record.send(join.reflection.name)
1505 collection.loaded
18057d2f » jeremy 2006-08-17 Cache nil results for :incl... 1506
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1507 return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
1508 association = join.instantiate(row)
204c2755 » jeremy 2007-10-28 Associations: speedup dupli... 1509 collection.target.push(association)
18057d2f » jeremy 2006-08-17 Cache nil results for :incl... 1510 when :has_one
1511 return if record.id.to_s != join.parent.record_id(row).to_s
1512 association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
1513 record.send("set_#{join.reflection.name}_target", association)
1514 when :belongs_to
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1515 return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
1516 association = join.instantiate(row)
1517 record.send("set_#{join.reflection.name}_target", association)
1518 else
1519 raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
1520 end
1521 return association
1522 end
1523
f384622a » dhh 2006-07-04 Doc fixes 1524 class JoinBase # :nodoc:
ea25e246 » technoweenie 2006-03-19 Quit ignoring default :incl... 1525 attr_reader :active_record, :table_joins
9c9069a6 » technoweenie 2006-03-18 Fixed has_many :through to ... 1526 delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1527
ea25e246 » technoweenie 2006-03-19 Quit ignoring default :incl... 1528 def initialize(active_record, joins = nil)
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1529 @active_record = active_record
1530 @cached_record = {}
ea25e246 » technoweenie 2006-03-19 Quit ignoring default :incl... 1531 @table_joins = joins
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1532 end
1533
1534 def aliased_prefix
1535 "t0"
1536 end
1537
1538 def aliased_primary_key
1539 "#{ aliased_prefix }_r0"
1540 end
1541
1542 def aliased_table_name
1543 active_record.table_name
1544 end
1545
1546 def column_names_with_alias
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1547 unless defined?(@column_names_with_alias)
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1548 @column_names_with_alias = []
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1549
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1550 ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
1551 @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
1552 end
1553 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1554
1555 @column_names_with_alias
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1556 end
1557
1558 def extract_record(row)
1559 column_names_with_alias.inject({}){|record, (cn, an)| record[cn] = row[an]; record}
1560 end
1561
1562 def record_id(row)
1563 row[aliased_primary_key]
1564 end
1565
1566 def instantiate(row)
3d5c9471 » jeremy 2007-03-17 Fix method visibility bug u... 1567 @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1568 end
1569 end
1570
f384622a » dhh 2006-07-04 Doc fixes 1571 class JoinAssociation < JoinBase # :nodoc:
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1572 attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name
9412c053 » technoweenie 2006-03-18 Eager Loading support added... 1573 delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1574
1575 def initialize(reflection, join_dependency, parent = nil)
57af961a » technoweenie 2006-03-18 Raise error when trying to ... 1576 reflection.check_validity!
1577 if reflection.options[:polymorphic]
1578 raise EagerLoadPolymorphicError.new(reflection)
1579 end
1580
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1581 super(reflection.klass)
1582 @parent = parent
1583 @reflection = reflection
1584 @aliased_prefix = "t#{ join_dependency.joins.size }"
d22f9c94 » Marcel Molina 2006-05-21 Fix Oracle boolean support ... 1585 @aliased_table_name = table_name #.tr('.', '_') # start with the table name, sub out any .'s
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 1586 @parent_table_name = parent.active_record.table_name
a9fb1544 » technoweenie 2006-04-05 Added support for eagerly i... 1587
ed10f873 » technoweenie 2006-03-22 STI associations are now al... 1588 if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{aliased_table_name.downcase}\son}
ea25e246 » technoweenie 2006-03-19 Quit ignoring default :incl... 1589 join_dependency.table_aliases[aliased_table_name] += 1
1590 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1591
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1592 unless join_dependency.table_aliases[aliased_table_name].zero?
1593 # if the table name has been used, then use an alias
7aaf4867 » jeremy 2006-08-16 Included associations: go d... 1594 @aliased_table_name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}"
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1595 table_index = join_dependency.table_aliases[aliased_table_name]
7aaf4867 » jeremy 2006-08-16 Included associations: go d... 1596 join_dependency.table_aliases[aliased_table_name] += 1
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1597 @aliased_table_name = @aliased_table_name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
d7f780e0 » jeremy 2006-08-14 Fix for deep includes on th... 1598 else
1599 join_dependency.table_aliases[aliased_table_name] += 1
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1600 end
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1601
02d34440 » technoweenie 2006-03-15 Alias the has_and_belongs_t... 1602 if reflection.macro == :has_and_belongs_to_many || (reflection.macro == :has_many && reflection.options[:through])
38bae0a9 » technoweenie 2006-03-24 Change has_many :through to... 1603 @aliased_join_table_name = reflection.macro == :has_and_belongs_to_many ? reflection.options[:join_table] : reflection.through_reflection.klass.table_name
02d34440 » technoweenie 2006-03-15 Alias the has_and_belongs_t... 1604 unless join_dependency.table_aliases[aliased_join_table_name].zero?
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1605 @aliased_join_table_name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}_join"
1606 table_index = join_dependency.table_aliases[aliased_join_table_name]
7aaf4867 » jeremy 2006-08-16 Included associations: go d... 1607 join_dependency.table_aliases[aliased_join_table_name] += 1
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1608 @aliased_join_table_name = @aliased_join_table_name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
7aaf4867 » jeremy 2006-08-16 Included associations: go d... 1609 else
1610 join_dependency.table_aliases[aliased_join_table_name] += 1
02d34440 » technoweenie 2006-03-15 Alias the has_and_belongs_t... 1611 end
1612 end
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1613 end
1614
1615 def association_join
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1616 connection = reflection.active_record.connection
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1617 join = case reflection.macro
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1618 when :has_and_belongs_to_many
bef071dd » jeremy 2007-10-29 Introduce finder :joins wit... 1619 " #{join_type} %s ON %s.%s = %s.%s " % [
229c0f43 » technoweenie 2006-03-17 Rework table aliasing to ac... 1620 table_alias_for(options[:join_table], aliased_join_table_name),
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1621 connection.quote_table_name(aliased_join_table_name),
bc97a871 » technoweenie 2007-01-22 Fix incorrect usage of #cla... 1622 options[:foreign_key] || reflection.active_record.to_s.foreign_key,
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1623 connection.quote_table_name(parent.aliased_table_name),
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1624 reflection.active_record.primary_key] +
bef071dd » jeremy 2007-10-29 Introduce finder :joins wit... 1625 " #{join_type} %s ON %s.%s = %s.%s " % [
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1626 table_name_and_alias,
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1627 connection.quote_table_name(aliased_table_name),
1628 klass.primary_key,
79823e0b » NZKoz 2007-11-10 Ensure that column names ar... 1629 connection.quote_table_name(aliased_join_table_name),
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1630 options[:association_foreign_key] || klass.to_s.foreign_key
55854c41 » dhh 2006-03-04 Added cascading eager loadi... 1631 ]
1632 when :has_many, :has_one
4f00c705 » dhh 2006-03-05 Fixed eager loading problem... 1633 case
1634 when reflection.macro == :has_many && reflection.options[:through]
9c9069a6 » technoweenie 2006-03-18 Fixed has_many :through to ... 1635 through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
8b5f4e47 » jeremy 2007-12-22 Ruby 1.9 compat: fix warnin... 1636
1637 jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
1638 first_key = second_key = as_extra = nil
1639
4f00c705 » dhh 2006-03-05 Fixed eager loading problem...