activerecord 2.1.2 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (110) hide show
  1. data/CHANGELOG +32 -6
  2. data/README +0 -0
  3. data/Rakefile +4 -5
  4. data/lib/active_record.rb +11 -10
  5. data/lib/active_record/aggregations.rb +110 -38
  6. data/lib/active_record/association_preload.rb +104 -15
  7. data/lib/active_record/associations.rb +427 -212
  8. data/lib/active_record/associations/association_collection.rb +101 -16
  9. data/lib/active_record/associations/association_proxy.rb +65 -13
  10. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
  13. data/lib/active_record/associations/has_many_association.rb +28 -28
  14. data/lib/active_record/associations/has_many_through_association.rb +21 -19
  15. data/lib/active_record/associations/has_one_association.rb +24 -7
  16. data/lib/active_record/associations/has_one_through_association.rb +3 -4
  17. data/lib/active_record/attribute_methods.rb +13 -5
  18. data/lib/active_record/base.rb +435 -212
  19. data/lib/active_record/calculations.rb +12 -5
  20. data/lib/active_record/callbacks.rb +28 -9
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
  30. data/lib/active_record/dirty.rb +25 -7
  31. data/lib/active_record/dynamic_finder_match.rb +41 -0
  32. data/lib/active_record/fixtures.rb +10 -9
  33. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  34. data/lib/active_record/locale/en.yml +54 -0
  35. data/lib/active_record/migration.rb +47 -10
  36. data/lib/active_record/named_scope.rb +29 -16
  37. data/lib/active_record/reflection.rb +118 -54
  38. data/lib/active_record/schema_dumper.rb +13 -7
  39. data/lib/active_record/test_case.rb +18 -5
  40. data/lib/active_record/transactions.rb +89 -34
  41. data/lib/active_record/validations.rb +270 -180
  42. data/lib/active_record/version.rb +1 -1
  43. data/test/cases/active_schema_test_mysql.rb +5 -0
  44. data/test/cases/adapter_test.rb +6 -0
  45. data/test/cases/aggregations_test.rb +39 -0
  46. data/test/cases/associations/belongs_to_associations_test.rb +10 -0
  47. data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
  48. data/test/cases/associations/eager_test.rb +54 -5
  49. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
  50. data/test/cases/associations/has_many_associations_test.rb +74 -7
  51. data/test/cases/associations/has_many_through_associations_test.rb +50 -3
  52. data/test/cases/associations/has_one_associations_test.rb +17 -0
  53. data/test/cases/associations/has_one_through_associations_test.rb +49 -1
  54. data/test/cases/associations_test.rb +0 -0
  55. data/test/cases/attribute_methods_test.rb +59 -4
  56. data/test/cases/base_test.rb +93 -21
  57. data/test/cases/binary_test.rb +1 -5
  58. data/test/cases/calculations_test.rb +5 -0
  59. data/test/cases/callbacks_observers_test.rb +38 -0
  60. data/test/cases/connection_test_mysql.rb +1 -1
  61. data/test/cases/defaults_test.rb +32 -1
  62. data/test/cases/deprecated_finder_test.rb +0 -0
  63. data/test/cases/dirty_test.rb +13 -0
  64. data/test/cases/finder_test.rb +162 -12
  65. data/test/cases/fixtures_test.rb +32 -3
  66. data/test/cases/helper.rb +15 -0
  67. data/test/cases/i18n_test.rb +41 -0
  68. data/test/cases/inheritance_test.rb +2 -2
  69. data/test/cases/lifecycle_test.rb +0 -0
  70. data/test/cases/locking_test.rb +4 -9
  71. data/test/cases/method_scoping_test.rb +109 -2
  72. data/test/cases/migration_test.rb +43 -8
  73. data/test/cases/multiple_db_test.rb +25 -0
  74. data/test/cases/named_scope_test.rb +74 -0
  75. data/test/cases/pooled_connections_test.rb +103 -0
  76. data/test/cases/readonly_test.rb +0 -0
  77. data/test/cases/reflection_test.rb +11 -3
  78. data/test/cases/reload_models_test.rb +20 -0
  79. data/test/cases/sanitize_test.rb +25 -0
  80. data/test/cases/schema_authorization_test_postgresql.rb +2 -2
  81. data/test/cases/transactions_test.rb +62 -12
  82. data/test/cases/unconnected_test.rb +0 -0
  83. data/test/cases/validations_i18n_test.rb +921 -0
  84. data/test/cases/validations_test.rb +44 -33
  85. data/test/connections/native_mysql/connection.rb +1 -3
  86. data/test/fixtures/companies.yml +1 -0
  87. data/test/fixtures/customers.yml +10 -1
  88. data/test/fixtures/fixture_database.sqlite3 +0 -0
  89. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  90. data/test/fixtures/organizations.yml +5 -0
  91. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  92. data/test/models/author.rb +3 -0
  93. data/test/models/category.rb +3 -0
  94. data/test/models/club.rb +6 -0
  95. data/test/models/company.rb +25 -1
  96. data/test/models/customer.rb +19 -1
  97. data/test/models/member.rb +2 -0
  98. data/test/models/member_detail.rb +4 -0
  99. data/test/models/organization.rb +4 -0
  100. data/test/models/parrot.rb +1 -0
  101. data/test/models/post.rb +3 -0
  102. data/test/models/reply.rb +0 -0
  103. data/test/models/topic.rb +3 -0
  104. data/test/schema/schema.rb +12 -1
  105. metadata +22 -10
  106. data/lib/active_record/vendor/mysql.rb +0 -1214
  107. data/test/cases/adapter_test_sqlserver.rb +0 -95
  108. data/test/cases/table_name_test_sqlserver.rb +0 -23
  109. data/test/cases/threaded_connections_test.rb +0 -48
  110. data/test/schema/sqlserver_specific_schema.rb +0 -5
@@ -73,6 +73,7 @@ module ActiveRecord
73
73
  end
74
74
  end
75
75
 
