duck_record 0.0.16 → 0.0.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,22 +21,17 @@ module DuckRecord
21
21
 
22
22
  # These classes will be loaded when associations are created.
23
23
  # So there is no need to eager load them.
24
- autoload :Association
25
- autoload :SingularAssociation
26
- autoload :CollectionAssociation
27
- autoload :ForeignAssociation
28
- autoload :CollectionProxy
24
+ autoload :EmbedsAssociation
25
+ autoload :EmbedsManyProxy
29
26
 
30
- autoload :HasManyAssociation
31
- autoload :HasOneAssociation
27
+ autoload :EmbedsManyAssociation
28
+ autoload :EmbedsOneAssociation
32
29
 
33
30
  module Builder #:nodoc:
34
- autoload :Association, "duck_record/associations/builder/association"
35
- autoload :SingularAssociation, "duck_record/associations/builder/singular_association"
36
- autoload :CollectionAssociation, "duck_record/associations/builder/collection_association"
31
+ autoload :EmbedsAssociation, "duck_record/associations/builder/embeds_association"
37
32
 
38
- autoload :HasOne, "duck_record/associations/builder/has_one"
39
- autoload :HasMany, "duck_record/associations/builder/has_many"
33
+ autoload :EmbedsOne, "duck_record/associations/builder/embeds_one"
34
+ autoload :EmbedsMany, "duck_record/associations/builder/embeds_many"
40
35
  end
41
36
 
42
37
  def self.eager_load!
@@ -89,882 +84,6 @@ module DuckRecord
89
84
  @association_cache[name] = association
90
85
  end
91
86
 
