activerecord 6.0.0.beta1 → 6.0.1.rc1
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 +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
data/README.rdoc
CHANGED
@@ -13,6 +13,8 @@ columns. Although these mappings can be defined explicitly, it's recommended
|
|
13
13
|
to follow naming conventions, especially when getting started with the
|
14
14
|
library.
|
15
15
|
|
16
|
+
You can read more about Active Record in the {Active Record Basics}[https://edgeguides.rubyonrails.org/active_record_basics.html] guide.
|
17
|
+
|
16
18
|
A short rundown of some of the major features:
|
17
19
|
|
18
20
|
* Automated mapping between classes and tables, attributes and columns.
|
@@ -206,7 +208,7 @@ Active Record is released under the MIT license:
|
|
206
208
|
|
207
209
|
API documentation is at:
|
208
210
|
|
209
|
-
*
|
211
|
+
* https://api.rubyonrails.org
|
210
212
|
|
211
213
|
Bug reports for the Ruby on Rails project can be filed here:
|
212
214
|
|
data/lib/active_record.rb
CHANGED
@@ -55,7 +55,6 @@ module ActiveRecord
|
|
55
55
|
autoload :Persistence
|
56
56
|
autoload :QueryCache
|
57
57
|
autoload :Querying
|
58
|
-
autoload :CollectionCacheKey
|
59
58
|
autoload :ReadonlyAttributes
|
60
59
|
autoload :RecordInvalid, "active_record/validations"
|
61
60
|
autoload :Reflection
|
@@ -74,6 +73,7 @@ module ActiveRecord
|
|
74
73
|
autoload :Translation
|
75
74
|
autoload :Validations
|
76
75
|
autoload :SecureToken
|
76
|
+
autoload :DatabaseSelector, "active_record/middleware/database_selector"
|
77
77
|
|
78
78
|
eager_autoload do
|
79
79
|
autoload :ActiveRecordError, "active_record/errors"
|
@@ -153,6 +153,12 @@ module ActiveRecord
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
+
module Middleware
|
157
|
+
extend ActiveSupport::Autoload
|
158
|
+
|
159
|
+
autoload :DatabaseSelector, "active_record/middleware/database_selector"
|
160
|
+
end
|
161
|
+
|
156
162
|
module Tasks
|
157
163
|
extend ActiveSupport::Autoload
|
158
164
|
|
@@ -15,17 +15,26 @@ module ActiveRecord
|
|
15
15
|
other == records
|
16
16
|
end
|
17
17
|
|
18
|
-
def build(
|
19
|
-
|
18
|
+
def build(attributes = nil, &block)
|
19
|
+
block = _deprecated_scope_block("new", &block)
|
20
|
+
@association.scoping(self) do
|
21
|
+
@association.build(attributes, &block)
|
22
|
+
end
|
20
23
|
end
|
21
24
|
alias new build
|
22
25
|
|
23
|
-
def create(
|
24
|
-
|
26
|
+
def create(attributes = nil, &block)
|
27
|
+
block = _deprecated_scope_block("create", &block)
|
28
|
+
@association.scoping(self) do
|
29
|
+
@association.create(attributes, &block)
|
30
|
+
end
|
25
31
|
end
|
26
32
|
|
27
|
-
def create!(
|
28
|
-
|
33
|
+
def create!(attributes = nil, &block)
|
34
|
+
block = _deprecated_scope_block("create!", &block)
|
35
|
+
@association.scoping(self) do
|
36
|
+
@association.create!(attributes, &block)
|
37
|
+
end
|
29
38
|
end
|
30
39
|
|
31
40
|
private
|
@@ -92,7 +92,7 @@ module ActiveRecord
|
|
92
92
|
through_reflection = reflection.through_reflection
|
93
93
|
source_reflection_names = reflection.source_reflection_names
|
94
94
|
source_associations = reflection.through_reflection.klass._reflections.keys
|
95
|
-
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or '
|
95
|
+
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
|
96
96
|
else
|
97
97
|
super("Could not find the source association(s).")
|
98
98
|
end
|
@@ -703,8 +703,9 @@ module ActiveRecord
|
|
703
703
|
# #belongs_to associations.
|
704
704
|
#
|
705
705
|
# Extra options on the associations, as defined in the
|
706
|
-
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt>
|
707
|
-
# also prevent the association's inverse
|
706
|
+
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt>
|
707
|
+
# constant, or a custom scope, will also prevent the association's inverse
|
708
|
+
# from being found automatically.
|
708
709
|
#
|
709
710
|
# The automatic guessing of the inverse association uses a heuristic based
|
710
711
|
# on the name of the class, so it may not work for all associations,
|
@@ -17,6 +17,23 @@ module ActiveRecord
|
|
17
17
|
# CollectionAssociation
|
18
18
|
# HasManyAssociation + ForeignAssociation
|
19
19
|
# HasManyThroughAssociation + ThroughAssociation
|
20
|
+
#
|
21
|
+
# Associations in Active Record are middlemen between the object that
|
22
|
+
# holds the association, known as the <tt>owner</tt>, and the associated
|
23
|
+
# result set, known as the <tt>target</tt>. Association metadata is available in
|
24
|
+
# <tt>reflection</tt>, which is an instance of <tt>ActiveRecord::Reflection::AssociationReflection</tt>.
|
25
|
+
#
|
26
|
+
# For example, given
|
27
|
+
#
|
28
|
+
# class Blog < ActiveRecord::Base
|
29
|
+
# has_many :posts
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# blog = Blog.first
|
33
|
+
#
|
34
|
+
# The association of <tt>blog.posts</tt> has the object +blog+ as its
|
35
|
+
# <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
|
36
|
+
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
20
37
|
class Association #:nodoc:
|
21
38
|
attr_reader :owner, :target, :reflection
|
22
39
|
|
@@ -26,6 +43,7 @@ module ActiveRecord
|
|
26
43
|
reflection.check_validity!
|
27
44
|
|
28
45
|
@owner, @reflection = owner, reflection
|
46
|
+
@_scope = nil
|
29
47
|
|
30
48
|
reset
|
31
49
|
reset_scope
|
@@ -78,7 +96,7 @@ module ActiveRecord
|
|
78
96
|
end
|
79
97
|
|
80
98
|
def scope
|
81
|
-
target_scope.merge!(association_scope)
|
99
|
+
@_scope&.spawn || target_scope.merge!(association_scope)
|
82
100
|
end
|
83
101
|
|
84
102
|
def reset_scope
|
@@ -178,6 +196,13 @@ module ActiveRecord
|
|
178
196
|
_create_record(attributes, true, &block)
|
179
197
|
end
|
180
198
|
|
199
|
+
def scoping(relation, &block)
|
200
|
+
@_scope = relation
|
201
|
+
relation.scoping(&block)
|
202
|
+
ensure
|
203
|
+
@_scope = nil
|
204
|
+
end
|
205
|
+
|
181
206
|
private
|
182
207
|
def find_target
|
183
208
|
scope = self.scope
|
@@ -208,7 +233,7 @@ module ActiveRecord
|
|
208
233
|
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
209
234
|
# through association's scope)
|
210
235
|
def target_scope
|
211
|
-
AssociationRelation.create(klass, self).merge!(klass.
|
236
|
+
AssociationRelation.create(klass, self).merge!(klass.scope_for_association)
|
212
237
|
end
|
213
238
|
|
214
239
|
def scope_for_create
|
@@ -27,40 +27,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
27
27
|
"Please choose a different association name."
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
reflection = create_reflection model, name, scope, options, extension
|
30
|
+
reflection = create_reflection(model, name, scope, options, &block)
|
32
31
|
define_accessors model, reflection
|
33
32
|
define_callbacks model, reflection
|
34
33
|
define_validations model, reflection
|
35
34
|
reflection
|
36
35
|
end
|
37
36
|
|
38
|
-
def self.create_reflection(model, name, scope, options,
|
37
|
+
def self.create_reflection(model, name, scope, options, &block)
|
39
38
|
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
|
40
39
|
|
41
40
|
validate_options(options)
|
42
41
|
|
43
|
-
|
42
|
+
extension = define_extensions(model, name, &block)
|
43
|
+
options[:extend] = [*options[:extend], extension] if extension
|
44
|
+
|
45
|
+
scope = build_scope(scope)
|
44
46
|
|
45
47
|
ActiveRecord::Reflection.create(macro, name, scope, options, model)
|
46
48
|
end
|
47
49
|
|
48
|
-
def self.build_scope(scope
|
49
|
-
new_scope = scope
|
50
|
-
|
50
|
+
def self.build_scope(scope)
|
51
51
|
if scope && scope.arity == 0
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
if extension
|
56
|
-
new_scope = wrap_scope new_scope, extension
|
52
|
+
proc { instance_exec(&scope) }
|
53
|
+
else
|
54
|
+
scope
|
57
55
|
end
|
58
|
-
|
59
|
-
new_scope
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.wrap_scope(scope, extension)
|
63
|
-
scope
|
64
56
|
end
|
65
57
|
|
66
58
|
def self.macro
|
@@ -136,5 +128,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
136
128
|
name = reflection.name
|
137
129
|
model.before_destroy lambda { |o| o.association(name).handle_dependency }
|
138
130
|
end
|
131
|
+
|
132
|
+
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
133
|
+
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
134
|
+
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
|
139
135
|
end
|
140
136
|
end
|
@@ -74,11 +74,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
74
74
|
|
75
75
|
def self.add_touch_callbacks(model, reflection)
|
76
76
|
foreign_key = reflection.foreign_key
|
77
|
-
|
77
|
+
name = reflection.name
|
78
78
|
touch = reflection.options[:touch]
|
79
79
|
|
80
80
|
callback = lambda { |changes_method| lambda { |record|
|
81
|
-
BelongsTo.touch_record(record, record.send(changes_method), foreign_key,
|
81
|
+
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch, belongs_to_touch_method)
|
82
82
|
}}
|
83
83
|
|
84
84
|
if reflection.counter_cache_column
|
@@ -123,5 +123,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
123
123
|
model.validates_presence_of reflection.name, message: :required
|
124
124
|
end
|
125
125
|
end
|
126
|
+
|
127
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks, :define_validations,
|
128
|
+
:add_counter_cache_callbacks, :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
|
126
129
|
end
|
127
130
|
end
|
@@ -20,11 +20,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
-
def self.define_extensions(model, name)
|
23
|
+
def self.define_extensions(model, name, &block)
|
24
24
|
if block_given?
|
25
|
-
extension_module_name = "#{
|
26
|
-
extension = Module.new(&
|
27
|
-
model.
|
25
|
+
extension_module_name = "#{name.to_s.camelize}AssociationExtension"
|
26
|
+
extension = Module.new(&block)
|
27
|
+
model.const_set(extension_module_name, extension)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -67,16 +67,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
67
67
|
CODE
|
68
68
|
end
|
69
69
|
|
70
|
-
|
71
|
-
if scope
|
72
|
-
if scope.arity > 0
|
73
|
-
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
74
|
-
else
|
75
|
-
proc { instance_exec(&scope).extending(mod) }
|
76
|
-
end
|
77
|
-
else
|
78
|
-
proc { extending(mod) }
|
79
|
-
end
|
80
|
-
end
|
70
|
+
private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
|
81
71
|
end
|
82
72
|
end
|
@@ -63,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
63
63
|
|
64
64
|
def middle_reflection(join_model)
|
65
65
|
middle_name = [lhs_model.name.downcase.pluralize,
|
66
|
-
association_name].join("_").gsub("::", "_").to_sym
|
66
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
67
67
|
middle_options = middle_options join_model
|
68
68
|
|
69
69
|
HasMany.create_reflection(lhs_model,
|
@@ -13,5 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
13
13
|
def self.valid_dependent_options
|
14
14
|
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
15
|
end
|
16
|
+
|
17
|
+
private_class_method :macro, :valid_options, :valid_dependent_options
|
16
18
|
end
|
17
19
|
end
|
@@ -7,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:as]
|
10
|
+
valid = super + [:as, :touch]
|
11
11
|
valid += [:through, :source, :source_type] if options[:through]
|
12
12
|
valid
|
13
13
|
end
|
@@ -16,6 +16,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
16
16
|
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.define_callbacks(model, reflection)
|
20
|
+
super
|
21
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
22
|
+
end
|
23
|
+
|
19
24
|
def self.add_destroy_callbacks(model, reflection)
|
20
25
|
super unless reflection.options[:through]
|
21
26
|
end
|
@@ -26,5 +31,34 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
26
31
|
model.validates_presence_of reflection.name, message: :required
|
27
32
|
end
|
28
33
|
end
|
34
|
+
|
35
|
+
def self.touch_record(o, name, touch)
|
36
|
+
record = o.send name
|
37
|
+
|
38
|
+
return unless record && record.persisted?
|
39
|
+
|
40
|
+
if touch != true
|
41
|
+
record.touch(touch)
|
42
|
+
else
|
43
|
+
record.touch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.add_touch_callbacks(model, reflection)
|
48
|
+
name = reflection.name
|
49
|
+
touch = reflection.options[:touch]
|
50
|
+
|
51
|
+
callback = lambda { |record|
|
52
|
+
HasOne.touch_record(record, name, touch)
|
53
|
+
}
|
54
|
+
|
55
|
+
model.after_create callback, if: :saved_changes?
|
56
|
+
model.after_update callback, if: :saved_changes?
|
57
|
+
model.after_destroy callback
|
58
|
+
model.after_touch callback
|
59
|
+
end
|
60
|
+
|
61
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
|
62
|
+
:define_callbacks, :define_validations, :add_touch_callbacks
|
29
63
|
end
|
30
64
|
end
|
@@ -109,9 +109,8 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
# Add +records+ to this association.
|
113
|
-
#
|
114
|
-
# +push+ and +concat+ behave identically.
|
112
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
113
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
115
114
|
def concat(*records)
|
116
115
|
records = records.flatten
|
117
116
|
if owner.new_record?
|
@@ -233,7 +232,7 @@ module ActiveRecord
|
|
233
232
|
# loaded and you are going to fetch the records anyway it is better to
|
234
233
|
# check <tt>collection.length.zero?</tt>.
|
235
234
|
def empty?
|
236
|
-
if loaded? || @association_ids
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
237
236
|
size.zero?
|
238
237
|
else
|
239
238
|
target.empty? && !scope.exists?
|
@@ -347,7 +346,6 @@ module ActiveRecord
|
|
347
346
|
add_to_target(record) do
|
348
347
|
result = insert_record(record, true, raise) {
|
349
348
|
@_was_loaded = loaded?
|
350
|
-
@association_ids = nil
|
351
349
|
}
|
352
350
|
end
|
353
351
|
raise ActiveRecord::Rollback unless result
|
@@ -384,6 +382,7 @@ module ActiveRecord
|
|
384
382
|
|
385
383
|
delete_records(existing_records, method) if existing_records.any?
|
386
384
|
@target -= records
|
385
|
+
@association_ids = nil
|
387
386
|
|
388
387
|
records.each { |record| callback(:after_remove, record) }
|
389
388
|
end
|
@@ -424,7 +423,6 @@ module ActiveRecord
|
|
424
423
|
unless owner.new_record?
|
425
424
|
result &&= insert_record(record, true, raise) {
|
426
425
|
@_was_loaded = loaded?
|
427
|
-
@association_ids = nil
|
428
426
|
}
|
429
427
|
end
|
430
428
|
end
|
@@ -447,6 +445,7 @@ module ActiveRecord
|
|
447
445
|
if index
|
448
446
|
target[index] = record
|
449
447
|
elsif @_was_loaded || !loaded?
|
448
|
+
@association_ids = nil
|
450
449
|
target << record
|
451
450
|
end
|
452
451
|
|
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# object, known as the <tt>@target</tt>. The kind of association any proxy is
|
8
|
-
# about is available in <tt>@reflection</tt>. That's an instance of the class
|
9
|
-
# ActiveRecord::Reflection::AssociationReflection.
|
5
|
+
# Collection proxies in Active Record are middlemen between an
|
6
|
+
# <tt>association</tt>, and its <tt>target</tt> result set.
|
10
7
|
#
|
11
8
|
# For example, given
|
12
9
|
#
|
@@ -16,14 +13,14 @@ module ActiveRecord
|
|
16
13
|
#
|
17
14
|
# blog = Blog.first
|
18
15
|
#
|
19
|
-
#
|
20
|
-
# <tt
|
21
|
-
#
|
16
|
+
# The collection proxy returned by <tt>blog.posts</tt> is built from a
|
17
|
+
# <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
|
18
|
+
# of posts as the <tt>target</tt>.
|
22
19
|
#
|
23
|
-
# This class delegates unknown methods to <tt
|
24
|
-
#
|
20
|
+
# This class delegates unknown methods to the <tt>association</tt>'s
|
21
|
+
# relation class via a delegate cache.
|
25
22
|
#
|
26
|
-
# The <tt
|
23
|
+
# The <tt>target</tt> result set is not loaded until needed. For example,
|
27
24
|
#
|
28
25
|
# blog.posts.count
|
29
26
|
#
|
@@ -366,34 +363,6 @@ module ActiveRecord
|
|
366
363
|
@association.create!(attributes, &block)
|
367
364
|
end
|
368
365
|
|
369
|
-
# Add one or more records to the collection by setting their foreign keys
|
370
|
-
# to the association's primary key. Since #<< flattens its argument list and
|
371
|
-
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
372
|
-
# so method calls may be chained.
|
373
|
-
#
|
374
|
-
# class Person < ActiveRecord::Base
|
375
|
-
# has_many :pets
|
376
|
-
# end
|
377
|
-
#
|
378
|
-
# person.pets.size # => 0
|
379
|
-
# person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
|
380
|
-
# person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
|
381
|
-
# person.pets.size # => 3
|
382
|
-
#
|
383
|
-
# person.id # => 1
|
384
|
-
# person.pets
|
385
|
-
# # => [
|
386
|
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
387
|
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
388
|
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
389
|
-
# # ]
|
390
|
-
#
|
391
|
-
# person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
|
392
|
-
# person.pets.size # => 5
|
393
|
-
def concat(*records)
|
394
|
-
@association.concat(*records)
|
395
|
-
end
|
396
|
-
|
397
366
|
# Replaces this collection with +other_array+. This will perform a diff
|
398
367
|
# and delete/add only records that have changed.
|
399
368
|
#
|
@@ -1033,8 +1002,9 @@ module ActiveRecord
|
|
1033
1002
|
end
|
1034
1003
|
|
1035
1004
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
1036
|
-
# to the association's primary key.
|
1037
|
-
#
|
1005
|
+
# to the association's primary key. Since <tt><<</tt> flattens its argument list and
|
1006
|
+
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
|
1007
|
+
# so several appends may be chained together.
|
1038
1008
|
#
|
1039
1009
|
# class Person < ActiveRecord::Base
|
1040
1010
|
# has_many :pets
|
@@ -1057,8 +1027,9 @@ module ActiveRecord
|
|
1057
1027
|
end
|
1058
1028
|
alias_method :push, :<<
|
1059
1029
|
alias_method :append, :<<
|
1030
|
+
alias_method :concat, :<<
|
1060
1031
|
|
1061
|
-
def prepend(*args)
|
1032
|
+
def prepend(*args) # :nodoc:
|
1062
1033
|
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
1063
1034
|
end
|
1064
1035
|
|