76
+ # See ActiveRecord::Associations::ClassMethods for documentation.
76
77
  module Associations # :nodoc:
77
78
  def self.included(base)
78
79
  base.extend(ClassMethods)
@@ -150,6 +151,7 @@ module ActiveRecord
150
151
  # #others.destroy_all | X | X | X
151
152
  # #others.find(*args) | X | X | X
152
153
  # #others.find_first | X | |
154
+ # #others.exist? | X | X | X
153
155
  # #others.uniq | X | X | X
154
156
  # #others.reset | X | X | X
155
157
  #
@@ -505,6 +507,14 @@ module ActiveRecord
505
507
  #
506
508
  # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
507
509
  #
510
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
511
+ #
512
+ # class Picture < ActiveRecord::Base
513
+ # has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
514
+ # end
515
+ #
516
+ # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
517
+ #
508
518
  # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
509
519
  # before the actual model exists.
510
520
  #
@@ -582,12 +592,13 @@ module ActiveRecord
582
592
  # has_many :clients
583
593
  # end
584
594
  #
585
- # class Company < ActiveRecord::Base; end
595
+ # class Client < ActiveRecord::Base; end
586
596
  # end
587
597
  # end
588
598
  #
589
- # When Firm#clients is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
590
- # with a class in another module scope, this can be done by specifying the complete class name. Example:
599
+ # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
600
+ # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
601
+ # Example:
591
602
  #
592
603
  # module MyApplication
593
604
  # module Business
@@ -611,32 +622,55 @@ module ActiveRecord
611
622
  # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
612
623
  # possible.
613
624
  module ClassMethods
614
- # Adds the following methods for retrieval and query of collections of associated objects:
615
- # +collection+ is replaced with the symbol passed as the first argument, so
616
- # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
617
- # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
625
+ # Specifies a one-to-many association. The following methods for retrieval and query of
626
+ # collections of associated objects will be added:
627
+ #
628
+ # [collection(force_reload = false)]
629
+ # Returns an array of all the associated objects.
618
630
  # An empty array is returned if none are found.
619
- # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
620
- # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by setting their foreign keys to +NULL+.
621
- # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
622
- # * <tt>collection=objects</tt> - Replaces the collections content by deleting and adding objects as appropriate.
623
- # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids
624
- # * <tt>collection_singular_ids=ids</tt> - Replace the collection with the objects identified by the primary keys in +ids+
625
- # * <tt>collection.clear</tt> - Removes every object from the collection. This destroys the associated objects if they
626
- # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
627
- # otherwise sets their foreign keys to +NULL+.
628
- # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
629
- # * <tt>collection.size</tt> - Returns the number of associated objects.
630
- # * <tt>collection.find</tt> - Finds an associated object according to the same rules as Base.find.
631
- # * <tt>collection.build(attributes = {}, ...)</tt> - Returns one or more new objects of the collection type that have been instantiated
632
- # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
633
- # associated object already exists, not if it's +nil+!
634
- # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
635
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
636
- # *Note:* This only works if an associated object already exists, not if it's +nil+!
631
+ # [collection<<(object, ...)]
632
+ # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
633
+ # [collection.delete(object, ...)]
634
+ # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
635
+ # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
636
+ # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
637
+ # [collection=objects]
638
+ # Replaces the collections content by deleting and adding objects as appropriate.
639
+ # [collection_singular_ids]
640
+ # Returns an array of the associated objects' ids
641
+ # [collection_singular_ids=ids]
642
+ # Replace the collection with the objects identified by the primary keys in +ids+
643
+ # [collection.clear]
644
+ # Removes every object from the collection. This destroys the associated objects if they
645
+ # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
646
+ # database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
647
+ # [collection.empty?]
648
+ # Returns +true+ if there are no associated objects.
649
+ # [collection.size]
650
+ # Returns the number of associated objects.
651
+ # [collection.find(...)]
652
+ # Finds an associated object according to the same rules as ActiveRecord::Base.find.
653
+ # [collection.exist?(...)]
654
+ # Checks whether an associated object with the given conditions exists.
655
+ # Uses the same rules as ActiveRecord::Base.exists?.
656
+ # [collection.build(attributes = {}, ...)]
657
+ # Returns one or more new objects of the collection type that have been instantiated
658
+ # with +attributes+ and linked to this object through a foreign key, but have not yet
659
+ # been saved. <b>Note:</b> This only works if an associated object already exists, not if
660
+ # it's +nil+!
661
+ # [collection.create(attributes = {})]
662
+ # Returns a new object of the collection type that has been instantiated
663
+ # with +attributes+, linked to this object through a foreign key, and that has already
664
+ # been saved (if it passed the validation). <b>Note:</b> This only works if an associated
665
+ # object already exists, not if it's +nil+!
666
+ #
667
+ # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
668
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
669
+ #
670
+ # === Example
637
671
  #
638
672
  # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
639
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
673
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
640
674
  # * <tt>Firm#clients<<</tt>
641
675
  # * <tt>Firm#clients.delete</tt>
642
676
  # * <tt>Firm#clients=</tt>
@@ -646,52 +680,74 @@ module ActiveRecord
646
680
  # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
647
681
  # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
648
682
  # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
683
+ # * <tt>Firm#clients.exist?(:name => 'ACME')</tt> (similar to <tt>Client.exist?(:name => 'ACME', :firm_id => firm.id)</tt>)
649
684
  # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
650
685
  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
651
686
  # The declaration can also include an options hash to specialize the behavior of the association.
652
687
  #
653
- # Options are:
654
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
688
+ # === Supported options
689
+ # [:class_name]
690
+ # Specify the class name of the association. Use it only if that name can't be inferred
655
691
  # from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
656
692
  # if the real class name is SpecialProduct, you'll have to specify it with this option.
657
- # * <tt>:conditions</tt> - Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
693
+ # [:conditions]
694
+ # Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
658
695
  # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
659
696
  # is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
660
697
  # or <tt>@blog.posts.build</tt>.
