activerecord 5.2.8 → 6.0.0.beta1
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 +299 -788
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +35 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +15 -16
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +74 -0
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/active_record.rb +2 -1
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +63 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +67 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +44 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
Copyright (c) 2004-
|
1
|
+
Copyright (c) 2004-2019 David Heinemeier Hansson
|
2
|
+
|
3
|
+
Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
|
2
4
|
|
3
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
6
|
a copy of this software and associated documentation files (the
|
data/README.rdoc
CHANGED
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
192
192
|
|
193
193
|
Source code can be downloaded as part of the Rails project on GitHub:
|
194
194
|
|
195
|
-
* https://github.com/rails/rails/tree/
|
195
|
+
* https://github.com/rails/rails/tree/master/activerecord
|
196
196
|
|
197
197
|
|
198
198
|
== License
|
data/examples/performance.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
# See ActiveRecord::Aggregations::ClassMethods for documentation
|
5
5
|
module Aggregations
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
6
|
def initialize_dup(*) # :nodoc:
|
9
7
|
@aggregation_cache = {}
|
10
8
|
super
|
@@ -225,6 +223,10 @@ module ActiveRecord
|
|
225
223
|
def composed_of(part_id, options = {})
|
226
224
|
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
|
227
225
|
|
226
|
+
unless self < Aggregations
|
227
|
+
include Aggregations
|
228
|
+
end
|
229
|
+
|
228
230
|
name = part_id.id2name
|
229
231
|
class_name = options[:class_name] || name.camelize
|
230
232
|
mapping = options[:mapping] || [ name, name ]
|
@@ -40,7 +40,9 @@ module ActiveRecord
|
|
40
40
|
end
|
41
41
|
|
42
42
|
# Reloads the \target and returns +self+ on success.
|
43
|
-
|
43
|
+
# The QueryCache is cleared if +force+ is true.
|
44
|
+
def reload(force = false)
|
45
|
+
klass.connection.clear_query_cache if force && klass
|
44
46
|
reset
|
45
47
|
reset_scope
|
46
48
|
load_target
|
@@ -79,18 +81,6 @@ module ActiveRecord
|
|
79
81
|
target_scope.merge!(association_scope)
|
80
82
|
end
|
81
83
|
|
82
|
-
# The scope for this association.
|
83
|
-
#
|
84
|
-
# Note that the association_scope is merged into the target_scope only when the
|
85
|
-
# scope method is called. This is because at that point the call may be surrounded
|
86
|
-
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
|
87
|
-
# actually gets built.
|
88
|
-
def association_scope
|
89
|
-
if klass
|
90
|
-
@association_scope ||= AssociationScope.scope(self)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
84
|
def reset_scope
|
95
85
|
@association_scope = nil
|
96
86
|
end
|
@@ -129,12 +119,6 @@ module ActiveRecord
|
|
129
119
|
reflection.klass
|
130
120
|
end
|
131
121
|
|
132
|
-
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
133
|
-
# through association's scope)
|
134
|
-
def target_scope
|
135
|
-
AssociationRelation.create(klass, self).merge!(klass.all)
|
136
|
-
end
|
137
|
-
|
138
122
|
def extensions
|
139
123
|
extensions = klass.default_extensions | reflection.extensions
|
140
124
|
|
@@ -195,6 +179,38 @@ module ActiveRecord
|
|
195
179
|
end
|
196
180
|
|
197
181
|
private
|
182
|
+
def find_target
|
183
|
+
scope = self.scope
|
184
|
+
return scope.to_a if skip_statement_cache?(scope)
|
185
|
+
|
186
|
+
conn = klass.connection
|
187
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
188
|
+
as = AssociationScope.create { params.bind }
|
189
|
+
target_scope.merge!(as.scope(self))
|
190
|
+
end
|
191
|
+
|
192
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
193
|
+
sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
|
194
|
+
end
|
195
|
+
|
196
|
+
# The scope for this association.
|
197
|
+
#
|
198
|
+
# Note that the association_scope is merged into the target_scope only when the
|
199
|
+
# scope method is called. This is because at that point the call may be surrounded
|
200
|
+
# by scope.scoping { ... } or unscoped { ... } etc, which affects the scope which
|
201
|
+
# actually gets built.
|
202
|
+
def association_scope
|
203
|
+
if klass
|
204
|
+
@association_scope ||= AssociationScope.scope(self)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
209
|
+
# through association's scope)
|
210
|
+
def target_scope
|
211
|
+
AssociationRelation.create(klass, self).merge!(klass.all)
|
212
|
+
end
|
213
|
+
|
198
214
|
def scope_for_create
|
199
215
|
scope.scope_for_create
|
200
216
|
end
|
@@ -26,7 +26,9 @@ module ActiveRecord
|
|
26
26
|
chain = get_chain(reflection, association, scope.alias_tracker)
|
27
27
|
|
28
28
|
scope.extending! reflection.extensions
|
29
|
-
add_constraints(scope, owner, chain)
|
29
|
+
scope = add_constraints(scope, owner, chain)
|
30
|
+
scope.limit!(1) unless reflection.collection?
|
31
|
+
scope
|
30
32
|
end
|
31
33
|
|
32
34
|
def self.get_bind_values(owner, chain)
|
@@ -46,13 +48,9 @@ module ActiveRecord
|
|
46
48
|
binds
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
51
|
-
protected
|
52
|
-
|
51
|
+
private
|
53
52
|
attr_reader :value_transformation
|
54
53
|
|
55
|
-
private
|
56
54
|
def join(table, constraint)
|
57
55
|
table.create_join(table, table.create_on(constraint))
|
58
56
|
end
|
@@ -16,21 +16,6 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def replace(record)
|
20
|
-
if record
|
21
|
-
raise_on_type_mismatch!(record)
|
22
|
-
update_counters_on_replace(record)
|
23
|
-
set_inverse_instance(record)
|
24
|
-
@updated = true
|
25
|
-
else
|
26
|
-
decrement_counters
|
27
|
-
end
|
28
|
-
|
29
|
-
replace_keys(record)
|
30
|
-
|
31
|
-
self.target = record
|
32
|
-
end
|
33
|
-
|
34
19
|
def inversed_from(record)
|
35
20
|
replace_keys(record)
|
36
21
|
super
|
@@ -49,30 +34,60 @@ module ActiveRecord
|
|
49
34
|
@updated
|
50
35
|
end
|
51
36
|
|
52
|
-
def decrement_counters
|
37
|
+
def decrement_counters
|
53
38
|
update_counters(-1)
|
54
39
|
end
|
55
40
|
|
56
|
-
def increment_counters
|
41
|
+
def increment_counters
|
57
42
|
update_counters(1)
|
58
43
|
end
|
59
44
|
|
45
|
+
def decrement_counters_before_last_save
|
46
|
+
if reflection.polymorphic?
|
47
|
+
model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
|
48
|
+
else
|
49
|
+
model_was = klass
|
50
|
+
end
|
51
|
+
|
52
|
+
foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
|
53
|
+
|
54
|
+
if foreign_key_was && model_was < ActiveRecord::Base
|
55
|
+
update_counters_via_scope(model_was, foreign_key_was, -1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
60
59
|
def target_changed?
|
61
60
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
62
61
|
end
|
63
62
|
|
64
63
|
private
|
64
|
+
def replace(record)
|
65
|
+
if record
|
66
|
+
raise_on_type_mismatch!(record)
|
67
|
+
set_inverse_instance(record)
|
68
|
+
@updated = true
|
69
|
+
end
|
70
|
+
|
71
|
+
replace_keys(record)
|
72
|
+
|
73
|
+
self.target = record
|
74
|
+
end
|
65
75
|
|
66
76
|
def update_counters(by)
|
67
77
|
if require_counter_update? && foreign_key_present?
|
68
78
|
if target && !stale_target?
|
69
79
|
target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
|
70
80
|
else
|
71
|
-
klass.
|
81
|
+
update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
|
72
82
|
end
|
73
83
|
end
|
74
84
|
end
|
75
85
|
|
86
|
+
def update_counters_via_scope(klass, foreign_key, by)
|
87
|
+
scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
|
88
|
+
scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
|
89
|
+
end
|
90
|
+
|
76
91
|
def find_target?
|
77
92
|
!loaded? && foreign_key_present? && klass
|
78
93
|
end
|
@@ -81,25 +96,12 @@ module ActiveRecord
|
|
81
96
|
reflection.counter_cache_column && owner.persisted?
|
82
97
|
end
|
83
98
|
|
84
|
-
def update_counters_on_replace(record)
|
85
|
-
if require_counter_update? && different_target?(record)
|
86
|
-
owner.instance_variable_set :@_after_replace_counter_called, true
|
87
|
-
record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
|
88
|
-
decrement_counters
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Checks whether record is different to the current target, without loading it
|
93
|
-
def different_target?(record)
|
94
|
-
record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
|
95
|
-
end
|
96
|
-
|
97
99
|
def replace_keys(record)
|
98
|
-
owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
|
100
|
+
owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
|
99
101
|
end
|
100
102
|
|
101
|
-
def primary_key(
|
102
|
-
reflection.association_primary_key(
|
103
|
+
def primary_key(klass)
|
104
|
+
reflection.association_primary_key(klass)
|
103
105
|
end
|
104
106
|
|
105
107
|
def foreign_key_present?
|
@@ -113,14 +115,6 @@ module ActiveRecord
|
|
113
115
|
inverse && inverse.has_one?
|
114
116
|
end
|
115
117
|
|
116
|
-
def target_id
|
117
|
-
if options[:primary_key]
|
118
|
-
owner.send(reflection.name).try(:id)
|
119
|
-
else
|
120
|
-
owner._read_attribute(reflection.foreign_key)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
118
|
def stale_state
|
125
119
|
result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
|
126
120
|
result && result.to_s
|
@@ -19,10 +19,6 @@ module ActiveRecord
|
|
19
19
|
owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
|
20
20
|
end
|
21
21
|
|
22
|
-
def different_target?(record)
|
23
|
-
super || record.class != klass
|
24
|
-
end
|
25
|
-
|
26
22
|
def inverse_reflection_for(record)
|
27
23
|
reflection.polymorphic_inverse_of(record.class)
|
28
24
|
end
|
@@ -21,58 +21,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
21
21
|
add_default_callbacks(model, reflection) if reflection.options[:default]
|
22
22
|
end
|
23
23
|
|
24
|
-
def self.define_accessors(mixin, reflection)
|
25
|
-
super
|
26
|
-
add_counter_cache_methods mixin
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.add_counter_cache_methods(mixin)
|
30
|
-
return if mixin.method_defined? :belongs_to_counter_cache_after_update
|
31
|
-
|
32
|
-
mixin.class_eval do
|
33
|
-
def belongs_to_counter_cache_after_update(reflection)
|
34
|
-
foreign_key = reflection.foreign_key
|
35
|
-
cache_column = reflection.counter_cache_column
|
36
|
-
|
37
|
-
if (@_after_replace_counter_called ||= false)
|
38
|
-
@_after_replace_counter_called = false
|
39
|
-
elsif association(reflection.name).target_changed?
|
40
|
-
if reflection.polymorphic?
|
41
|
-
model = attribute_in_database(reflection.foreign_type).try(:constantize)
|
42
|
-
model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
|
43
|
-
else
|
44
|
-
model = reflection.klass
|
45
|
-
model_was = reflection.klass
|
46
|
-
end
|
47
|
-
|
48
|
-
foreign_key_was = attribute_before_last_save foreign_key
|
49
|
-
foreign_key = attribute_in_database foreign_key
|
50
|
-
|
51
|
-
if foreign_key && model.respond_to?(:increment_counter)
|
52
|
-
foreign_key = counter_cache_target(reflection, model, foreign_key)
|
53
|
-
model.increment_counter(cache_column, foreign_key)
|
54
|
-
end
|
55
|
-
|
56
|
-
if foreign_key_was && model_was.respond_to?(:decrement_counter)
|
57
|
-
foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
|
58
|
-
model_was.decrement_counter(cache_column, foreign_key_was)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
def counter_cache_target(reflection, model, foreign_key)
|
65
|
-
primary_key = reflection.association_primary_key(model)
|
66
|
-
model.unscoped.where!(primary_key => foreign_key)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
24
|
def self.add_counter_cache_callbacks(model, reflection)
|
72
25
|
cache_column = reflection.counter_cache_column
|
73
26
|
|
74
27
|
model.after_update lambda { |record|
|
75
|
-
|
28
|
+
association = association(reflection.name)
|
29
|
+
|
30
|
+
if association.target_changed?
|
31
|
+
association.increment_counters
|
32
|
+
association.decrement_counters_before_last_save
|
33
|
+
end
|
76
34
|
}
|
77
35
|
|
78
36
|
klass = reflection.class_name.safe_constantize
|
@@ -123,12 +81,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
123
81
|
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
|
124
82
|
}}
|
125
83
|
|
126
|
-
|
84
|
+
if reflection.counter_cache_column
|
85
|
+
touch_callback = callback.(:saved_changes)
|
86
|
+
update_callback = lambda { |record|
|
87
|
+
instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
|
88
|
+
}
|
89
|
+
model.after_update update_callback, if: :saved_changes?
|
90
|
+
else
|
127
91
|
model.after_create callback.(:saved_changes), if: :saved_changes?
|
92
|
+
model.after_update callback.(:saved_changes), if: :saved_changes?
|
128
93
|
model.after_destroy callback.(:changes_to_save)
|
129
94
|
end
|
130
95
|
|
131
|
-
model.after_update callback.(:saved_changes), if: :saved_changes?
|
132
96
|
model.after_touch callback.(:changes_to_save)
|
133
97
|
end
|
134
98
|
|
@@ -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)
|
24
24
|
if block_given?
|
25
25
|
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
|
26
|
-
extension = Module.new(&
|
27
|
-
model.
|
26
|
+
extension = Module.new(&Proc.new)
|
27
|
+
model.module_parent.const_set(extension_module_name, extension)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -2,39 +2,6 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
4
|
class HasAndBelongsToMany # :nodoc:
|
5
|
-
class JoinTableResolver # :nodoc:
|
6
|
-
KnownTable = Struct.new :join_table
|
7
|
-
|
8
|
-
class KnownClass # :nodoc:
|
9
|
-
def initialize(lhs_class, rhs_class_name)
|
10
|
-
@lhs_class = lhs_class
|
11
|
-
@rhs_class_name = rhs_class_name
|
12
|
-
@join_table = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def join_table
|
16
|
-
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def klass
|
22
|
-
@lhs_class.send(:compute_type, @rhs_class_name)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.build(lhs_class, name, options)
|
27
|
-
if options[:join_table]
|
28
|
-
KnownTable.new options[:join_table].to_s
|
29
|
-
else
|
30
|
-
class_name = options.fetch(:class_name) {
|
31
|
-
name.to_s.camelize.singularize
|
32
|
-
}
|
33
|
-
KnownClass.new lhs_class, class_name.to_s
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
5
|
attr_reader :lhs_model, :association_name, :options
|
39
6
|
|
40
7
|
def initialize(association_name, lhs_model, options)
|
@@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
44
11
|
end
|
45
12
|
|
46
13
|
def through_model
|
47
|
-
habtm = JoinTableResolver.build lhs_model, association_name, options
|
48
|
-
|
49
14
|
join_model = Class.new(ActiveRecord::Base) {
|
50
15
|
class << self
|
51
16
|
attr_accessor :left_model
|
@@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
56
21
|
end
|
57
22
|
|
58
23
|
def self.table_name
|
59
|
-
|
24
|
+
# Table name needs to be resolved lazily
|
25
|
+
# because RHS class might not have been loaded
|
26
|
+
@table_name ||= table_name_resolver.call
|
60
27
|
end
|
61
28
|
|
62
29
|
def self.compute_type(class_name)
|
@@ -86,7 +53,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
86
53
|
}
|
87
54
|
|
88
55
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
89
|
-
join_model.table_name_resolver =
|
56
|
+
join_model.table_name_resolver = -> { table_name }
|
90
57
|
join_model.left_model = lhs_model
|
91
58
|
|
92
59
|
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
@@ -96,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
96
63
|
|
97
64
|
def middle_reflection(join_model)
|
98
65
|
middle_name = [lhs_model.name.downcase.pluralize,
|
99
|
-
association_name].join("_"
|
66
|
+
association_name].join("_").gsub("::", "_").to_sym
|
100
67
|
middle_options = middle_options join_model
|
101
68
|
|
102
69
|
HasMany.create_reflection(lhs_model,
|
@@ -117,6 +84,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
117
84
|
middle_options
|
118
85
|
end
|
119
86
|
|
87
|
+
def table_name
|
88
|
+
if options[:join_table]
|
89
|
+
options[:join_table].to_s
|
90
|
+
else
|
91
|
+
class_name = options.fetch(:class_name) {
|
92
|
+
association_name.to_s.camelize.singularize
|
93
|
+
}
|
94
|
+
klass = lhs_model.send(:compute_type, class_name.to_s)
|
95
|
+
[lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
120
99
|
def belongs_to_options(options)
|
121
100
|
rhs_options = {}
|
122
101
|
|
@@ -109,8 +109,9 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
# Add +records+ to this association.
|
113
|
-
#
|
112
|
+
# Add +records+ to this association. Returns +self+ so method calls may
|
113
|
+
# be chained. Since << flattens its argument list and inserts each record,
|
114
|
+
# +push+ and +concat+ behave identically.
|
114
115
|
def concat(*records)
|
115
116
|
records = records.flatten
|
116
117
|
if owner.new_record?
|
@@ -211,9 +212,11 @@ module ActiveRecord
|
|
211
212
|
def size
|
212
213
|
if !find_target? || loaded?
|
213
214
|
target.size
|
215
|
+
elsif @association_ids
|
216
|
+
@association_ids.size
|
214
217
|
elsif !association_scope.group_values.empty?
|
215
218
|
load_target.size
|
216
|
-
elsif !association_scope.distinct_value && target.
|
219
|
+
elsif !association_scope.distinct_value && !target.empty?
|
217
220
|
unsaved_records = target.select(&:new_record?)
|
218
221
|
unsaved_records.size + count_records
|
219
222
|
else
|
@@ -230,10 +233,10 @@ module ActiveRecord
|
|
230
233
|
# loaded and you are going to fetch the records anyway it is better to
|
231
234
|
# check <tt>collection.length.zero?</tt>.
|
232
235
|
def empty?
|
233
|
-
if loaded?
|
236
|
+
if loaded? || @association_ids
|
234
237
|
size.zero?
|
235
238
|
else
|
236
|
-
|
239
|
+
target.empty? && !scope.exists?
|
237
240
|
end
|
238
241
|
end
|
239
242
|
|
@@ -300,23 +303,6 @@ module ActiveRecord
|
|
300
303
|
end
|
301
304
|
|
302
305
|
private
|
303
|
-
|
304
|
-
def find_target
|
305
|
-
scope = self.scope
|
306
|
-
return scope.to_a if skip_statement_cache?(scope)
|
307
|
-
|
308
|
-
conn = klass.connection
|
309
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
310
|
-
as = AssociationScope.create { params.bind }
|
311
|
-
target_scope.merge!(as.scope(self))
|
312
|
-
end
|
313
|
-
|
314
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
-
sc.execute(binds, conn) do |record|
|
316
|
-
set_inverse_instance(record)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
306
|
# We have some records loaded from the database (persisted) and some that are
|
321
307
|
# in-memory (memory). The same record may be represented in the persisted array
|
322
308
|
# and in the memory array.
|
@@ -361,6 +347,7 @@ module ActiveRecord
|
|
361
347
|
add_to_target(record) do
|
362
348
|
result = insert_record(record, true, raise) {
|
363
349
|
@_was_loaded = loaded?
|
350
|
+
@association_ids = nil
|
364
351
|
}
|
365
352
|
end
|
366
353
|
raise ActiveRecord::Rollback unless result
|
@@ -396,8 +383,7 @@ module ActiveRecord
|
|
396
383
|
records.each { |record| callback(:before_remove, record) }
|
397
384
|
|
398
385
|
delete_records(existing_records, method) if existing_records.any?
|
399
|
-
|
400
|
-
@association_ids = nil
|
386
|
+
@target -= records
|
401
387
|
|
402
388
|
records.each { |record| callback(:after_remove, record) }
|
403
389
|
end
|
@@ -438,6 +424,7 @@ module ActiveRecord
|
|
438
424
|
unless owner.new_record?
|
439
425
|
result &&= insert_record(record, true, raise) {
|
440
426
|
@_was_loaded = loaded?
|
427
|
+
@association_ids = nil
|
441
428
|
}
|
442
429
|
end
|
443
430
|
end
|
@@ -460,7 +447,6 @@ module ActiveRecord
|
|
460
447
|
if index
|
461
448
|
target[index] = record
|
462
449
|
elsif @_was_loaded || !loaded?
|
463
|
-
@association_ids = nil
|
464
450
|
target << record
|
465
451
|
end
|
466
452
|
|
@@ -366,6 +366,34 @@ module ActiveRecord
|
|
366
366
|
@association.create!(attributes, &block)
|
367
367
|
end
|
368
368
|
|
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
|
+
|
369
397
|
# Replaces this collection with +other_array+. This will perform a diff
|
370
398
|
# and delete/add only records that have changed.
|
371
399
|
#
|
@@ -1005,9 +1033,8 @@ module ActiveRecord
|
|
1005
1033
|
end
|
1006
1034
|
|
1007
1035
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
1008
|
-
# to the association's primary key.
|
1009
|
-
#
|
1010
|
-
# so several appends may be chained together.
|
1036
|
+
# to the association's primary key. Returns +self+, so several appends may be
|
1037
|
+
# chained together.
|
1011
1038
|
#
|
1012
1039
|
# class Person < ActiveRecord::Base
|
1013
1040
|
# has_many :pets
|
@@ -1030,7 +1057,6 @@ module ActiveRecord
|
|
1030
1057
|
end
|
1031
1058
|
alias_method :push, :<<
|
1032
1059
|
alias_method :append, :<<
|
1033
|
-
alias_method :concat, :<<
|
1034
1060
|
|
1035
1061
|
def prepend(*args)
|
1036
1062
|
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
@@ -1062,7 +1088,7 @@ module ActiveRecord
|
|
1062
1088
|
# person.pets.reload # fetches pets from the database
|
1063
1089
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1064
1090
|
def reload
|
1065
|
-
proxy_association.reload
|
1091
|
+
proxy_association.reload(true)
|
1066
1092
|
reset_scope
|
1067
1093
|
end
|
1068
1094
|
|
@@ -1099,7 +1125,7 @@ module ActiveRecord
|
|
1099
1125
|
SpawnMethods,
|
1100
1126
|
].flat_map { |klass|
|
1101
1127
|
klass.public_instance_methods(false)
|
1102
|
-
} - self.public_instance_methods(false) - [:select] + [:scoping]
|
1128
|
+
} - self.public_instance_methods(false) - [:select] + [:scoping, :values]
|
1103
1129
|
|
1104
1130
|
delegate(*delegate_methods, to: :scope)
|
1105
1131
|
|