92
- # \Associations are a set of macro-like class methods for tying objects together through
93
- # foreign keys. They express relationships like "Project has one Project Manager"
94
- # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
95
- # class which are specialized according to the collection or association symbol and the
96
- # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
97
- # methods.
98
- #
99
- # class Project < ActiveRecord::Base
100
- # belongs_to :portfolio
101
- # has_one :project_manager
102
- # has_many :milestones
103
- # has_and_belongs_to_many :categories
104
- # end
105
- #
106
- # The project class now has the following methods (and more) to ease the traversal and
107
- # manipulation of its relationships:
108
- # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
109
- # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
110
- # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
111
- # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
112
- # <tt>Project#milestones.build, Project#milestones.create</tt>
113
- # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
114
- # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
115
- #
116
- # === A word of warning
117
- #
118
- # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of
119
- # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
120
- # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> and will break things.
121
- # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of <tt>ActiveRecord::Base</tt> instance methods.
122
- #
123
- # == Auto-generated methods
124
- # See also Instance Public methods below for more details.
125
- #
126
- # === Singular associations (one-to-one)
127
- # | | belongs_to |
128
- # generated methods | belongs_to | :polymorphic | has_one
129
- # ----------------------------------+------------+--------------+---------
130
- # other(force_reload=false) | X | X | X
131
- # other=(other) | X | X | X
132
- # build_other(attributes={}) | X | | X
133
- # create_other(attributes={}) | X | | X
134
- # create_other!(attributes={}) | X | | X
135
- #
136
- # === Collection associations (one-to-many / many-to-many)
137
- # | | | has_many
138
- # generated methods | habtm | has_many | :through
139
- # ----------------------------------+-------+----------+----------
140
- # others(force_reload=false) | X | X | X
141
- # others=(other,other,...) | X | X | X
142
- # other_ids | X | X | X
143
- # other_ids=(id,id,...) | X | X | X
144
- # others<< | X | X | X
145
- # others.push | X | X | X
146
- # others.concat | X | X | X
147
- # others.build(attributes={}) | X | X | X
148
- # others.create(attributes={}) | X | X | X
149
- # others.create!(attributes={}) | X | X | X
150
- # others.size | X | X | X
151
- # others.length | X | X | X
152
- # others.count | X | X | X
153
- # others.sum(*args) | X | X | X
154
- # others.empty? | X | X | X
155
- # others.clear | X | X | X
156
- # others.delete(other,other,...) | X | X | X
157
- # others.delete_all | X | X | X
158
- # others.destroy(other,other,...) | X | X | X
159
- # others.destroy_all | X | X | X
160
- # others.find(*args) | X | X | X
161
- # others.exists? | X | X | X
162
- # others.distinct | X | X | X
163
- # others.reset | X | X | X
164
- #
165
- # === Overriding generated methods
166
- #
167
- # Association methods are generated in a module included into the model
168
- # class, making overrides easy. The original generated method can thus be
169
- # called with +super+:
170
- #
171
- # class Car < ActiveRecord::Base
172
- # belongs_to :owner
173
- # belongs_to :old_owner
174
- #
175
- # def owner=(new_owner)
176
- # self.old_owner = self.owner
177
- # super
178
- # end
179
- # end
180
- #
181
- # The association methods module is included immediately after the
182
- # generated attributes methods module, meaning an association will
183
- # override the methods for an attribute with the same name.
184
- #
185
- # == Cardinality and associations
186
- #
187
- # Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
188
- # relationships between models. Each model uses an association to describe its role in
189
- # the relation. The #belongs_to association is always used in the model that has
190
- # the foreign key.
191
- #
192
- # === One-to-one
193
- #
194
- # Use #has_one in the base, and #belongs_to in the associated model.
195
- #
196
- # class Employee < ActiveRecord::Base
197
- # has_one :office
198
- # end
199
- # class Office < ActiveRecord::Base
200
- # belongs_to :employee # foreign key - employee_id
201
- # end
202
- #
203
- # === One-to-many
204
- #
205
- # Use #has_many in the base, and #belongs_to in the associated model.
206
- #
207
- # class Manager < ActiveRecord::Base
208
- # has_many :employees
209
- # end
210
- # class Employee < ActiveRecord::Base
211
- # belongs_to :manager # foreign key - manager_id
212
- # end
213
- #
214
- # === Many-to-many
215
- #
216
- # There are two ways to build a many-to-many relationship.
217
- #
218
- # The first way uses a #has_many association with the <tt>:through</tt> option and a join model, so
219
- # there are two stages of associations.
220
- #
221
- # class Assignment < ActiveRecord::Base
222
- # belongs_to :programmer # foreign key - programmer_id
223
- # belongs_to :project # foreign key - project_id
224
- # end
225
- # class Programmer < ActiveRecord::Base
226
- # has_many :assignments
227
- # has_many :projects, through: :assignments
228
- # end
229
- # class Project < ActiveRecord::Base
230
- # has_many :assignments
231
- # has_many :programmers, through: :assignments
232
- # end
233
- #
234
- # For the second way, use #has_and_belongs_to_many in both models. This requires a join table
235
- # that has no corresponding model or primary key.
236
- #
237
- # class Programmer < ActiveRecord::Base
238
- # has_and_belongs_to_many :projects # foreign keys in the join table
239
- # end
240
- # class Project < ActiveRecord::Base
241
- # has_and_belongs_to_many :programmers # foreign keys in the join table
242
- # end
243
- #
244
- # Choosing which way to build a many-to-many relationship is not always simple.
245
- # If you need to work with the relationship model as its own entity,
246
- # use #has_many <tt>:through</tt>. Use #has_and_belongs_to_many when working with legacy schemas or when
247
- # you never work directly with the relationship itself.
248
- #
249
- # == Is it a #belongs_to or #has_one association?
250
- #
251
- # Both express a 1-1 relationship. The difference is mostly where to place the foreign
252
- # key, which goes on the table for the class declaring the #belongs_to relationship.
253
- #
254
- # class User < ActiveRecord::Base
255
- # # I reference an account.
256
- # belongs_to :account
257
- # end
258
- #
259
- # class Account < ActiveRecord::Base
260
- # # One user references me.
261
- # has_one :user
262
- # end
263
- #
264
- # The tables for these classes could look something like:
265
- #
266
- # CREATE TABLE users (
267
- # id int NOT NULL auto_increment,
268
- # account_id int default NULL,
269
- # name varchar default NULL,
270
- # PRIMARY KEY (id)
271
- # )
272
- #
273
- # CREATE TABLE accounts (
274
- # id int NOT NULL auto_increment,
275
- # name varchar default NULL,
276
- # PRIMARY KEY (id)
277
- # )
278
- #
279
- # == Unsaved objects and associations
280
- #
281
- # You can manipulate objects and associations before they are saved to the database, but
282
- # there is some special behavior you should be aware of, mostly involving the saving of
283
- # associated objects.
284
- #
285
- # You can set the <tt>:autosave</tt> option on a #has_one, #belongs_to,
286
- # #has_many, or #has_and_belongs_to_many association. Setting it
287
- # to +true+ will _always_ save the members, whereas setting it to +false+ will
288
- # _never_ save the members. More details about <tt>:autosave</tt> option is available at
289
- # AutosaveAssociation.
290
- #
291
- # === One-to-one associations
292
- #
293
- # * Assigning an object to a #has_one association automatically saves that object and
294
- # the object being replaced (if there is one), in order to update their foreign
295
- # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
296
- # * If either of these saves fail (due to one of the objects being invalid), an
297
- # ActiveRecord::RecordNotSaved exception is raised and the assignment is
298
- # cancelled.
299
- # * If you wish to assign an object to a #has_one association without saving it,
300
- # use the <tt>#build_association</tt> method (documented below). The object being
301
- # replaced will still be saved to update its foreign key.
302
- # * Assigning an object to a #belongs_to association does not save the object, since
303
- # the foreign key field belongs on the parent. It does not save the parent either.
304
- #
305
- # === Collections
306
- #
307
- # * Adding an object to a collection (#has_many or #has_and_belongs_to_many) automatically
308
- # saves that object, except if the parent object (the owner of the collection) is not yet
309
- # stored in the database.
310
- # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
311
- # fails, then <tt>push</tt> returns +false+.
312
- # * If saving fails while replacing the collection (via <tt>association=</tt>), an
313
- # ActiveRecord::RecordNotSaved exception is raised and the assignment is
314
- # cancelled.
315
- # * You can add an object to a collection without automatically saving it by using the
316
- # <tt>collection.build</tt> method (documented below).
317
- # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
318
- # saved when the parent is saved.
319
- #
320
- # == Customizing the query
321
- #
322
- # \Associations are built from <tt>Relation</tt> objects, and you can use the Relation syntax
323
- # to customize them. For example, to add a condition:
324
- #
325
- # class Blog < ActiveRecord::Base
326
- # has_many :published_posts, -> { where(published: true) }, class_name: 'Post'
327
- # end
328
- #
329
- # Inside the <tt>-> { ... }</tt> block you can use all of the usual Relation methods.
330
- #
331
- # === Accessing the owner object
332
- #
333
- # Sometimes it is useful to have access to the owner object when building the query. The owner
334
- # is passed as a parameter to the block. For example, the following association would find all
335
- # events that occur on the user's birthday:
336
- #
337
- # class User < ActiveRecord::Base
338
- # has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event'
339
- # end
340
- #
341
- # Note: Joining, eager loading and preloading of these associations is not fully possible.
342
- # These operations happen before instance creation and the scope will be called with a +nil+ argument.
343
- # This can lead to unexpected behavior and is deprecated.
344
- #
345
- # == Association callbacks
346
- #
347
- # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
348
- # you can also define callbacks that get triggered when you add an object to or remove an
349
- # object from an association collection.
350
- #
351
- # class Project
352
- # has_and_belongs_to_many :developers, after_add: :evaluate_velocity
353
- #
354
- # def evaluate_velocity(developer)
355
- # ...
356
- # end
357
- # end
358
- #
359
- # It's possible to stack callbacks by passing them as an array. Example:
360
- #
361
- # class Project
362
- # has_and_belongs_to_many :developers,
363
- # after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
364
- # end
365
- #
366
- # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
367
- #
368
- # If any of the +before_add+ callbacks throw an exception, the object will not be
369
- # added to the collection.
370
- #
371
- # Similarly, if any of the +before_remove+ callbacks throw an exception, the object
372
- # will not be removed from the collection.
373
- #
374
- # == Association extensions
375
- #
376
- # The proxy objects that control the access to associations can be extended through anonymous
377
- # modules. This is especially beneficial for adding new finders, creators, and other
378
- # factory-type methods that are only used as part of this association.
379
- #
380
- # class Account < ActiveRecord::Base
381
- # has_many :people do
382
- # def find_or_create_by_name(name)
383
- # first_name, last_name = name.split(" ", 2)
384
- # find_or_create_by(first_name: first_name, last_name: last_name)
385
- # end
386
- # end
387
- # end
388
- #
389
- # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
390
- # person.first_name # => "David"
391
- # person.last_name # => "Heinemeier Hansson"
392
- #
393
- # If you need to share the same extensions between many associations, you can use a named
394
- # extension module.
395
- #
396
- # module FindOrCreateByNameExtension
397
- # def find_or_create_by_name(name)
398
- # first_name, last_name = name.split(" ", 2)
399
- # find_or_create_by(first_name: first_name, last_name: last_name)
400
- # end
401
- # end
402
- #
403
- # class Account < ActiveRecord::Base
404
- # has_many :people, -> { extending FindOrCreateByNameExtension }
405
- # end
406
- #
407
- # class Company < ActiveRecord::Base
408
- # has_many :people, -> { extending FindOrCreateByNameExtension }
409
- # end
410
- #
411
- # Some extensions can only be made to work with knowledge of the association's internals.
412
- # Extensions can access relevant state using the following methods (where +items+ is the
413
- # name of the association):
414
- #
415
- # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
416
- # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
417
- # * <tt>record.association(:items).target</tt> - Returns the associated object for #belongs_to and #has_one, or
418
- # the collection of associated objects for #has_many and #has_and_belongs_to_many.
419
- #
420
- # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
421
- # above. In this case, you can access <tt>proxy_association</tt>. For example,
422
- # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
423
- # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
424
- # association extensions.
425
- #
426
- # == Association Join Models
427
- #
428
- # Has Many associations can be configured with the <tt>:through</tt> option to use an
429
- # explicit join model to retrieve the data. This operates similarly to a
430
- # #has_and_belongs_to_many association. The advantage is that you're able to add validations,
431
- # callbacks, and extra attributes on the join model. Consider the following schema:
432
- #
433
- # class Author < ActiveRecord::Base
434
- # has_many :authorships
435
- # has_many :books, through: :authorships
436
- # end
437
- #
438
- # class Authorship < ActiveRecord::Base
439
- # belongs_to :author
440
- # belongs_to :book
441
- # end
442
- #
443
- # @author = Author.first
444
- # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
445
- # @author.books # selects all books by using the Authorship join model
446
- #
447
- # You can also go through a #has_many association on the join model:
448
- #
449
- # class Firm < ActiveRecord::Base
450
- # has_many :clients
451
- # has_many :invoices, through: :clients
452
- # end
453
- #
454
- # class Client < ActiveRecord::Base
455
- # belongs_to :firm
456
- # has_many :invoices
457
- # end
458
- #
459
- # class Invoice < ActiveRecord::Base
460
- # belongs_to :client
461
- # end
462
- #
463
- # @firm = Firm.first
464
- # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
465
- # @firm.invoices # selects all invoices by going through the Client join model
466
- #
467
- # Similarly you can go through a #has_one association on the join model:
468
- #
469
- # class Group < ActiveRecord::Base
470
- # has_many :users
471
- # has_many :avatars, through: :users
472
- # end
473
- #
474
- # class User < ActiveRecord::Base
475
- # belongs_to :group
476
- # has_one :avatar
477
- # end
478
- #
479
- # class Avatar < ActiveRecord::Base
480
- # belongs_to :user
481
- # end
482
- #
483
- # @group = Group.first
484
- # @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
485
- # @group.avatars # selects all avatars by going through the User join model.
486
- #
487
- # An important caveat with going through #has_one or #has_many associations on the
488
- # join model is that these associations are *read-only*. For example, the following
489
- # would not work following the previous example:
490
- #
491
- # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
492
- # @group.avatars.delete(@group.avatars.last) # so would this
493
- #
494
- # == Setting Inverses
495
- #
496
- # If you are using a #belongs_to on the join model, it is a good idea to set the
497
- # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
498
- # works correctly (where <tt>tags</tt> is a #has_many <tt>:through</tt> association):
499
- #
500
- # @post = Post.first
501
- # @tag = @post.tags.build name: "ruby"
502
- # @tag.save
503
- #
504
- # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the
505
- # <tt>:inverse_of</tt> is set:
506
- #
507
- # class Tagging < ActiveRecord::Base
508
- # belongs_to :post
509
- # belongs_to :tag, inverse_of: :taggings
510
- # end
511
- #
512
- # If you do not set the <tt>:inverse_of</tt> record, the association will
513
- # do its best to match itself up with the correct inverse. Automatic
514
- # inverse detection only works on #has_many, #has_one, and
515
- # #belongs_to associations.
516
- #
517
- # Extra options on the associations, as defined in the
518
- # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
519
- # also prevent the association's inverse from being found automatically.
520
- #
521
- # The automatic guessing of the inverse association uses a heuristic based
522
- # on the name of the class, so it may not work for all associations,
523
- # especially the ones with non-standard names.
524
- #
525
- # You can turn off the automatic detection of inverse associations by setting
526
- # the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
527
- #
528
- # class Tagging < ActiveRecord::Base
529
- # belongs_to :tag, inverse_of: false
530
- # end
531
- #
532
- # == Nested \Associations
533
- #
534
- # You can actually specify *any* association with the <tt>:through</tt> option, including an
535
- # association which has a <tt>:through</tt> option itself. For example:
536
- #
537
- # class Author < ActiveRecord::Base
538
- # has_many :posts
539
- # has_many :comments, through: :posts
540
- # has_many :commenters, through: :comments
541
- # end
542
- #
543
- # class Post < ActiveRecord::Base
544
- # has_many :comments
545
- # end
546
- #
547
- # class Comment < ActiveRecord::Base
548
- # belongs_to :commenter
549
- # end
550
- #
551
- # @author = Author.first
552
- # @author.commenters # => People who commented on posts written by the author
553
- #
554
- # An equivalent way of setting up this association this would be:
555
- #
556
- # class Author < ActiveRecord::Base
557
- # has_many :posts
558
- # has_many :commenters, through: :posts
559
- # end
560
- #
561
- # class Post < ActiveRecord::Base
562
- # has_many :comments
563
- # has_many :commenters, through: :comments
564
- # end
565
- #
566
- # class Comment < ActiveRecord::Base
567
- # belongs_to :commenter
568
- # end
569
- #
570
- # When using a nested association, you will not be able to modify the association because there
571
- # is not enough information to know what modification to make. For example, if you tried to
572
- # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
573
- # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
574
- #
575
- # == Polymorphic \Associations
576
- #
577
- # Polymorphic associations on models are not restricted on what types of models they
578
- # can be associated with. Rather, they specify an interface that a #has_many association
579
- # must adhere to.
580
- #
581
- # class Asset < ActiveRecord::Base
582
- # belongs_to :attachable, polymorphic: true
583
- # end
584
- #
585
- # class Post < ActiveRecord::Base
586
- # has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use.
587
- # end
588
- #
589
- # @asset.attachable = @post
590
- #
591
- # This works by using a type column in addition to a foreign key to specify the associated
592
- # record. In the Asset example, you'd need an +attachable_id+ integer column and an
593
- # +attachable_type+ string column.
594
- #
595
- # Using polymorphic associations in combination with single table inheritance (STI) is
596
- # a little tricky. In order for the associations to work as expected, ensure that you
597
- # store the base model for the STI models in the type column of the polymorphic
598
- # association. To continue with the asset example above, suppose there are guest posts
599
- # and member posts that use the posts table for STI. In this case, there must be a +type+
600
- # column in the posts table.
601
- #
602
- # Note: The <tt>attachable_type=</tt> method is being called when assigning an +attachable+.
603
- # The +class_name+ of the +attachable+ is passed as a String.
604
- #
605
- # class Asset < ActiveRecord::Base
606
- # belongs_to :attachable, polymorphic: true
607
- #
608
- # def attachable_type=(class_name)
609
- # super(class_name.constantize.base_class.to_s)
610
- # end
611
- # end
612
- #
613
- # class Post < ActiveRecord::Base
614
- # # because we store "Post" in attachable_type now dependent: :destroy will work
615
- # has_many :assets, as: :attachable, dependent: :destroy
616
- # end
617
- #
618
- # class GuestPost < Post
619
- # end
620
- #
621
- # class MemberPost < Post
622
- # end
623
- #
624
- # == Caching
625
- #
626
- # All of the methods are built on a simple caching principle that will keep the result
627
- # of the last query around unless specifically instructed not to. The cache is even
628
- # shared across methods to make it even cheaper to use the macro-added methods without
629
- # worrying too much about performance at the first go.
630
- #
631
- # project.milestones # fetches milestones from the database
632
- # project.milestones.size # uses the milestone cache
633
- # project.milestones.empty? # uses the milestone cache
634
- # project.milestones(true).size # fetches milestones from the database
635
- # project.milestones # uses the milestone cache
636
- #
637
- # == Eager loading of associations
638
- #
639
- # Eager loading is a way to find objects of a certain class and a number of named associations.
640
- # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100
641
- # posts that each need to display their author triggers 101 database queries. Through the
642
- # use of eager loading, the number of queries will be reduced from 101 to 2.
643
- #
644
- # class Post < ActiveRecord::Base
645
- # belongs_to :author
646
- # has_many :comments
647
- # end
648
- #
649
- # Consider the following loop using the class above:
650
- #
651
- # Post.all.each do |post|
652
- # puts "Post: " + post.title
653
- # puts "Written by: " + post.author.name
654
- # puts "Last comment on: " + post.comments.first.created_on
655
- # end
656
- #
657
- # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
658
- # first just optimize it for retrieving the author:
659
- #
660
- # Post.includes(:author).each do |post|
661
- #
662
- # This references the name of the #belongs_to association that also used the <tt>:author</tt>
663
- # symbol. After loading the posts, +find+ will collect the +author_id+ from each one and load
664
- # all of the referenced authors with one query. Doing so will cut down the number of queries
665
- # from 201 to 102.
666
- #
667
- # We can improve upon the situation further by referencing both associations in the finder with:
668
- #
669
- # Post.includes(:author, :comments).each do |post|
670
- #
671
- # This will load all comments with a single query. This reduces the total number of queries
672
- # to 3. In general, the number of queries will be 1 plus the number of associations
673
- # named (except if some of the associations are polymorphic #belongs_to - see below).
674
- #
675
- # To include a deep hierarchy of associations, use a hash:
676
- #
677
- # Post.includes(:author, { comments: { author: :gravatar } }).each do |post|
678
- #
679
- # The above code will load all the comments and all of their associated
680
- # authors and gravatars. You can mix and match any combination of symbols,
681
- # arrays, and hashes to retrieve the associations you want to load.
682
- #
683
- # All of this power shouldn't fool you into thinking that you can pull out huge amounts
684
- # of data with no performance penalty just because you've reduced the number of queries.
685
- # The database still needs to send all the data to Active Record and it still needs to
686
- # be processed. So it's no catch-all for performance problems, but it's a great way to
687
- # cut down on the number of queries in a situation as the one described above.
688
- #
689
- # Since only one table is loaded at a time, conditions or orders cannot reference tables
690
- # other than the main one. If this is the case, Active Record falls back to the previously
691
- # used <tt>LEFT OUTER JOIN</tt> based strategy. For example:
692
- #
693
- # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
694
- #
695
- # This will result in a single SQL query with joins along the lines of:
696
- # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
697
- # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
698
- # like this can have unintended consequences.
699
- # In the above example, posts with no approved comments are not returned at all because
700
- # the conditions apply to the SQL statement as a whole and not just to the association.
701
- #
702
- # You must disambiguate column references for this fallback to happen, for example
703
- # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
704
- #
705
- # If you want to load all posts (including posts with no approved comments), then write
706
- # your own <tt>LEFT OUTER JOIN</tt> query using <tt>ON</tt>:
707
- #
708
- # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
709
- #
710
- # In this case, it is usually more natural to include an association which has conditions defined on it:
711
- #
712
- # class Post < ActiveRecord::Base
713
- # has_many :approved_comments, -> { where(approved: true) }, class_name: 'Comment'
714
- # end
715
- #
716
- # Post.includes(:approved_comments)
717
- #
718
- # This will load posts and eager load the +approved_comments+ association, which contains
719
- # only those comments that have been approved.
720
- #
721
- # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
722
- # returning all the associated objects:
723
- #
724
- # class Picture < ActiveRecord::Base
725
- # has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
726
- # end
727
- #
728
- # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
729
- #
730
- # Eager loading is supported with polymorphic associations.
731
- #
732
- # class Address < ActiveRecord::Base
733
- # belongs_to :addressable, polymorphic: true
734
- # end
735
- #
736
- # A call that tries to eager load the addressable model
737
- #
738
- # Address.includes(:addressable)
739
- #
740
- # This will execute one query to load the addresses and load the addressables with one
741
- # query per addressable type.
742
- # For example, if all the addressables are either of class Person or Company, then a total
743
- # of 3 queries will be executed. The list of addressable types to load is determined on
744
- # the back of the addresses loaded. This is not supported if Active Record has to fallback
745
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
746
- # The reason is that the parent model's type is a column value so its corresponding table
747
- # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
748
- #
749
- # == Table Aliasing
750
- #
751
- # Active Record uses table aliasing in the case that a table is referenced multiple times
752
- # in a join. If a table is referenced only once, the standard table name is used. The
753
- # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
754
- # Indexes are appended for any more successive uses of the table name.
755
- #
756
- # Post.joins(:comments)
757
- # # => SELECT ... FROM posts INNER JOIN comments ON ...
758
- # Post.joins(:special_comments) # STI
759
- # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
760
- # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
761
- # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
762
- #
763
- # Acts as tree example:
764
- #
765
- # TreeMixin.joins(:children)
766
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
767
- # TreeMixin.joins(children: :parent)
768
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
769
- # INNER JOIN parents_mixins ...
770
- # TreeMixin.joins(children: {parent: :children})
771
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
772
- # INNER JOIN parents_mixins ...
773
- # INNER JOIN mixins childrens_mixins_2
774
- #
775
- # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
776
- #
777
- # Post.joins(:categories)
778
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
779
- # Post.joins(categories: :posts)
780
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
781
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
782
- # Post.joins(categories: {posts: :categories})
783
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
784
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
785
- # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
786
- #
787
- # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table
788
- # names will take precedence over the eager associations:
789
- #
790
- # Post.joins(:comments).joins("inner join comments ...")
791
- # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
792
- # Post.joins(:comments, :special_comments).joins("inner join comments ...")
793
- # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
794
- # INNER JOIN comments special_comments_posts ...
795
- # INNER JOIN comments ...
796
- #
797
- # Table aliases are automatically truncated according to the maximum length of table identifiers
798
- # according to the specific database.
799
- #
800
- # == Modules
801
- #
802
- # By default, associations will look for objects within the current module scope. Consider:
803
- #
804
- # module MyApplication
805
- # module Business
806
- # class Firm < ActiveRecord::Base
807
- # has_many :clients
808
- # end
809
- #
810
- # class Client < ActiveRecord::Base; end
811
- # end
812
- # end
813
- #
814
- # When <tt>Firm#clients</tt> is called, it will in turn call
815
- # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
816
- # If you want to associate with a class in another module scope, this can be done by
817
- # specifying the complete class name.
818
- #
819
- # module MyApplication
820
- # module Business
821
- # class Firm < ActiveRecord::Base; end
822
- # end
823
- #
824
- # module Billing
825
- # class Account < ActiveRecord::Base
826
- # belongs_to :firm, class_name: "MyApplication::Business::Firm"
827
- # end
828
- # end
829
- # end
830
- #
831
- # == Bi-directional associations
832
- #
833
- # When you specify an association, there is usually an association on the associated model
834
- # that specifies the same relationship in reverse. For example, with the following models:
835
- #
836
- # class Dungeon < ActiveRecord::Base
837
- # has_many :traps
838
- # has_one :evil_wizard
839
- # end
840
- #
841
- # class Trap < ActiveRecord::Base
842
- # belongs_to :dungeon
843
- # end
844
- #
845
- # class EvilWizard < ActiveRecord::Base
846
- # belongs_to :dungeon
847
- # end
848
- #
849
- # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
850
- # the inverse of each other, and the inverse of the +dungeon+ association on +EvilWizard+
851
- # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
852
- # Active Record can guess the inverse of the association based on the name
853
- # of the class. The result is the following:
854
- #
855
- # d = Dungeon.first
856
- # t = d.traps.first
857
- # d.object_id == t.dungeon.object_id # => true
858
- #
859
- # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
860
- # the same in-memory instance since the association matches the name of the class.
861
- # The result would be the same if we added +:inverse_of+ to our model definitions:
862
- #
863
- # class Dungeon < ActiveRecord::Base
864
- # has_many :traps, inverse_of: :dungeon
865
- # has_one :evil_wizard, inverse_of: :dungeon
866
- # end
867
- #
868
- # class Trap < ActiveRecord::Base
869
- # belongs_to :dungeon, inverse_of: :traps
870
- # end
871
- #
872
- # class EvilWizard < ActiveRecord::Base
873
- # belongs_to :dungeon, inverse_of: :evil_wizard
874
- # end
875
- #
876
- # There are limitations to <tt>:inverse_of</tt> support:
877
- #
878
- # * does not work with <tt>:through</tt> associations.
879
- # * does not work with <tt>:polymorphic</tt> associations.
880
- # * inverse associations for #belongs_to associations #has_many are ignored.
881
- #
882
- # For more information, see the documentation for the +:inverse_of+ option.
883
- #
884
- # == Deleting from associations
885
- #
886
- # === Dependent associations
887
- #
888
- # #has_many, #has_one, and #belongs_to associations support the <tt>:dependent</tt> option.
889
- # This allows you to specify that associated records should be deleted when the owner is
890
- # deleted.
891
- #
892
- # For example:
893
- #
894
- # class Author
895
- # has_many :posts, dependent: :destroy
896
- # end
897
- # Author.find(1).destroy # => Will destroy all of the author's posts, too
898
- #
899
- # The <tt>:dependent</tt> option can have different values which specify how the deletion
900
- # is done. For more information, see the documentation for this option on the different
901
- # specific association types. When no option is given, the behavior is to do nothing
902
- # with the associated records when destroying a record.
903
- #
904
- # Note that <tt>:dependent</tt> is implemented using Rails' callback
905
- # system, which works by processing callbacks in order. Therefore, other
906
- # callbacks declared either before or after the <tt>:dependent</tt> option
907
- # can affect what it does.
908
- #
909
- # Note that <tt>:dependent</tt> option is ignored for #has_one <tt>:through</tt> associations.
910
- #
911
- # === Delete or destroy?
912
- #
913
- # #has_many and #has_and_belongs_to_many associations have the methods <tt>destroy</tt>,
914
- # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
915
- #
916
- # For #has_and_belongs_to_many, <tt>delete</tt> and <tt>destroy</tt> are the same: they
917
- # cause the records in the join table to be removed.
918
- #
919
- # For #has_many, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
920
- # record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
921
- # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
922
- # if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
923
- # The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for
924
- # #has_many <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
925
- # the join records, without running their callbacks).
926
- #
927
- # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
928
- # it returns the association rather than the records which have been deleted.
929
- #
930
- # === What gets deleted?
931
- #
932
- # There is a potential pitfall here: #has_and_belongs_to_many and #has_many <tt>:through</tt>
933
- # associations have records in join tables, as well as the associated records. So when we
934
- # call one of these deletion methods, what exactly should be deleted?
935
- #
936
- # The answer is that it is assumed that deletion on an association is about removing the
937
- # <i>link</i> between the owner and the associated object(s), rather than necessarily the
938
- # associated objects themselves. So with #has_and_belongs_to_many and #has_many
939
- # <tt>:through</tt>, the join records will be deleted, but the associated records won't.
940
- #
941
- # This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt>
942
- # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
943
- # to be removed from the database.
944
- #
945
- # However, there are examples where this strategy doesn't make sense. For example, suppose
946
- # a person has many projects, and each project has many tasks. If we deleted one of a person's
947
- # tasks, we would probably not want the project to be deleted. In this scenario, the delete method
948
- # won't actually work: it can only be used if the association on the join model is a
949
- # #belongs_to. In other situations you are expected to perform operations directly on
950
- # either the associated records or the <tt>:through</tt> association.
951
- #
952
- # With a regular #has_many there is no distinction between the "associated records"
953
- # and the "link", so there is only one choice for what gets deleted.
954
- #
955
- # With #has_and_belongs_to_many and #has_many <tt>:through</tt>, if you want to delete the
956
- # associated records themselves, you can always do something along the lines of
957
- # <tt>person.tasks.each(&:destroy)</tt>.
958
- #
959
- # == Type safety with ActiveRecord::AssociationTypeMismatch
960
- #
961
- # If you attempt to assign an object to an association that doesn't match the inferred
962
- # or specified <tt>:class_name</tt>, you'll get an ActiveRecord::AssociationTypeMismatch.
963
- #
964
- # == Options
965
- #
966
- # All of the association macros can be specialized through options. This makes cases
967
- # more complex than the simple and guessable ones possible.
968
87
  module ClassMethods
969
88
  # Specifies a one-to-many association. The following methods for retrieval and query of
970
89
  # collections of associated objects will be added:
@@ -1177,8 +296,8 @@ module DuckRecord
1177
296
  # has_many :tags, as: :taggable
1178
297
  # has_many :reports, -> { readonly }
1179
298
  # has_many :subscribers, through: :subscriptions, source: :user
1180
- def has_many(name, options = {}, &extension)
1181
- reflection = Builder::HasMany.build(self, name, options, &extension)
299
+ def embeds_many(name, options = {}, &extension)
300
+ reflection = Builder::EmbedsMany.build(self, name, options, &extension)
1182
301
  Reflection.add_reflection self, name, reflection
1183
302
  end
1184
303
 
@@ -1308,8 +427,8 @@ module DuckRecord
1308
427
  # has_one :club, through: :membership
1309
428
  # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
1310
429
  # has_one :credit_card, required: true
1311
- def has_one(name, options = {})
1312
- reflection = Builder::HasOne.build(self, name, options)
430
+ def embeds_one(name, options = {})
431
+ reflection = Builder::EmbedsOne.build(self, name, options)
1313
432
  Reflection.add_reflection self, name, reflection
1314
433
  end
1315
434
  end