661
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
698
+ # [:order]
699
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
662
700
  # such as <tt>last_name, first_name DESC</tt>.
663
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
701
+ # [:foreign_key]
702
+ # Specify the foreign key used for the association. By default this is guessed to be the name
664
703
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
665
704
  # as the default <tt>:foreign_key</tt>.
666
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt> all the associated objects are destroyed
705
+ # [:primary_key]
706
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
707
+ # [:dependent]
708
+ # If set to <tt>:destroy</tt> all the associated objects are destroyed
667
709
  # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
668
710
  # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
669
711
  # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
670
712
  # the <tt>:through</tt> option.
671
- # * <tt>:finder_sql</tt> - Specify a complete SQL statement to fetch the association. This is a good way to go for complex
713
+ # [:finder_sql]
714
+ # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
672
715
  # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
673
- # * <tt>:counter_sql</tt> - Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
716
+ # [:counter_sql]
717
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
674
718
  # 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>.
675
- # * <tt>:extend</tt> - Specify a named module for extending the proxy. See "Association extensions".
676
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
677
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
678
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
679
- # * <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.
680
- # * <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
681
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will rise an error.
682
- # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
683
- # * <tt>:through</tt> - Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
719
+ # [:extend]
720
+ # Specify a named module for extending the proxy. See "Association extensions".
721
+ # [:include]
722
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
723
+ # [:group]
724
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
725
+ # [:limit]
726
+ # An integer determining the limit on the number of rows that should be returned.
727
+ # [:offset]
728
+ # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
729
+ # [:select]
730
+ # 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
731
+ # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
732
+ # [:as]
733
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
734
+ # [:through]
735
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
684
736
  # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
685
737
  # or <tt>has_many</tt> association on the join model.
686
- # * <tt>:source</tt> - Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
738
+ # [:source]
739
+ # Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
687
740
  # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
688
741
  # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
689
- # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
742
+ # [:source_type]
743
+ # Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
690
744
  # association is a polymorphic +belongs_to+.
691
- # * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
692
- # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
693
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. true by default.
694
- #
745
+ # [:uniq]
746
+ # If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
747
+ # [:readonly]
748
+ # If true, all the associated objects are readonly through the association.
749
+ # [:validate]
750
+ # If false, don't validate the associated objects when saving the parent object. true by default.
695
751
  # Option examples:
696
752
  # has_many :comments, :order => "posted_on"
697
753
  # has_many :comments, :include => :author
@@ -722,56 +778,89 @@ module ActiveRecord
722
778
  end
723
779
  end
724
780
 
725
- # Adds the following methods for retrieval and query of a single associated object:
726
- # +association+ is replaced with the symbol passed as the first argument, so
727
- # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
728
- # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
729
- # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, sets it as the foreign key,
781
+ # Specifies a one-to-one association with another class. This method should only be used
782
+ # if the other class contains the foreign key. If the current class contains the foreign key,
783
+ # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
784
+ # on when to use has_one and when to use belongs_to.
785
+ #
786
+ # The following methods for retrieval and query of a single associated object will be added:
787
+ #
788
+ # [association(force_reload = false)]
789
+ # Returns the associated object. +nil+ is returned if none is found.
790
+ # [association=(associate)]
791
+ # Assigns the associate object, extracts the primary key, sets it as the foreign key,
730
792
  # and saves the associate object.
731
- # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
732
- # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
733
- # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if
734
- # an association already exists. It will NOT work if the association is +nil+.
735
- # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
736
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
793
+ # [association.nil?]
794
+ # Returns +true+ if there is no associated object.
795
+ # [build_association(attributes = {})]
796
+ # Returns a new object of the associated type that has been instantiated
797
+ # with +attributes+ and linked to this object through a foreign key, but has not
798
+ # yet been saved. <b>Note:</b> This ONLY works if an association already exists.
799
+ # It will NOT work if the association is +nil+.
800
+ # [create_association(attributes = {})]
801
+ # Returns a new object of the associated type that has been instantiated
802
+ # with +attributes+, linked to this object through a foreign key, and that
803
+ # has already been saved (if it passed the validation).
737
804
  #
738
- # Example: An Account class declares <tt>has_one :beneficiary</tt>, which will add:
805
+ # (+association+ is replaced with the symbol passed as the first argument, so
806
+ # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
807
+ #
808
+ # === Example
809
+ #
810
+ # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
739
811
  # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
740
812
  # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
741
813
  # * <tt>Account#beneficiary.nil?</tt>
742
814
  # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
743
815
  # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
744
816
  #
817
+ # === Options
818
+ #
745
819
  # The declaration can also include an options hash to specialize the behavior of the association.
746
820
  #
747
821
  # Options are:
748
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
822
+ # [:class_name]
823
+ # Specify the class name of the association. Use it only if that name can't be inferred
749
824
  # from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
750
825
  # if the real class name is Person, you'll have to specify it with this option.
751
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
826
+ # [:conditions]
827
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
752
828
  # SQL fragment, such as <tt>rank = 5</tt>.
753
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
829
+ # [:order]
830
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
754
831
  # such as <tt>last_name, first_name DESC</tt>.
755
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
832
+ # [:dependent]
833
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
756
834
  # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
757
835
  # object's foreign key is set to +NULL+. Also, association is assigned.
758
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
836
+ # [:foreign_key]
837
+ # Specify the foreign key used for the association. By default this is guessed to be the name
759
838
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
760
839
  # as the default <tt>:foreign_key</tt>.
761
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
762
- # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
763
- # * <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
840
+ # [:primary_key]
841
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
842
+ # [:include]
843
+ # Specify second-order associations that should be eager loaded when this object is loaded.
844
+ # [:as]
845
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
846
+ # [:select]
847
+ # 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
764
848
  # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
765
- # * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
849
+ # [:through]
850
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
766
851
  # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
767
852
  # <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
768
- # * <tt>:source</tt> - Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
853
+ # [:source]
854
+ # Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
769
855
  # inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
770
856
  # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
771
- # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
857
+ # [:source_type]
858
+ # Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
772
859
  # association is a polymorphic +belongs_to+.
