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.
- data/CHANGELOG +32 -6
- data/README +0 -0
- data/Rakefile +4 -5
- data/lib/active_record.rb +11 -10
- data/lib/active_record/aggregations.rb +110 -38
- data/lib/active_record/association_preload.rb +104 -15
- data/lib/active_record/associations.rb +427 -212
- data/lib/active_record/associations/association_collection.rb +101 -16
- data/lib/active_record/associations/association_proxy.rb +65 -13
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
- data/lib/active_record/associations/has_many_association.rb +28 -28
- data/lib/active_record/associations/has_many_through_association.rb +21 -19
- data/lib/active_record/associations/has_one_association.rb +24 -7
- data/lib/active_record/associations/has_one_through_association.rb +3 -4
- data/lib/active_record/attribute_methods.rb +13 -5
- data/lib/active_record/base.rb +435 -212
- data/lib/active_record/calculations.rb +12 -5
- data/lib/active_record/callbacks.rb +28 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
- data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
- data/lib/active_record/dirty.rb +25 -7
- data/lib/active_record/dynamic_finder_match.rb +41 -0
- data/lib/active_record/fixtures.rb +10 -9
- data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
- data/lib/active_record/locale/en.yml +54 -0
- data/lib/active_record/migration.rb +47 -10
- data/lib/active_record/named_scope.rb +29 -16
- data/lib/active_record/reflection.rb +118 -54
- data/lib/active_record/schema_dumper.rb +13 -7
- data/lib/active_record/test_case.rb +18 -5
- data/lib/active_record/transactions.rb +89 -34
- data/lib/active_record/validations.rb +270 -180
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_mysql.rb +5 -0
- data/test/cases/adapter_test.rb +6 -0
- data/test/cases/aggregations_test.rb +39 -0
- data/test/cases/associations/belongs_to_associations_test.rb +10 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
- data/test/cases/associations/eager_test.rb +54 -5
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
- data/test/cases/associations/has_many_associations_test.rb +74 -7
- data/test/cases/associations/has_many_through_associations_test.rb +50 -3
- data/test/cases/associations/has_one_associations_test.rb +17 -0
- data/test/cases/associations/has_one_through_associations_test.rb +49 -1
- data/test/cases/associations_test.rb +0 -0
- data/test/cases/attribute_methods_test.rb +59 -4
- data/test/cases/base_test.rb +93 -21
- data/test/cases/binary_test.rb +1 -5
- data/test/cases/calculations_test.rb +5 -0
- data/test/cases/callbacks_observers_test.rb +38 -0
- data/test/cases/connection_test_mysql.rb +1 -1
- data/test/cases/defaults_test.rb +32 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +13 -0
- data/test/cases/finder_test.rb +162 -12
- data/test/cases/fixtures_test.rb +32 -3
- data/test/cases/helper.rb +15 -0
- data/test/cases/i18n_test.rb +41 -0
- data/test/cases/inheritance_test.rb +2 -2
- data/test/cases/lifecycle_test.rb +0 -0
- data/test/cases/locking_test.rb +4 -9
- data/test/cases/method_scoping_test.rb +109 -2
- data/test/cases/migration_test.rb +43 -8
- data/test/cases/multiple_db_test.rb +25 -0
- data/test/cases/named_scope_test.rb +74 -0
- data/test/cases/pooled_connections_test.rb +103 -0
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +11 -3
- data/test/cases/reload_models_test.rb +20 -0
- data/test/cases/sanitize_test.rb +25 -0
- data/test/cases/schema_authorization_test_postgresql.rb +2 -2
- data/test/cases/transactions_test.rb +62 -12
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_i18n_test.rb +921 -0
- data/test/cases/validations_test.rb +44 -33
- data/test/connections/native_mysql/connection.rb +1 -3
- data/test/fixtures/companies.yml +1 -0
- data/test/fixtures/customers.yml +10 -1
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/organizations.yml +5 -0
- data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
- data/test/models/author.rb +3 -0
- data/test/models/category.rb +3 -0
- data/test/models/club.rb +6 -0
- data/test/models/company.rb +25 -1
- data/test/models/customer.rb +19 -1
- data/test/models/member.rb +2 -0
- data/test/models/member_detail.rb +4 -0
- data/test/models/organization.rb +4 -0
- data/test/models/parrot.rb +1 -0
- data/test/models/post.rb +3 -0
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +3 -0
- data/test/schema/schema.rb +12 -1
- metadata +22 -10
- data/lib/active_record/vendor/mysql.rb +0 -1214
- data/test/cases/adapter_test_sqlserver.rb +0 -95
- data/test/cases/table_name_test_sqlserver.rb +0 -23
- data/test/cases/threaded_connections_test.rb +0 -48
- 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
|
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::
|
590
|
-
# with a class in another module scope, this can be done by specifying the complete class name.
|
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
|
-
#
|
615
|
-
#
|
616
|
-
#
|
617
|
-
#
|
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
|
-
#
|
620
|
-
#
|
621
|
-
#
|
622
|
-
#
|
623
|
-
#
|
624
|
-
#
|
625
|
-
#
|
626
|
-
#
|
627
|
-
#
|
628
|
-
#
|
629
|
-
#
|
630
|
-
#
|
631
|
-
#
|
632
|
-
#
|
633
|
-
# associated
|
634
|
-
#
|
635
|
-
#
|
636
|
-
#
|
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 =
|
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
|
-
#
|
654
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
676
|
-
#
|
677
|
-
#
|
678
|
-
#
|
679
|
-
#
|
680
|
-
#
|
681
|
-
#
|
682
|
-
#
|
683
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
692
|
-
#
|
693
|
-
#
|
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
|
-
#
|
726
|
-
#
|
727
|
-
#
|
728
|
-
#
|
729
|
-
#
|
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
|
-
#
|
732
|
-
#
|
733
|
-
#
|
734
|
-
#
|
735
|
-
#
|
736
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
762
|
-
#
|
763
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
774
|
-
#
|
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(
|
885
|
+
association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
797
886
|
|
798
|
-
if !association.nil? && (new_record? || association.new_record? || association[
|
799
|
-
association[
|
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
|
-
#
|
815
|
-
#
|
816
|
-
#
|
817
|
-
#
|
818
|
-
#
|
819
|
-
#
|
820
|
-
#
|
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
|
-
#
|
823
|
-
#
|
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
|
-
#
|
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
|
835
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
859
|
-
#
|
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
|
-
#
|
863
|
-
#
|
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(
|
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[
|
892
|
-
self[
|
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(
|
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[
|
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(
|
928
|
-
association.class.increment_counter(
|
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(
|
935
|
-
association.class.decrement_counter(
|
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
|
-
#
|
950
|
-
#
|
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
|
-
#
|
966
|
-
#
|
967
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
974
|
-
#
|
975
|
-
#
|
976
|
-
#
|
977
|
-
#
|
978
|
-
#
|
979
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
1002
|
-
#
|
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
|
-
#
|
1006
|
-
#
|
1007
|
-
#
|
1008
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
1021
|
-
#
|
1022
|
-
#
|
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
|
-
#
|
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
|
-
#
|
1027
|
-
#
|
1028
|
-
#
|
1029
|
-
#
|
1030
|
-
#
|
1031
|
-
#
|
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
|
-
#
|
1034
|
-
#
|
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
|
-
#
|
1067
|
-
# The
|
1068
|
-
#
|
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).
|
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
|
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
|
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(
|
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
|
-
|
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(
|
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
|
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
|
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(
|
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
|
-
|
1308
|
-
|
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(
|
1315
|
-
association.update_attribute(
|
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(
|
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(
|
1338
|
-
association.
|
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
|
1348
|
-
|
1349
|
-
|
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
|
-
|
1366
|
-
|
1367
|
-
|
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
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
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
|
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
|
-
"#{
|
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, "#{
|
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
|