activerecord 4.0.4 → 4.1.16
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1632 -1797
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +49 -29
- data/lib/active_record/associations/association.rb +9 -17
- data/lib/active_record/associations/association_scope.rb +59 -49
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
- data/lib/active_record/associations/builder/association.rb +84 -54
- data/lib/active_record/associations/builder/belongs_to.rb +90 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
- data/lib/active_record/associations/builder/has_many.rb +3 -3
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +121 -111
- data/lib/active_record/associations/collection_proxy.rb +73 -18
- data/lib/active_record/associations/has_many_association.rb +14 -11
- data/lib/active_record/associations/has_many_through_association.rb +33 -6
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_dependency.rb +208 -168
- data/lib/active_record/associations/preloader/association.rb +69 -27
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/preloader.rb +63 -49
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +30 -9
- data/lib/active_record/associations.rb +116 -42
- data/lib/active_record/attribute_assignment.rb +6 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +35 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -1
- data/lib/active_record/attribute_methods/read.rb +56 -29
- data/lib/active_record/attribute_methods/serialization.rb +44 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
- data/lib/active_record/attribute_methods/write.rb +59 -26
- data/lib/active_record/attribute_methods.rb +82 -43
- data/lib/active_record/autosave_association.rb +209 -194
- data/lib/active_record/base.rb +6 -2
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
- data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
- data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
- data/lib/active_record/connection_handling.rb +39 -5
- data/lib/active_record/core.rb +38 -54
- data/lib/active_record/counter_cache.rb +9 -10
- data/lib/active_record/dynamic_matchers.rb +6 -2
- data/lib/active_record/enum.rb +199 -0
- data/lib/active_record/errors.rb +22 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +173 -76
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +23 -9
- data/lib/active_record/integration.rb +54 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +6 -13
- data/lib/active_record/migration/command_recorder.rb +8 -2
- data/lib/active_record/migration.rb +91 -56
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +25 -13
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +26 -6
- data/lib/active_record/persistence.rb +23 -29
- data/lib/active_record/querying.rb +15 -12
- data/lib/active_record/railtie.rb +12 -61
- data/lib/active_record/railties/databases.rake +37 -56
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +230 -79
- data/lib/active_record/relation/batches.rb +74 -24
- data/lib/active_record/relation/calculations.rb +52 -48
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +210 -67
- data/lib/active_record/relation/merger.rb +15 -12
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder.rb +81 -40
- data/lib/active_record/relation/query_methods.rb +185 -108
- data/lib/active_record/relation/spawn_methods.rb +8 -5
- data/lib/active_record/relation.rb +79 -84
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +4 -4
- data/lib/active_record/schema_dumper.rb +18 -6
- data/lib/active_record/schema_migration.rb +31 -18
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +14 -29
- data/lib/active_record/scoping.rb +5 -0
- data/lib/active_record/store.rb +67 -18
- data/lib/active_record/tasks/database_tasks.rb +66 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/transactions.rb +10 -12
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +19 -9
- data/lib/active_record/version.rb +4 -7
- data/lib/active_record.rb +5 -7
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- data/lib/rails/generators/active_record.rb +2 -8
- metadata +18 -30
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/join_helper.rb +0 -45
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -96
@@ -4,6 +4,12 @@ require 'active_support/core_ext/module/remove_method'
|
|
4
4
|
require 'active_record/errors'
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
|
+
class AssociationNotFoundError < ConfigurationError #:nodoc:
|
8
|
+
def initialize(record, association_name)
|
9
|
+
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
8
14
|
def initialize(reflection, associated_class = nil)
|
9
15
|
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
@@ -44,7 +50,7 @@ module ActiveRecord
|
|
44
50
|
def initialize(reflection)
|
45
51
|
through_reflection = reflection.through_reflection
|
46
52
|
source_reflection_names = reflection.source_reflection_names
|
47
|
-
source_associations = reflection.through_reflection.klass.
|
53
|
+
source_associations = reflection.through_reflection.klass._reflections.keys
|
48
54
|
super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
|
49
55
|
end
|
50
56
|
end
|
@@ -73,21 +79,15 @@ module ActiveRecord
|
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
76
|
-
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
|
77
|
-
def initialize(reflection)
|
78
|
-
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
82
|
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
|
83
83
|
def initialize(reflection)
|
84
|
-
super("
|
84
|
+
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
88
|
class ReadOnlyAssociation < ActiveRecordError #:nodoc:
|
89
89
|
def initialize(reflection)
|
90
|
-
super("
|
90
|
+
super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
@@ -114,7 +114,6 @@ module ActiveRecord
|
|
114
114
|
|
115
115
|
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
|
116
116
|
autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
|
117
|
-
autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
|
118
117
|
autoload :HasManyAssociation, 'active_record/associations/has_many_association'
|
119
118
|
autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
|
120
119
|
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
|
@@ -137,7 +136,6 @@ module ActiveRecord
|
|
137
136
|
autoload :JoinDependency, 'active_record/associations/join_dependency'
|
138
137
|
autoload :AssociationScope, 'active_record/associations/association_scope'
|
139
138
|
autoload :AliasTracker, 'active_record/associations/alias_tracker'
|
140
|
-
autoload :JoinHelper, 'active_record/associations/join_helper'
|
141
139
|
end
|
142
140
|
|
143
141
|
# Clears out the association cache.
|
@@ -153,7 +151,7 @@ module ActiveRecord
|
|
153
151
|
association = association_instance_get(name)
|
154
152
|
|
155
153
|
if association.nil?
|
156
|
-
reflection
|
154
|
+
raise AssociationNotFoundError.new(self, name) unless reflection = self.class._reflect_on_association(name)
|
157
155
|
association = reflection.association_class.new(self, reflection)
|
158
156
|
association_instance_set(name, association)
|
159
157
|
end
|
@@ -164,7 +162,7 @@ module ActiveRecord
|
|
164
162
|
private
|
165
163
|
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
|
166
164
|
def association_instance_get(name)
|
167
|
-
@association_cache[name
|
165
|
+
@association_cache[name]
|
168
166
|
end
|
169
167
|
|
170
168
|
# Set the specified association instance.
|
@@ -172,7 +170,7 @@ module ActiveRecord
|
|
172
170
|
@association_cache[name] = association
|
173
171
|
end
|
174
172
|
|
175
|
-
# Associations are a set of macro-like class methods for tying objects together through
|
173
|
+
# \Associations are a set of macro-like class methods for tying objects together through
|
176
174
|
# foreign keys. They express relationships like "Project has one Project Manager"
|
177
175
|
# or "Project belongs to a Portfolio". Each macro adds a number of methods to the
|
178
176
|
# class which are specialized according to the collection or association symbol and the
|
@@ -365,11 +363,11 @@ module ActiveRecord
|
|
365
363
|
# there is some special behavior you should be aware of, mostly involving the saving of
|
366
364
|
# associated objects.
|
367
365
|
#
|
368
|
-
# You can set the
|
366
|
+
# You can set the <tt>:autosave</tt> option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
|
369
367
|
# <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
|
370
368
|
# to +true+ will _always_ save the members, whereas setting it to +false+ will
|
371
|
-
# _never_ save the members. More details about
|
372
|
-
#
|
369
|
+
# _never_ save the members. More details about <tt>:autosave</tt> option is available at
|
370
|
+
# AutosaveAssociation.
|
373
371
|
#
|
374
372
|
# === One-to-one associations
|
375
373
|
#
|
@@ -402,7 +400,7 @@ module ActiveRecord
|
|
402
400
|
#
|
403
401
|
# == Customizing the query
|
404
402
|
#
|
405
|
-
# Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
|
403
|
+
# \Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
|
406
404
|
# to customize them. For example, to add a condition:
|
407
405
|
#
|
408
406
|
# class Blog < ActiveRecord::Base
|
@@ -568,6 +566,8 @@ module ActiveRecord
|
|
568
566
|
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
|
569
567
|
# @group.avatars.delete(@group.avatars.last) # so would this
|
570
568
|
#
|
569
|
+
# == Setting Inverses
|
570
|
+
#
|
571
571
|
# If you are using a +belongs_to+ on the join model, it is a good idea to set the
|
572
572
|
# <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
|
573
573
|
# works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
|
@@ -584,7 +584,27 @@ module ActiveRecord
|
|
584
584
|
# belongs_to :tag, inverse_of: :taggings
|
585
585
|
# end
|
586
586
|
#
|
587
|
-
#
|
587
|
+
# If you do not set the <tt>:inverse_of</tt> record, the association will
|
588
|
+
# do its best to match itself up with the correct inverse. Automatic
|
589
|
+
# inverse detection only works on <tt>has_many</tt>, <tt>has_one</tt>, and
|
590
|
+
# <tt>belongs_to</tt> associations.
|
591
|
+
#
|
592
|
+
# Extra options on the associations, as defined in the
|
593
|
+
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
|
594
|
+
# also prevent the association's inverse from being found automatically.
|
595
|
+
#
|
596
|
+
# The automatic guessing of the inverse association uses a heuristic based
|
597
|
+
# on the name of the class, so it may not work for all associations,
|
598
|
+
# especially the ones with non-standard names.
|
599
|
+
#
|
600
|
+
# You can turn off the automatic detection of inverse associations by setting
|
601
|
+
# the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
|
602
|
+
#
|
603
|
+
# class Taggable < ActiveRecord::Base
|
604
|
+
# belongs_to :tag, inverse_of: false
|
605
|
+
# end
|
606
|
+
#
|
607
|
+
# == Nested \Associations
|
588
608
|
#
|
589
609
|
# You can actually specify *any* association with the <tt>:through</tt> option, including an
|
590
610
|
# association which has a <tt>:through</tt> option itself. For example:
|
@@ -627,7 +647,7 @@ module ActiveRecord
|
|
627
647
|
# add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
|
628
648
|
# intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
|
629
649
|
#
|
630
|
-
# == Polymorphic Associations
|
650
|
+
# == Polymorphic \Associations
|
631
651
|
#
|
632
652
|
# Polymorphic associations on models are not restricted on what types of models they
|
633
653
|
# can be associated with. Rather, they specify an interface that a +has_many+ association
|
@@ -654,11 +674,14 @@ module ActiveRecord
|
|
654
674
|
# and member posts that use the posts table for STI. In this case, there must be a +type+
|
655
675
|
# column in the posts table.
|
656
676
|
#
|
677
|
+
# Note: The <tt>attachable_type=</tt> method is being called when assigning an +attachable+.
|
678
|
+
# The +class_name+ of the +attachable+ is passed as a String.
|
679
|
+
#
|
657
680
|
# class Asset < ActiveRecord::Base
|
658
681
|
# belongs_to :attachable, polymorphic: true
|
659
682
|
#
|
660
|
-
# def attachable_type=(
|
661
|
-
# super(
|
683
|
+
# def attachable_type=(class_name)
|
684
|
+
# super(class_name.constantize.base_class.to_s)
|
662
685
|
# end
|
663
686
|
# end
|
664
687
|
#
|
@@ -750,6 +773,12 @@ module ActiveRecord
|
|
750
773
|
# like this can have unintended consequences.
|
751
774
|
# In the above example posts with no approved comments are not returned at all, because
|
752
775
|
# the conditions apply to the SQL statement as a whole and not just to the association.
|
776
|
+
#
|
777
|
+
# If you want to load all posts (including posts with no approved comments) then write
|
778
|
+
# your own LEFT OUTER JOIN query using ON
|
779
|
+
#
|
780
|
+
# Post.joins('LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = true')
|
781
|
+
#
|
753
782
|
# You must disambiguate column references for this fallback to happen, for example
|
754
783
|
# <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
|
755
784
|
#
|
@@ -789,7 +818,7 @@ module ActiveRecord
|
|
789
818
|
# For example if all the addressables are either of class Person or Company then a total
|
790
819
|
# of 3 queries will be executed. The list of addressable types to load is determined on
|
791
820
|
# the back of the addresses loaded. This is not supported if Active Record has to fallback
|
792
|
-
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError
|
821
|
+
# to the previous implementation of eager loading and will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>.
|
793
822
|
# The reason is that the parent model's type is a column value so its corresponding table
|
794
823
|
# name cannot be put in the +FROM+/+JOIN+ clauses of that query.
|
795
824
|
#
|
@@ -1024,7 +1053,7 @@ module ActiveRecord
|
|
1024
1053
|
# An empty array is returned if none are found.
|
1025
1054
|
# [collection<<(object, ...)]
|
1026
1055
|
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
|
1027
|
-
# Note that this operation instantly fires update
|
1056
|
+
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
|
1028
1057
|
# parent object, unless the parent object is a new record.
|
1029
1058
|
# [collection.delete(object, ...)]
|
1030
1059
|
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
|
@@ -1060,10 +1089,10 @@ module ActiveRecord
|
|
1060
1089
|
# [collection.size]
|
1061
1090
|
# Returns the number of associated objects.
|
1062
1091
|
# [collection.find(...)]
|
1063
|
-
# Finds an associated object according to the same rules as ActiveRecord::Base.find
|
1092
|
+
# Finds an associated object according to the same rules as <tt>ActiveRecord::Base.find</tt>.
|
1064
1093
|
# [collection.exists?(...)]
|
1065
1094
|
# Checks whether an associated object with the given conditions exists.
|
1066
|
-
# Uses the same rules as ActiveRecord::Base.exists
|
1095
|
+
# Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
|
1067
1096
|
# [collection.build(attributes = {}, ...)]
|
1068
1097
|
# Returns one or more new objects of the collection type that have been instantiated
|
1069
1098
|
# with +attributes+ and linked to this object through a foreign key, but have not yet
|
@@ -1082,7 +1111,7 @@ module ActiveRecord
|
|
1082
1111
|
#
|
1083
1112
|
# === Example
|
1084
1113
|
#
|
1085
|
-
#
|
1114
|
+
# A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
|
1086
1115
|
# * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
|
1087
1116
|
# * <tt>Firm#clients<<</tt>
|
1088
1117
|
# * <tt>Firm#clients.delete</tt>
|
@@ -1116,8 +1145,8 @@ module ActiveRecord
|
|
1116
1145
|
# Controls what happens to the associated objects when
|
1117
1146
|
# their owner is destroyed. Note that these are implemented as
|
1118
1147
|
# callbacks, and Rails executes callbacks in order. Therefore, other
|
1119
|
-
# similar callbacks may affect the
|
1120
|
-
#
|
1148
|
+
# similar callbacks may affect the <tt>:dependent</tt> behavior, and the
|
1149
|
+
# <tt>:dependent</tt> behavior may affect other callbacks.
|
1121
1150
|
#
|
1122
1151
|
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
|
1123
1152
|
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
|
@@ -1163,8 +1192,8 @@ module ActiveRecord
|
|
1163
1192
|
# If true, always save the associated objects or destroy them if marked for destruction,
|
1164
1193
|
# when saving the parent object. If false, never save or destroy the associated objects.
|
1165
1194
|
# By default, only save associated objects that are new records. This option is implemented as a
|
1166
|
-
# before_save callback. Because callbacks are run in the order they are defined, associated objects
|
1167
|
-
# may need to be explicitly saved in any user-defined before_save callbacks.
|
1195
|
+
# +before_save+ callback. Because callbacks are run in the order they are defined, associated objects
|
1196
|
+
# may need to be explicitly saved in any user-defined +before_save+ callbacks.
|
1168
1197
|
#
|
1169
1198
|
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
|
1170
1199
|
# [:inverse_of]
|
@@ -1183,13 +1212,14 @@ module ActiveRecord
|
|
1183
1212
|
# has_many :reports, -> { readonly }
|
1184
1213
|
# has_many :subscribers, through: :subscriptions, source: :user
|
1185
1214
|
def has_many(name, scope = nil, options = {}, &extension)
|
1186
|
-
Builder::HasMany.build(self, name, scope, options, &extension)
|
1215
|
+
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
|
1216
|
+
Reflection.add_reflection self, name, reflection
|
1187
1217
|
end
|
1188
1218
|
|
1189
1219
|
# Specifies a one-to-one association with another class. This method should only be used
|
1190
1220
|
# if the other class contains the foreign key. If the current class contains the foreign key,
|
1191
1221
|
# then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
|
1192
|
-
# on when to use has_one and when to use belongs_to
|
1222
|
+
# on when to use +has_one+ and when to use +belongs_to+.
|
1193
1223
|
#
|
1194
1224
|
# The following methods for retrieval and query of a single associated object will be added:
|
1195
1225
|
#
|
@@ -1197,7 +1227,8 @@ module ActiveRecord
|
|
1197
1227
|
# Returns the associated object. +nil+ is returned if none is found.
|
1198
1228
|
# [association=(associate)]
|
1199
1229
|
# Assigns the associate object, extracts the primary key, sets it as the foreign key,
|
1200
|
-
# and saves the associate object.
|
1230
|
+
# and saves the associate object. To avoid database inconsistencies, permanently deletes an existing
|
1231
|
+
# associated object when assigning a new one, even if the new one isn't saved to database.
|
1201
1232
|
# [build_association(attributes = {})]
|
1202
1233
|
# Returns a new object of the associated type that has been instantiated
|
1203
1234
|
# with +attributes+ and linked to this object through a foreign key, but has not
|
@@ -1286,7 +1317,8 @@ module ActiveRecord
|
|
1286
1317
|
# has_one :club, through: :membership
|
1287
1318
|
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
|
1288
1319
|
def has_one(name, scope = nil, options = {})
|
1289
|
-
Builder::HasOne.build(self, name, scope, options)
|
1320
|
+
reflection = Builder::HasOne.build(self, name, scope, options)
|
1321
|
+
Reflection.add_reflection self, name, reflection
|
1290
1322
|
end
|
1291
1323
|
|
1292
1324
|
# Specifies a one-to-one association with another class. This method should only be used
|
@@ -1357,7 +1389,7 @@ module ActiveRecord
|
|
1357
1389
|
# class is created and decremented when it's destroyed. This requires that a column
|
1358
1390
|
# named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
|
1359
1391
|
# is used on the associate class (such as a Post class) - that is the migration for
|
1360
|
-
# <tt>#{table_name}_count</tt> is created on the associate class (such that Post.comments_count will
|
1392
|
+
# <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
|
1361
1393
|
# return the count cached, see note below). You can also specify a custom counter
|
1362
1394
|
# cache column by providing a column name instead of a +true+/+false+ value to this
|
1363
1395
|
# option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
|
@@ -1390,7 +1422,7 @@ module ActiveRecord
|
|
1390
1422
|
# belongs_to :firm, foreign_key: "client_of"
|
1391
1423
|
# belongs_to :person, primary_key: "name", foreign_key: "person_name"
|
1392
1424
|
# belongs_to :author, class_name: "Person", foreign_key: "author_id"
|
1393
|
-
# belongs_to :valid_coupon, ->(o) { where "discounts >
|
1425
|
+
# belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
|
1394
1426
|
# class_name: "Coupon", foreign_key: "coupon_id"
|
1395
1427
|
# belongs_to :attachable, polymorphic: true
|
1396
1428
|
# belongs_to :project, readonly: true
|
@@ -1398,7 +1430,8 @@ module ActiveRecord
|
|
1398
1430
|
# belongs_to :company, touch: true
|
1399
1431
|
# belongs_to :company, touch: :employees_last_updated_at
|
1400
1432
|
def belongs_to(name, scope = nil, options = {})
|
1401
|
-
Builder::BelongsTo.build(self, name, scope, options)
|
1433
|
+
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1434
|
+
Reflection.add_reflection self, name, reflection
|
1402
1435
|
end
|
1403
1436
|
|
1404
1437
|
# Specifies a many-to-many relationship with another class. This associates two classes via an
|
@@ -1439,7 +1472,7 @@ module ActiveRecord
|
|
1439
1472
|
# [collection<<(object, ...)]
|
1440
1473
|
# Adds one or more objects to the collection by creating associations in the join table
|
1441
1474
|
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
|
1442
|
-
# Note that this operation instantly fires update
|
1475
|
+
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
|
1443
1476
|
# parent object, unless the parent object is a new record.
|
1444
1477
|
# [collection.delete(object, ...)]
|
1445
1478
|
# Removes one or more objects from the collection by removing their associations from the join table.
|
@@ -1462,10 +1495,10 @@ module ActiveRecord
|
|
1462
1495
|
# [collection.find(id)]
|
1463
1496
|
# Finds an associated object responding to the +id+ and that
|
1464
1497
|
# meets the condition that it has to be associated with this object.
|
1465
|
-
# Uses the same rules as ActiveRecord::Base.find
|
1498
|
+
# Uses the same rules as <tt>ActiveRecord::Base.find</tt>.
|
1466
1499
|
# [collection.exists?(...)]
|
1467
1500
|
# Checks whether an associated object with the given conditions exists.
|
1468
|
-
# Uses the same rules as ActiveRecord::Base.exists
|
1501
|
+
# Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
|
1469
1502
|
# [collection.build(attributes = {})]
|
1470
1503
|
# Returns a new object of the collection type that has been instantiated
|
1471
1504
|
# with +attributes+ and linked to this object through the join table, but has not yet been saved.
|
@@ -1535,7 +1568,48 @@ module ActiveRecord
|
|
1535
1568
|
# has_and_belongs_to_many :categories, join_table: "prods_cats"
|
1536
1569
|
# has_and_belongs_to_many :categories, -> { readonly }
|
1537
1570
|
def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
|
1538
|
-
|
1571
|
+
if scope.is_a?(Hash)
|
1572
|
+
options = scope
|
1573
|
+
scope = nil
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
habtm_reflection = ActiveRecord::Reflection::AssociationReflection.new(:has_and_belongs_to_many, name, scope, options, self)
|
1577
|
+
|
1578
|
+
builder = Builder::HasAndBelongsToMany.new name, self, options
|
1579
|
+
|
1580
|
+
join_model = builder.through_model
|
1581
|
+
|
1582
|
+
# FIXME: we should move this to the internal constants. Also people
|
1583
|
+
# should never directly access this constant so I'm not happy about
|
1584
|
+
# setting it.
|
1585
|
+
const_set join_model.name, join_model
|
1586
|
+
|
1587
|
+
middle_reflection = builder.middle_reflection join_model
|
1588
|
+
|
1589
|
+
Builder::HasMany.define_callbacks self, middle_reflection
|
1590
|
+
Reflection.add_reflection self, middle_reflection.name, middle_reflection
|
1591
|
+
middle_reflection.parent_reflection = [name.to_sym, habtm_reflection]
|
1592
|
+
|
1593
|
+
include Module.new {
|
1594
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
1595
|
+
def destroy_associations
|
1596
|
+
association(:#{middle_reflection.name}).delete_all(:delete_all)
|
1597
|
+
association(:#{name}).reset
|
1598
|
+
super
|
1599
|
+
end
|
1600
|
+
RUBY
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
hm_options = {}
|
1604
|
+
hm_options[:through] = middle_reflection.name
|
1605
|
+
hm_options[:source] = join_model.right_reflection.name
|
1606
|
+
|
1607
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table].each do |k|
|
1608
|
+
hm_options[k] = options[k] if options.key? k
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
has_many name, scope, hm_options, &extension
|
1612
|
+
self._reflections[name.to_sym].parent_reflection = [name.to_sym, habtm_reflection]
|
1539
1613
|
end
|
1540
1614
|
end
|
1541
1615
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
+
require 'active_model/forbidden_attributes_protection'
|
1
2
|
|
2
3
|
module ActiveRecord
|
3
4
|
module AttributeAssignment
|
4
5
|
extend ActiveSupport::Concern
|
5
|
-
include ActiveModel::DeprecatedMassAssignmentSecurity
|
6
6
|
include ActiveModel::ForbiddenAttributesProtection
|
7
7
|
|
8
8
|
# Allows you to set all the attributes by passing in a hash of attributes with
|
@@ -12,6 +12,9 @@ module ActiveRecord
|
|
12
12
|
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
|
13
13
|
# exception is raised.
|
14
14
|
def assign_attributes(new_attributes)
|
15
|
+
if !new_attributes.respond_to?(:stringify_keys)
|
16
|
+
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
17
|
+
end
|
15
18
|
return if new_attributes.blank?
|
16
19
|
|
17
20
|
attributes = new_attributes.stringify_keys
|
@@ -40,11 +43,11 @@ module ActiveRecord
|
|
40
43
|
|
41
44
|
def _assign_attribute(k, v)
|
42
45
|
public_send("#{k}=", v)
|
43
|
-
rescue NoMethodError
|
46
|
+
rescue NoMethodError, NameError
|
44
47
|
if respond_to?("#{k}=")
|
45
48
|
raise
|
46
49
|
else
|
47
|
-
raise UnknownAttributeError,
|
50
|
+
raise UnknownAttributeError.new(self, k)
|
48
51
|
end
|
49
52
|
end
|
50
53
|
|
@@ -41,8 +41,9 @@ module ActiveRecord
|
|
41
41
|
# task.read_attribute_before_type_cast('id') # => '1'
|
42
42
|
# task.read_attribute('completed_on') # => Sun, 21 Oct 2012
|
43
43
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
44
|
+
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
44
45
|
def read_attribute_before_type_cast(attr_name)
|
45
|
-
@attributes[attr_name]
|
46
|
+
@attributes[attr_name.to_s]
|
46
47
|
end
|
47
48
|
|
48
49
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -14,24 +14,12 @@ module ActiveRecord
|
|
14
14
|
|
15
15
|
class_attribute :partial_writes, instance_writer: false
|
16
16
|
self.partial_writes = true
|
17
|
-
|
18
|
-
def self.partial_updates=(v); self.partial_writes = v; end
|
19
|
-
def self.partial_updates?; partial_writes?; end
|
20
|
-
def self.partial_updates; partial_writes; end
|
21
|
-
|
22
|
-
ActiveSupport::Deprecation.deprecate_methods(
|
23
|
-
singleton_class,
|
24
|
-
:partial_updates= => :partial_writes=,
|
25
|
-
:partial_updates? => :partial_writes?,
|
26
|
-
:partial_updates => :partial_writes
|
27
|
-
)
|
28
17
|
end
|
29
18
|
|
30
19
|
# Attempts to +save+ the record and clears changed attributes if successful.
|
31
20
|
def save(*)
|
32
21
|
if status = super
|
33
|
-
|
34
|
-
@changed_attributes.clear
|
22
|
+
changes_applied
|
35
23
|
end
|
36
24
|
status
|
37
25
|
end
|
@@ -39,49 +27,70 @@ module ActiveRecord
|
|
39
27
|
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
|
40
28
|
def save!(*)
|
41
29
|
super.tap do
|
42
|
-
|
43
|
-
@changed_attributes.clear
|
30
|
+
changes_applied
|
44
31
|
end
|
45
32
|
end
|
46
33
|
|
47
34
|
# <tt>reload</tt> the record and clears changed attributes.
|
48
35
|
def reload(*)
|
49
36
|
super.tap do
|
50
|
-
|
51
|
-
@changed_attributes.clear
|
37
|
+
reset_changes
|
52
38
|
end
|
53
39
|
end
|
54
40
|
|
41
|
+
def initialize_dup(other) # :nodoc:
|
42
|
+
super
|
43
|
+
init_changed_attributes
|
44
|
+
end
|
45
|
+
|
55
46
|
private
|
47
|
+
def initialize_internals_callback
|
48
|
+
super
|
49
|
+
init_changed_attributes
|
50
|
+
end
|
51
|
+
|
52
|
+
def init_changed_attributes
|
53
|
+
@changed_attributes = nil
|
54
|
+
# Intentionally avoid using #column_defaults since overridden defaults (as is done in
|
55
|
+
# optimistic locking) won't get written unless they get marked as changed
|
56
|
+
self.class.columns.each do |c|
|
57
|
+
attr, orig_value = c.name, c.default
|
58
|
+
changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
56
62
|
# Wrap write_attribute to remember original attribute value.
|
57
63
|
def write_attribute(attr, value)
|
58
64
|
attr = attr.to_s
|
59
65
|
|
66
|
+
save_changed_attribute(attr, value)
|
67
|
+
|
68
|
+
super(attr, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def save_changed_attribute(attr, value)
|
60
72
|
# The attribute already has an unsaved change.
|
61
73
|
if attribute_changed?(attr)
|
62
|
-
old =
|
63
|
-
|
74
|
+
old = changed_attributes[attr]
|
75
|
+
changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
64
76
|
else
|
65
77
|
old = clone_attribute_value(:read_attribute, attr)
|
66
|
-
|
78
|
+
changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
67
79
|
end
|
68
|
-
|
69
|
-
# Carry on.
|
70
|
-
super(attr, value)
|
71
80
|
end
|
72
81
|
|
73
|
-
def
|
82
|
+
def _update_record(*)
|
74
83
|
partial_writes? ? super(keys_for_partial_write) : super
|
75
84
|
end
|
76
85
|
|
77
|
-
def
|
86
|
+
def _create_record(*)
|
78
87
|
partial_writes? ? super(keys_for_partial_write) : super
|
79
88
|
end
|
80
89
|
|
81
90
|
# Serialized attributes should always be written in case they've been
|
82
91
|
# changed in place.
|
83
92
|
def keys_for_partial_write
|
84
|
-
changed
|
93
|
+
changed
|
85
94
|
end
|
86
95
|
|
87
96
|
def _field_changed?(attr, old, value)
|
@@ -15,6 +15,7 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
# Returns the primary key value.
|
17
17
|
def id
|
18
|
+
return unless self.class.primary_key
|
18
19
|
sync_with_transaction_state
|
19
20
|
read_attribute(self.class.primary_key)
|
20
21
|
end
|
@@ -37,6 +38,12 @@ module ActiveRecord
|
|
37
38
|
read_attribute_before_type_cast(self.class.primary_key)
|
38
39
|
end
|
39
40
|
|
41
|
+
# Returns the primary key previous value.
|
42
|
+
def id_was
|
43
|
+
sync_with_transaction_state
|
44
|
+
attribute_was(self.class.primary_key)
|
45
|
+
end
|
46
|
+
|
40
47
|
protected
|
41
48
|
|
42
49
|
def attribute_method?(attr_name)
|
@@ -52,7 +59,7 @@ module ActiveRecord
|
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
55
|
-
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
|
62
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
|
56
63
|
|
57
64
|
def dangerous_attribute_method?(method_name)
|
58
65
|
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
|