773
- # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
774
- # * <tt>:validate</tt> - If false, don't validate the associated object when saving the parent object. +false+ by default.
860
+ # [:readonly]
861
+ # If true, the associated object is readonly through the association.
862
+ # [:validate]
863
+ # If false, don't validate the associated object when saving the parent object. +false+ by default.
775
864
  #
776
865
  # Option examples:
777
866
  # has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -793,10 +882,10 @@ module ActiveRecord
793
882
 
794
883
  method_name = "has_one_after_save_for_#{reflection.name}".to_sym
795
884
  define_method(method_name) do
796
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
885
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
797
886
 
798
- if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id)
799
- association["#{reflection.primary_key_name}"] = id
887
+ if !association.nil? && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
888
+ association[reflection.primary_key_name] = id
800
889
  association.save(true)
801
890
  end
802
891
  end
@@ -811,18 +900,34 @@ module ActiveRecord
811
900
  end
812
901
  end
813
902
 
814
- # Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
815
- # +association+ is replaced with the symbol passed as the first argument, so
816
- # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
817
- # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
818
- # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, and sets it as the foreign key.
819
- # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
820
- # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
903
+ # Specifies a one-to-one association with another class. This method should only be used
904
+ # if this class contains the foreign key. If the other class contains the foreign key,
905
+ # then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
906
+ # on when to use +has_one+ and when to use +belongs_to+.
907
+ #
908
+ # Methods will be added for retrieval and query for a single associated object, for which
909
+ # this object holds an id:
910
+ #
911
+ # [association(force_reload = false)]
912
+ # Returns the associated object. +nil+ is returned if none is found.
913
+ # [association=(associate)]
914
+ # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
915
+ # [association.nil?]
916
+ # Returns +true+ if there is no associated object.
917
+ # [build_association(attributes = {})]
918
+ # Returns a new object of the associated type that has been instantiated
821
919
  # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
822
- # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
823
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
920
+ # [create_association(attributes = {})]
921
+ # Returns a new object of the associated type that has been instantiated
922
+ # with +attributes+, linked to this object through a foreign key, and that
923
+ # has already been saved (if it passed the validation).
924
+ #
925
+ # (+association+ is replaced with the symbol passed as the first argument, so
926
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
927
+ #
928
+ # === Example
824
929
  #
825
- # Example: A Post class declares <tt>belongs_to :author</tt>, which will add:
930
+ # A Post class declares <tt>belongs_to :author</tt>, which will add:
826
931
  # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
827
932
  # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
828
933
  # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
@@ -831,36 +936,45 @@ module ActiveRecord
831
936
  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
832
937
  # The declaration can also include an options hash to specialize the behavior of the association.
833
938
  #
834
- # Options are:
835
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
939
+ # === Options
940
+ #
941
+ # [:class_name]
942
+ # Specify the class name of the association. Use it only if that name can't be inferred
836
943
  # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
837
944
  # if the real class name is Person, you'll have to specify it with this option.
838
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
945
+ # [:conditions]
946
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
839
947
  # SQL fragment, such as <tt>authorized = 1</tt>.
840
- # * <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
948
+ # [:select]
949
+ # 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
841
950
  # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
842
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
951
+ # [:foreign_key]
952
+ # Specify the foreign key used for the association. By default this is guessed to be the name
843
953
  # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
844
954
  # "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
845
955
  # will use a foreign key of "favorite_person_id".
846
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
956
+ # [:dependent]
957
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
847
958
  # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
848
959
  # <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
849
960
  # orphaned records behind.
850
- # * <tt>:counter_cache</tt> - Caches the number of belonging objects on the associate class through the use of +increment_counter+
961
+ # [:counter_cache]
962
+ # Caches the number of belonging objects on the associate class through the use of +increment_counter+
851
963
  # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
852
964
  # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
853
965
  # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
854
966
  # a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
855
- # When creating a counter cache column, the database statement or migration must specify a default value of <tt>0</tt>, failing to do
856
- # this results in a counter with +NULL+ value, which will never increment.
857
967
  # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
858
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
859
- # * <tt>:polymorphic</tt> - Specify this association is a polymorphic association by passing +true+.
968
+ # [:include]
969
+ # Specify second-order associations that should be eager loaded when this object is loaded.
970
+ # [:polymorphic]
971
+ # Specify this association is a polymorphic association by passing +true+.
860
972
  # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
861
973
  # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
862
- # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
863
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +false+ by default.
974
+ # [:readonly]
975
+ # If true, the associated object is readonly through the association.
976
+ # [:validate]
977
+ # If false, don't validate the associated objects when saving the parent object. +false+ by default.
864
978
  #
865
979
  # Option examples:
866
980
  # belongs_to :firm, :foreign_key => "client_of"
@@ -880,7 +994,7 @@ module ActiveRecord
880
994
 
881
995
  method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
882
996
  define_method(method_name) do
883
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
997
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
884
998
 
885
999
  if association && association.target
886
1000
  if association.new_record?
@@ -888,8 +1002,8 @@ module ActiveRecord
888
1002
  end
889
1003
 
890
1004
  if association.updated?
891
- self["#{reflection.primary_key_name}"] = association.id
892
- self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s
1005
+ self[reflection.primary_key_name] = association.id
1006
+ self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
893
1007
  end
894
1008
  end
895
1009
  end
@@ -901,7 +1015,7 @@ module ActiveRecord
901
1015
 
902
1016
  method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
903
1017
  define_method(method_name) do
904
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
1018
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
905
1019
 
906
1020
  if !association.nil?
907
1021
  if association.new_record?
@@ -909,7 +1023,7 @@ module ActiveRecord
909
1023
  end
910
1024
 
911
1025
  if association.updated?
912
- self["#{reflection.primary_key_name}"] = association.id
1026
+ self[reflection.primary_key_name] = association.id
913
1027
  end
914
1028
  end
915
1029
  end
@@ -924,15 +1038,15 @@ module ActiveRecord
924
1038
 
925
1039
  method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
926
1040
  define_method(method_name) do
927
- association = send("#{reflection.name}")
928
- association.class.increment_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
1041
+ association = send(reflection.name)
1042
+ association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
929
1043
  end
930
1044
  after_create method_name
931
1045
 
932
1046
  method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
933
1047
  define_method(method_name) do
934
- association = send("#{reflection.name}")
935
- association.class.decrement_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
1048
+ association = send(reflection.name)
1049
+ association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
936
1050
  end
937
1051
  before_destroy method_name
938
1052
 
@@ -946,8 +1060,9 @@ module ActiveRecord
946
1060
  configure_dependency_for_belongs_to(reflection)
947
1061
  end
948
1062
 
949
- # Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
950
- # an option, it is guessed using the lexical order of the class names. So a join between Developer and Project
1063
+ # Specifies a many-to-many relationship with another class. This associates two classes via an
1064
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
1065
+ # guessed using the lexical order of the class names. So a join between Developer and Project
951
1066
  # will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence
952
1067
  # is calculated using the <tt><</tt> operator for String. This means that if the strings are of different lengths,
953
1068
  # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
@@ -962,28 +1077,48 @@ module ActiveRecord
962
1077
  # associations with attributes to a real join model (see introduction).
963
1078
  #
964
1079
  # Adds the following methods for retrieval and query:
965
- # +collection+ is replaced with the symbol passed as the first argument, so
966
- # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
967
- # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
1080
+ #
1081
+ # [collection(force_reload = false)]
1082
+ # Returns an array of all the associated objects.
968
1083
  # An empty array is returned if none are found.
969
- # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by creating associations in the join table
1084
+ # [collection<<(object, ...)]
1085
+ # Adds one or more objects to the collection by creating associations in the join table
970
1086
  # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
971
- # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by removing their associations from the join table.
1087
+ # [collection.delete(object, ...)]
1088
+ # Removes one or more objects from the collection by removing their associations from the join table.
972
1089
  # This does not destroy the objects.
973
- # * <tt>collection=objects</tt> - Replaces the collection's content by deleting and adding objects as appropriate.
974
- # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids.
975
- # * <tt>collection_singular_ids=ids</tt> - Replace the collection by the objects identified by the primary keys in +ids+.
976
- # * <tt>collection.clear</tt> - Removes every object from the collection. This does not destroy the objects.
977
- # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
978
- # * <tt>collection.size</tt> - Returns the number of associated objects.
979
- # * <tt>collection.find(id)</tt> - Finds an associated object responding to the +id+ and that
1090
+ # [collection=objects]
1091
+ # Replaces the collection's content by deleting and adding objects as appropriate.
1092
+ # [collection_singular_ids]
1093
+ # Returns an array of the associated objects' ids.
1094
+ # [collection_singular_ids=ids]
1095
+ # Replace the collection by the objects identified by the primary keys in +ids+.
1096
+ # [collection.clear]
1097
+ # Removes every object from the collection. This does not destroy the objects.
1098
+ # [collection.empty?]
1099
+ # Returns +true+ if there are no associated objects.
1100
+ # [collection.size]
1101
+ # Returns the number of associated objects.
1102
+ # [collection.find(id)]
1103
+ # Finds an associated object responding to the +id+ and that
980
1104
  # meets the condition that it has to be associated with this object.
981
- # * <tt>collection.build(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
1105
+ # Uses the same rules as ActiveRecord::Base.find.
1106
+ # [collection.exist?(...)]
1107
+ # Checks whether an associated object with the given conditions exists.
1108
+ # Uses the same rules as ActiveRecord::Base.exists?.
1109
+ # [collection.build(attributes = {})]
1110
+ # Returns a new object of the collection type that has been instantiated
982
1111
  # with +attributes+ and linked to this object through the join table, but has not yet been saved.
983
- # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
1112
+ # [collection.create(attributes = {})]
1113
+ # Returns a new object of the collection type that has been instantiated
984
1114
  # with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
985
1115
  #
986
- # Example: A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
1116
+ # (+collection+ is replaced with the symbol passed as the first argument, so
1117
+ # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
1118
+ #
1119
+ # === Example
1120
+ #
1121
+ # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
987
1122
  # * <tt>Developer#projects</tt>
988
1123
  # * <tt>Developer#projects<<</tt>
989
1124
  # * <tt>Developer#projects.delete</tt>
@@ -994,44 +1129,67 @@ module ActiveRecord
994
1129
  # * <tt>Developer#projects.empty?</tt>
995
1130
  # * <tt>Developer#projects.size</tt>
996
1131
  # * <tt>Developer#projects.find(id)</tt>
1132
+ # * <tt>Developer#clients.exist?(...)</tt>
997
1133
  # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
998
1134
  # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
999
1135
  # The declaration may include an options hash to specialize the behavior of the association.
1000
1136
  #
1001
- # Options are:
1002
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
1137
+ # === Options
1138
+ #
1139
+ # [:class_name]
1140
+ # Specify the class name of the association. Use it only if that name can't be inferred
1003
1141
  # from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
1004
1142
  # Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
1005
- # * <tt>:join_table</tt> - Specify the name of the join table if the default based on lexical order isn't what you want.
1006
- # WARNING: If you're overwriting the table name of either class, the +table_name+ method MUST be declared underneath any
1007
- # +has_and_belongs_to_many+ declaration in order to work.
1008
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
1143
+ # [:join_table]
1144
+ # Specify the name of the join table if the default based on lexical order isn't what you want.
1145
+ # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
1146
+ # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
1147
+ # [:foreign_key]
1148
+ # Specify the foreign key used for the association. By default this is guessed to be the name
1009
1149
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
1010
1150
  # will use "person_id" as the default <tt>:foreign_key</tt>.
1011
- # * <tt>:association_foreign_key</tt> - Specify the association foreign key used for the association. By default this is
1151
+ # [:association_foreign_key]
1152
+ # Specify the association foreign key used for the association. By default this is
1012
1153
  # guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is Project,
1013
1154
  # the +has_and_belongs_to_many+ association will use "project_id" as the default <tt>:association_foreign_key</tt>.
1014
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1155
+ # [:conditions]
1156
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1015
1157
  # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
1016
1158
  # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
1017
1159
  # or <tt>@blog.posts.build</tt>.
1018
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
1160
+ # [:order]
1161
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
1019
1162
  # such as <tt>last_name, first_name DESC</tt>
1020
- # * <tt>:uniq</tt> - If true, duplicate associated objects will be ignored by accessors and query methods.
1021
- # * <tt>:finder_sql</tt> - Overwrite the default generated SQL statement used to fetch the association with a manual statement
1022
- # * <tt>:delete_sql</tt> - Overwrite the default generated SQL statement used to remove links between the associated
1163
+ # [:uniq]
1164
+ # If true, duplicate associated objects will be ignored by accessors and query methods.
1165
+ # [:finder_sql]
1166
+ # Overwrite the default generated SQL statement used to fetch the association with a manual statement
1167
+ # [:counter_sql]
1168
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
1169
+ # 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>.
1170
+ # [:delete_sql]
1171
+ # Overwrite the default generated SQL statement used to remove links between the associated
1023
1172
  # classes with a manual statement.
1024
- # * <tt>:insert_sql</tt> - Overwrite the default generated SQL statement used to add links between the associated classes
1173
+ # [:insert_sql]
1174
+ # Overwrite the default generated SQL statement used to add links between the associated classes
1025
1175
  # with a manual statement.
1026
- # * <tt>:extend</tt> - Anonymous module for extending the proxy, see "Association extensions".
1027
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
1028
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
1029
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
1030
- # * <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.
1031
- # * <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
1176
+ # [:extend]
1177
+ # Anonymous module for extending the proxy, see "Association extensions".
1178
+ # [:include]
1179
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
1180
+ # [:group]
1181
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
1182
+ # [:limit]
1183
+ # An integer determining the limit on the number of rows that should be returned.
1184
+ # [:offset]
1185
+ # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
1186
+ # [:select]
1187
+ # 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
1032
1188
  # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
1033
- # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
1034
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +true+ by default.
1189
+ # [:readonly]
1190
+ # If true, all the associated objects are readonly through the association.
1191
+ # [:validate]
1192
+ # If false, don't validate the associated objects when saving the parent object. +true+ by default.
1035
1193
  #
1036
1194
  # Option examples:
1037
1195
  # has_and_belongs_to_many :projects
@@ -1063,12 +1221,11 @@ module ActiveRecord
1063
1221
  end
1064
1222
 
1065
1223
  private
1066
- # Generate a join table name from two provided tables names.
1067
- # The order of names in join name is determined by lexical precedence.
1068
- # join_table_name("members", "clubs")
1069
- # => "clubs_members"
1070
- # join_table_name("members", "special_clubs")
1071
- # => "members_special_clubs"
1224
+ # Generates a join table name from two provided table names.
1225
+ # The names in the join table namesme end up in lexicographic order.
1226
+ #
1227
+ # join_table_name("members", "clubs") # => "clubs_members"
1228
+ # join_table_name("members", "special_clubs") # => "members_special_clubs"
1072
1229
  def join_table_name(first_table_name, second_table_name)
1073
1230
  if first_table_name < second_table_name
1074
1231
  join_table = "#{first_table_name}_#{second_table_name}"
@@ -1100,6 +1257,11 @@ module ActiveRecord
1100
1257
  association.target.nil? ? nil : association
1101
1258
  end
1102
1259
 
1260
+ define_method("loaded_#{reflection.name}?") do
1261
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
1262
+ association && association.loaded?
1263
+ end
1264
+
1103
1265
  define_method("#{reflection.name}=") do |new_value|
1104
1266
  association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
1105
1267
 
@@ -1142,7 +1304,11 @@ module ActiveRecord
1142
1304
  end
1143
1305
 
1144
1306
  define_method("#{reflection.name.to_s.singularize}_ids") do
1145
- send(reflection.name).map(&:id)
1307
+ if send(reflection.name).loaded? || reflection.options[:finder_sql]
1308
+ send(reflection.name).map(&:id)
1309
+ else
1310
+ send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
1311
+ end
1146
1312
  end
1147
1313
  end
1148
1314
 
@@ -1163,19 +1329,19 @@ module ActiveRecord
1163
1329
  end
1164
1330
  end
1165
1331
  end
1166
-
1332
+
1167
1333
  def add_single_associated_validation_callbacks(association_name)
1168
1334
  method_name = "validate_associated_records_for_#{association_name}".to_sym
1169
1335
  define_method(method_name) do
1170
1336
  association = instance_variable_get("@#{association_name}")
1171
1337
  if !association.nil?
1172
- errors.add "#{association_name}" unless association.target.nil? || association.valid?
1338
+ errors.add association_name unless association.target.nil? || association.valid?
1173
1339
  end
1174
1340
  end
1175
-
1341
+
1176
1342
  validate method_name
1177
1343
  end
1178
-
1344
+
1179
1345
  def add_multiple_associated_validation_callbacks(association_name)
1180
1346
  method_name = "validate_associated_records_for_#{association_name}".to_sym
1181
1347
  ivar = "@#{association_name}"
@@ -1191,7 +1357,7 @@ module ActiveRecord
1191
1357
  else
1192
1358
  association.target.select { |record| record.new_record? }
1193
1359
  end.each do |record|
1194
- errors.add "#{association_name}" unless record.valid?
1360
+ errors.add association_name unless record.valid?
1195
1361
  end
1196
1362
  end
1197
1363
  end
@@ -1211,7 +1377,7 @@ module ActiveRecord
1211
1377
 
1212
1378
  method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
1213
1379
  define_method(method_name) do
1214
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
1380
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
1215
1381
 
1216
1382
  records_to_save = if @new_record_before_save
1217
1383
  association
@@ -1263,56 +1429,85 @@ module ActiveRecord
1263
1429
  []
1264
1430
  end
1265
1431
 
1432
+ # Creates before_destroy callback methods that nullify, delete or destroy
1433
+ # has_many associated objects, according to the defined :dependent rule.
1434
+ #
1266
1435
  # See HasManyAssociation#delete_records. Dependent associations
1267
1436
  # delete children, otherwise foreign key is set to NULL.
1268
- def configure_dependency_for_has_many(reflection)
1437
+ #
1438
+ # The +extra_conditions+ parameter, which is not used within the main
1439
+ # Active Record codebase, is meant to allow plugins to define extra
1440
+ # finder conditions.
1441
+ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
1269
1442
  if reflection.options.include?(:dependent)
1270
1443
  # Add polymorphic type if the :as option is present
1271
1444
  dependent_conditions = []
1272
1445
  dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
1273
1446
  dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
1274
1447
  dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
1448
+ dependent_conditions << extra_conditions if extra_conditions
1275
1449
  dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
1276
1450
 
1277
1451
  case reflection.options[:dependent]
1278
1452
  when :destroy
1279
1453
  method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
1280
1454
  define_method(method_name) do
1281
- send("#{reflection.name}").each { |o| o.destroy }
1455
+ send(reflection.name).each { |o| o.destroy }
1282
1456
  end
1283
1457
  before_destroy method_name
1284
1458
  when :delete_all
1285
- module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
1459
+ module_eval %Q{
1460
+ before_destroy do |record|
1461
+ delete_all_has_many_dependencies(record,
1462
+ "#{reflection.name}",
1463
+ #{reflection.class_name},
1464
+ "#{dependent_conditions}")
1465
+ end
1466
+ }
1286
1467
  when :nullify
1287
- module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
1468
+ module_eval %Q{
1469
+ before_destroy do |record|
1470
+ nullify_has_many_dependencies(record,
1471
+ "#{reflection.name}",
1472
+ #{reflection.class_name},
1473
+ "#{reflection.primary_key_name}",
1474
+ "#{dependent_conditions}")
1475
+ end
1476
+ }
1288
1477
  else
1289
1478
  raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
1290
1479
  end
1291
1480
  end
1292
1481
  end
1293
1482
 
1483
+ # Creates before_destroy callback methods that nullify, delete or destroy
1484
+ # has_one associated objects, according to the defined :dependent rule.
1294
1485
  def configure_dependency_for_has_one(reflection)
1295
1486
  if reflection.options.include?(:dependent)
1296
1487
  case reflection.options[:dependent]
1297
1488
  when :destroy
1298
1489
  method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
1299
1490
  define_method(method_name) do
1300
- association = send("#{reflection.name}")
1491
+ association = send(reflection.name)
1301
1492
  association.destroy unless association.nil?
1302
1493
  end
1303
1494
  before_destroy method_name
1304
1495
  when :delete
1305
1496
  method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
1306
1497
  define_method(method_name) do
1307
- association = send("#{reflection.name}")
1308
- association.class.delete(association.id) unless association.nil?
1498
+ # Retrieve the associated object and delete it. The retrieval
1499
+ # is necessary because there may be multiple associated objects
1500
+ # with foreign keys pointing to this object, and we only want
1501
+ # to delete the correct one, not all of them.
1502
+ association = send(reflection.name)
1503
+ association.delete unless association.nil?
1309
1504
  end
1310
1505
  before_destroy method_name
1311
1506
  when :nullify
1312
1507
  method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
1313
1508
  define_method(method_name) do
1314
- association = send("#{reflection.name}")
1315
- association.update_attribute("#{reflection.primary_key_name}", nil) unless association.nil?
1509
+ association = send(reflection.name)
1510
+ association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
1316
1511
  end
1317
1512
  before_destroy method_name
1318
1513
  else
@@ -1327,15 +1522,15 @@ module ActiveRecord
1327
1522
  when :destroy
1328
1523
  method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
1329
1524
  define_method(method_name) do
1330
- association = send("#{reflection.name}")
1525
+ association = send(reflection.name)
1331
1526
  association.destroy unless association.nil?
1332
1527
  end
1333
1528
  before_destroy method_name
1334
1529
  when :delete
1335
1530
  method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
1336
1531
  define_method(method_name) do
1337
- association = send("#{reflection.name}")
1338
- association.class.delete(association.id) unless association.nil?
1532
+ association = send(reflection.name)
1533
+ association.delete unless association.nil?
1339
1534
  end
1340
1535
  before_destroy method_name
1341
1536
  else
@@ -1344,32 +1539,46 @@ module ActiveRecord
1344
1539
  end
1345
1540
  end
1346
1541
 
1347
- def create_has_many_reflection(association_id, options, &extension)
1348
- options.assert_valid_keys(
1349
- :class_name, :table_name, :foreign_key,
1350
- :dependent,
1351
- :select, :conditions, :include, :order, :group, :limit, :offset,
1352
- :as, :through, :source, :source_type,
1353
- :uniq,
1354
- :finder_sql, :counter_sql,
1355
- :before_add, :after_add, :before_remove, :after_remove,
1356
- :extend, :readonly,
1357
- :validate
1358
- )
1542
+ def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions)
1543
+ association_class.delete_all(dependent_conditions)
1544
+ end
1359
1545
 
1546
+ def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions)
1547
+ association_class.update_all("#{primary_key_name} = NULL", dependent_conditions)
1548
+ end
1549
+
1550
+ mattr_accessor :valid_keys_for_has_many_association
1551
+ @@valid_keys_for_has_many_association = [
1552
+ :class_name, :table_name, :foreign_key, :primary_key,
1553
+ :dependent,
1554
+ :select, :conditions, :include, :order, :group, :limit, :offset,
1555
+ :as, :through, :source, :source_type,
1556
+ :uniq,
1557
+ :finder_sql, :counter_sql,
1558
+ :before_add, :after_add, :before_remove, :after_remove,
1559
+ :extend, :readonly,
1560
+ :validate
1561
+ ]
1562
+
1563
+ def create_has_many_reflection(association_id, options, &extension)
1564
+ options.assert_valid_keys(valid_keys_for_has_many_association)
1360
1565
  options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1361
1566
 
1362
1567
  create_reflection(:has_many, association_id, options, self)
1363
1568
  end
1364
1569
 
1365
- def create_has_one_reflection(association_id, options)
1366
- options.assert_valid_keys(
1367
- :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, :validate
1368
- )
1570
+ mattr_accessor :valid_keys_for_has_one_association
1571
+ @@valid_keys_for_has_one_association = [
1572
+ :class_name, :foreign_key, :remote, :select, :conditions, :order,
1573
+ :include, :dependent, :counter_cache, :extend, :as, :readonly,
1574
+ :validate, :primary_key
1575
+ ]
1369
1576
 
1577
+ def create_has_one_reflection(association_id, options)
1578
+ options.assert_valid_keys(valid_keys_for_has_one_association)
1370
1579
  create_reflection(:has_one, association_id, options, self)
1371
1580
  end
1372
-
1581
+
1373
1582
  def create_has_one_through_reflection(association_id, options)
1374
1583
  options.assert_valid_keys(
1375
1584
  :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
@@ -1377,12 +1586,15 @@ module ActiveRecord
1377
1586
  create_reflection(:has_one, association_id, options, self)
1378
1587
  end
1379
1588
 
1380
- def create_belongs_to_reflection(association_id, options)
1381
- options.assert_valid_keys(
1382
- :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
1383
- :counter_cache, :extend, :polymorphic, :readonly, :validate
1384
- )
1589
+ mattr_accessor :valid_keys_for_belongs_to_association
1590
+ @@valid_keys_for_belongs_to_association = [
1591
+ :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
1592
+ :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
1593
+ :validate
1594
+ ]
1385
1595
 
1596
+ def create_belongs_to_reflection(association_id, options)
1597
+ options.assert_valid_keys(valid_keys_for_belongs_to_association)
1386
1598
  reflection = create_reflection(:belongs_to, association_id, options, self)
1387
1599
 
1388
1600
  if options[:polymorphic]
@@ -1392,16 +1604,19 @@ module ActiveRecord
1392
1604
  reflection
1393
1605
  end
1394
1606
 
1607
+ mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
1608
+ @@valid_keys_for_has_and_belongs_to_many_association = [
1609
+ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
1610
+ :select, :conditions, :include, :order, :group, :limit, :offset,
1611
+ :uniq,
1612
+ :finder_sql, :counter_sql, :delete_sql, :insert_sql,
1613
+ :before_add, :after_add, :before_remove, :after_remove,
1614
+ :extend, :readonly,
1615
+ :validate
1616
+ ]
1617
+
1395
1618
  def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1396
- options.assert_valid_keys(
1397
- :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
1398
- :select, :conditions, :include, :order, :group, :limit, :offset,
1399
- :uniq,
1400
- :finder_sql, :delete_sql, :insert_sql,
1401
- :before_add, :after_add, :before_remove, :after_remove,
1402
- :extend, :readonly,
1403
- :validate
1404
- )
1619
+ options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
1405
1620
 
1406
1621
  options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1407
1622
 
@@ -1437,7 +1652,7 @@ module ActiveRecord
1437
1652
  sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
1438
1653
  sql << join_dependency.join_associations.collect{|join| join.association_join }.join
1439
1654
 
1440
- add_joins!(sql, options, scope)
1655
+ add_joins!(sql, options[:joins], scope)
1441
1656
  add_conditions!(sql, options[:conditions], scope)
1442
1657
  add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
1443
1658
 
@@ -1492,8 +1707,8 @@ module ActiveRecord
1492
1707
  sql << " FROM #{connection.quote_table_name table_name} "
1493
1708
 
1494
1709
  if is_distinct
1495
- sql << distinct_join_associations.collect(&:association_join).join
1496
- add_joins!(sql, options, scope)
1710
+ sql << distinct_join_associations.collect { |assoc| assoc.association_join }.join
1711
+ add_joins!(sql, options[:joins], scope)
1497
1712
  end
1498
1713
 
1499
1714
  add_conditions!(sql, options[:conditions], scope)
@@ -1761,7 +1976,7 @@ module ActiveRecord
1761
1976
  end
1762
1977
 
1763
1978
  def aliased_primary_key
1764
- "#{ aliased_prefix }_r0"
1979
+ "#{aliased_prefix}_r0"
1765
1980
  end
1766
1981
 
1767
1982
  def aliased_table_name
@@ -1773,7 +1988,7 @@ module ActiveRecord
1773
1988
  @column_names_with_alias = []
1774
1989
 
1775
1990
  ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
1776
- @column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
1991
+ @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
1777
1992
  end
1778
1993
  end
1779
1994
 
@@ -1810,11 +2025,11 @@ module ActiveRecord
1810
2025
  @aliased_prefix = "t#{ join_dependency.joins.size }"
1811
2026
  @parent_table_name = parent.active_record.table_name
1812
2027
  @aliased_table_name = aliased_table_name_for(table_name)
1813
-
2028
+
1814
2029
  if reflection.macro == :has_and_belongs_to_many
1815
2030
  @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
1816
2031
  end
1817
-
2032
+
1818
2033
  if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
1819
2034
  @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
1820
2035
  end
@@ -1951,7 +2166,7 @@ module ActiveRecord
1951
2166
  end
1952
2167
 
1953
2168
  protected
1954
-
2169
+
1955
2170
  def aliased_table_name_for(name, suffix = nil)
1956
2171
  if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{name.downcase}\son}
1957
2172
  @join_dependency.table_aliases[name] += 1
@@ -1969,7 +2184,7 @@ module ActiveRecord
1969
2184
 
1970
2185
  name
1971
2186
  end
1972
-
2187
+
1973
2188
  def pluralize(table_name)
1974
2189
  ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
1975
2190
  end