activerecord 5.0.7 → 5.1.7
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 +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,5 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "thread"
|
2
|
+
require "active_support/core_ext/string/filters"
|
3
|
+
require "active_support/deprecation"
|
3
4
|
|
4
5
|
module ActiveRecord
|
5
6
|
# = Active Record Reflection
|
@@ -14,18 +15,19 @@ module ActiveRecord
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def self.create(macro, name, scope, options, ar)
|
17
|
-
klass =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
klass = \
|
19
|
+
case macro
|
20
|
+
when :composed_of
|
21
|
+
AggregateReflection
|
22
|
+
when :has_many
|
23
|
+
HasManyReflection
|
24
|
+
when :has_one
|
25
|
+
HasOneReflection
|
26
|
+
when :belongs_to
|
27
|
+
BelongsToReflection
|
28
|
+
else
|
29
|
+
raise "Unsupported Macro: #{macro}"
|
30
|
+
end
|
29
31
|
|
30
32
|
reflection = klass.new(name, scope, options, ar)
|
31
33
|
options[:through] ? ThroughReflection.new(reflection) : reflection
|
@@ -33,7 +35,8 @@ module ActiveRecord
|
|
33
35
|
|
34
36
|
def self.add_reflection(ar, name, reflection)
|
35
37
|
ar.clear_reflections_cache
|
36
|
-
|
38
|
+
name = name.to_s
|
39
|
+
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
37
40
|
end
|
38
41
|
|
39
42
|
def self.add_aggregate_reflection(ar, name, reflection)
|
@@ -135,8 +138,8 @@ module ActiveRecord
|
|
135
138
|
# BelongsToReflection
|
136
139
|
# HasAndBelongsToManyReflection
|
137
140
|
# ThroughReflection
|
138
|
-
#
|
139
|
-
#
|
141
|
+
# PolymorphicReflection
|
142
|
+
# RuntimeReflection
|
140
143
|
class AbstractReflection # :nodoc:
|
141
144
|
def through_reflection?
|
142
145
|
false
|
@@ -170,12 +173,44 @@ module ActiveRecord
|
|
170
173
|
|
171
174
|
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
172
175
|
|
173
|
-
def join_keys
|
174
|
-
|
176
|
+
def join_keys
|
177
|
+
get_join_keys klass
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns a list of scopes that should be applied for this Reflection
|
181
|
+
# object when querying the database.
|
182
|
+
def scopes
|
183
|
+
scope ? [scope] : []
|
184
|
+
end
|
185
|
+
|
186
|
+
def scope_chain
|
187
|
+
chain.map(&:scopes)
|
188
|
+
end
|
189
|
+
deprecate :scope_chain
|
190
|
+
|
191
|
+
def join_scope(table)
|
192
|
+
predicate_builder = predicate_builder(table)
|
193
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
194
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
195
|
+
|
196
|
+
scope_chain_items.inject(klass_scope || scope_chain_items.shift, &:merge!)
|
197
|
+
end
|
198
|
+
|
199
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
200
|
+
if scope
|
201
|
+
[build_scope(table, predicate_builder).instance_exec(&scope)]
|
202
|
+
else
|
203
|
+
[]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def klass_join_scope(table, predicate_builder) # :nodoc:
|
208
|
+
relation = build_scope(table, predicate_builder)
|
209
|
+
klass.scope_for_association(relation)
|
175
210
|
end
|
176
211
|
|
177
212
|
def constraints
|
178
|
-
|
213
|
+
chain.map(&:scopes).flatten
|
179
214
|
end
|
180
215
|
|
181
216
|
def counter_cache_column
|
@@ -247,6 +282,27 @@ module ActiveRecord
|
|
247
282
|
def chain
|
248
283
|
collect_join_chain
|
249
284
|
end
|
285
|
+
|
286
|
+
def get_join_keys(association_klass)
|
287
|
+
JoinKeys.new(join_pk(association_klass), join_fk)
|
288
|
+
end
|
289
|
+
|
290
|
+
def build_scope(table, predicate_builder = predicate_builder(table))
|
291
|
+
Relation.create(klass, table, predicate_builder)
|
292
|
+
end
|
293
|
+
|
294
|
+
private
|
295
|
+
def predicate_builder(table)
|
296
|
+
PredicateBuilder.new(TableMetadata.new(klass, table))
|
297
|
+
end
|
298
|
+
|
299
|
+
def join_pk(_)
|
300
|
+
foreign_key
|
301
|
+
end
|
302
|
+
|
303
|
+
def join_fk
|
304
|
+
active_record_primary_key
|
305
|
+
end
|
250
306
|
end
|
251
307
|
|
252
308
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
@@ -281,7 +337,6 @@ module ActiveRecord
|
|
281
337
|
end
|
282
338
|
|
283
339
|
def autosave=(autosave)
|
284
|
-
@automatic_inverse_of = false
|
285
340
|
@options[:autosave] = autosave
|
286
341
|
parent_reflection = self.parent_reflection
|
287
342
|
if parent_reflection
|
@@ -321,8 +376,7 @@ module ActiveRecord
|
|
321
376
|
end
|
322
377
|
end
|
323
378
|
|
324
|
-
|
325
|
-
# Holds all the meta-data about an aggregation as it was specified in the
|
379
|
+
# Holds all the metadata about an aggregation as it was specified in the
|
326
380
|
# Active Record class.
|
327
381
|
class AggregateReflection < MacroReflection #:nodoc:
|
328
382
|
def mapping
|
@@ -331,7 +385,7 @@ module ActiveRecord
|
|
331
385
|
end
|
332
386
|
end
|
333
387
|
|
334
|
-
# Holds all the
|
388
|
+
# Holds all the metadata about an association as it was specified in the
|
335
389
|
# Active Record class.
|
336
390
|
class AssociationReflection < MacroReflection #:nodoc:
|
337
391
|
# Returns the target association's class.
|
@@ -359,12 +413,22 @@ module ActiveRecord
|
|
359
413
|
|
360
414
|
def initialize(name, scope, options, active_record)
|
361
415
|
super
|
362
|
-
@automatic_inverse_of = nil
|
363
416
|
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
364
417
|
@foreign_type = options[:foreign_type] || "#{name}_type"
|
365
418
|
@constructable = calculate_constructable(macro, options)
|
366
419
|
@association_scope_cache = {}
|
367
420
|
@scope_lock = Mutex.new
|
421
|
+
|
422
|
+
if options[:class_name] && options[:class_name].class == Class
|
423
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
424
|
+
Passing a class to the `class_name` is deprecated and will raise
|
425
|
+
an ArgumentError in Rails 5.2. It eagerloads more classes than
|
426
|
+
necessary and potentially creates circular dependencies.
|
427
|
+
|
428
|
+
Please pass the class name as a string:
|
429
|
+
`#{macro} :#{name}, class_name: '#{options[:class_name]}'`
|
430
|
+
MSG
|
431
|
+
end
|
368
432
|
end
|
369
433
|
|
370
434
|
def association_scope_cache(conn, owner)
|
@@ -451,12 +515,6 @@ module ActiveRecord
|
|
451
515
|
false
|
452
516
|
end
|
453
517
|
|
454
|
-
# An array of arrays of scopes. Each item in the outside array corresponds to a reflection
|
455
|
-
# in the #chain.
|
456
|
-
def scope_chain
|
457
|
-
scope ? [[scope]] : [[]]
|
458
|
-
end
|
459
|
-
|
460
518
|
def has_scope?
|
461
519
|
scope
|
462
520
|
end
|
@@ -545,18 +603,16 @@ module ActiveRecord
|
|
545
603
|
|
546
604
|
# Attempts to find the inverse association name automatically.
|
547
605
|
# If it cannot find a suitable inverse association name, it returns
|
548
|
-
# nil
|
606
|
+
# +nil+.
|
549
607
|
def inverse_name
|
550
|
-
|
551
|
-
|
552
|
-
nil
|
553
|
-
else
|
554
|
-
@automatic_inverse_of ||= automatic_inverse_of
|
555
|
-
end
|
608
|
+
unless defined?(@inverse_name)
|
609
|
+
@inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
|
556
610
|
end
|
611
|
+
|
612
|
+
@inverse_name
|
557
613
|
end
|
558
614
|
|
559
|
-
# returns either
|
615
|
+
# returns either +nil+ or the inverse association name that it finds.
|
560
616
|
def automatic_inverse_of
|
561
617
|
if can_find_inverse_of_automatically?(self)
|
562
618
|
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
@@ -573,8 +629,6 @@ module ActiveRecord
|
|
573
629
|
return inverse_name
|
574
630
|
end
|
575
631
|
end
|
576
|
-
|
577
|
-
false
|
578
632
|
end
|
579
633
|
|
580
634
|
# Checks if the inverse reflection that is returned from the
|
@@ -683,11 +737,6 @@ module ActiveRecord
|
|
683
737
|
end
|
684
738
|
end
|
685
739
|
|
686
|
-
def join_keys(association_klass)
|
687
|
-
key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
|
688
|
-
JoinKeys.new(key, foreign_key)
|
689
|
-
end
|
690
|
-
|
691
740
|
def join_id_for(owner) # :nodoc:
|
692
741
|
owner[foreign_key]
|
693
742
|
end
|
@@ -697,6 +746,14 @@ module ActiveRecord
|
|
697
746
|
def calculate_constructable(macro, options)
|
698
747
|
!polymorphic?
|
699
748
|
end
|
749
|
+
|
750
|
+
def join_fk
|
751
|
+
foreign_key
|
752
|
+
end
|
753
|
+
|
754
|
+
def join_pk(klass)
|
755
|
+
polymorphic? ? association_primary_key(klass) : association_primary_key
|
756
|
+
end
|
700
757
|
end
|
701
758
|
|
702
759
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -711,16 +768,16 @@ module ActiveRecord
|
|
711
768
|
end
|
712
769
|
end
|
713
770
|
|
714
|
-
# Holds all the
|
771
|
+
# Holds all the metadata about a :through association as it was specified
|
715
772
|
# in the Active Record class.
|
716
773
|
class ThroughReflection < AbstractReflection #:nodoc:
|
717
774
|
attr_reader :delegate_reflection
|
718
775
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
719
|
-
:active_record_primary_key, :type, :to
|
776
|
+
:active_record_primary_key, :type, :get_join_keys, to: :source_reflection
|
720
777
|
|
721
778
|
def initialize(delegate_reflection)
|
722
779
|
@delegate_reflection = delegate_reflection
|
723
|
-
@klass
|
780
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
724
781
|
@source_reflection_name = delegate_reflection.options[:source]
|
725
782
|
end
|
726
783
|
|
@@ -798,45 +855,16 @@ module ActiveRecord
|
|
798
855
|
through_reflection.clear_association_scope_cache
|
799
856
|
end
|
800
857
|
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
# has_many :articles
|
805
|
-
# has_many :comment_tags, through: :articles
|
806
|
-
# end
|
807
|
-
#
|
808
|
-
# class Article
|
809
|
-
# has_many :comments
|
810
|
-
# has_many :comment_tags, through: :comments, source: :tags
|
811
|
-
# end
|
812
|
-
#
|
813
|
-
# class Comment
|
814
|
-
# has_many :tags
|
815
|
-
# end
|
816
|
-
#
|
817
|
-
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
|
818
|
-
# but only Comment.tags will be represented in the #chain. So this method creates an array
|
819
|
-
# of scopes corresponding to the chain.
|
820
|
-
def scope_chain
|
821
|
-
@scope_chain ||= begin
|
822
|
-
scope_chain = source_reflection.scope_chain.map(&:dup)
|
823
|
-
|
824
|
-
# Add to it the scope from this reflection (if any)
|
825
|
-
scope_chain.first << scope if scope
|
826
|
-
|
827
|
-
through_scope_chain = through_reflection.scope_chain.map(&:dup)
|
858
|
+
def scopes
|
859
|
+
source_reflection.scopes + super
|
860
|
+
end
|
828
861
|
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
through_scope_chain.first << lambda { |object|
|
833
|
-
where(type => source_type)
|
834
|
-
}
|
835
|
-
end
|
862
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
863
|
+
source_reflection.join_scopes(table, predicate_builder) + super
|
864
|
+
end
|
836
865
|
|
837
|
-
|
838
|
-
|
839
|
-
end
|
866
|
+
def source_type_scope
|
867
|
+
through_reflection.klass.where(foreign_type => options[:source_type])
|
840
868
|
end
|
841
869
|
|
842
870
|
def has_scope?
|
@@ -845,10 +873,6 @@ module ActiveRecord
|
|
845
873
|
through_reflection.has_scope?
|
846
874
|
end
|
847
875
|
|
848
|
-
def join_keys(association_klass)
|
849
|
-
source_reflection.join_keys(association_klass)
|
850
|
-
end
|
851
|
-
|
852
876
|
# A through association is nested if there would be more than one join table
|
853
877
|
def nested?
|
854
878
|
source_reflection.through_reflection? || through_reflection.through_reflection?
|
@@ -891,15 +915,13 @@ module ActiveRecord
|
|
891
915
|
}
|
892
916
|
|
893
917
|
if names.length > 1
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
" #{macro} :#{name}, #{example_options}\n" \
|
902
|
-
" end"
|
918
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
919
|
+
active_record.name,
|
920
|
+
macro,
|
921
|
+
name,
|
922
|
+
options,
|
923
|
+
source_reflection_names
|
924
|
+
)
|
903
925
|
end
|
904
926
|
|
905
927
|
@source_reflection_name = names.first
|
@@ -946,6 +968,14 @@ module ActiveRecord
|
|
946
968
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
947
969
|
end
|
948
970
|
|
971
|
+
if parent_reflection.nil?
|
972
|
+
reflections = active_record.reflections.keys.map(&:to_sym)
|
973
|
+
|
974
|
+
if reflections.index(through_reflection.name) > reflections.index(name)
|
975
|
+
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
976
|
+
end
|
977
|
+
end
|
978
|
+
|
949
979
|
check_validity_of_inverse!
|
950
980
|
end
|
951
981
|
|
@@ -976,7 +1006,7 @@ module ActiveRecord
|
|
976
1006
|
end
|
977
1007
|
end
|
978
1008
|
|
979
|
-
|
1009
|
+
private
|
980
1010
|
|
981
1011
|
def actual_source_reflection # FIXME: this is a horrible name
|
982
1012
|
source_reflection.send(:actual_source_reflection)
|
@@ -988,7 +1018,6 @@ module ActiveRecord
|
|
988
1018
|
|
989
1019
|
def inverse_name; delegate_reflection.send(:inverse_name); end
|
990
1020
|
|
991
|
-
private
|
992
1021
|
def derive_class_name
|
993
1022
|
# get the class_name of the belongs_to association of the through reflection
|
994
1023
|
options[:source_type] || source_reflection.class_name
|
@@ -998,15 +1027,32 @@ module ActiveRecord
|
|
998
1027
|
public_instance_methods
|
999
1028
|
|
1000
1029
|
delegate(*delegate_methods, to: :delegate_reflection)
|
1001
|
-
|
1002
1030
|
end
|
1003
1031
|
|
1004
|
-
class PolymorphicReflection <
|
1032
|
+
class PolymorphicReflection < AbstractReflection # :nodoc:
|
1005
1033
|
def initialize(reflection, previous_reflection)
|
1006
1034
|
@reflection = reflection
|
1007
1035
|
@previous_reflection = previous_reflection
|
1008
1036
|
end
|
1009
1037
|
|
1038
|
+
def scopes
|
1039
|
+
scopes = @previous_reflection.scopes
|
1040
|
+
if @previous_reflection.options[:source_type]
|
1041
|
+
scopes + [@previous_reflection.source_type_scope]
|
1042
|
+
else
|
1043
|
+
scopes
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
1048
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1049
|
+
if @previous_reflection.options[:source_type]
|
1050
|
+
scopes + [@previous_reflection.source_type_scope]
|
1051
|
+
else
|
1052
|
+
scopes
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
|
1010
1056
|
def klass
|
1011
1057
|
@reflection.klass
|
1012
1058
|
end
|
@@ -1023,10 +1069,6 @@ module ActiveRecord
|
|
1023
1069
|
@reflection.plural_name
|
1024
1070
|
end
|
1025
1071
|
|
1026
|
-
def join_keys(association_klass)
|
1027
|
-
@reflection.join_keys(association_klass)
|
1028
|
-
end
|
1029
|
-
|
1030
1072
|
def type
|
1031
1073
|
@reflection.type
|
1032
1074
|
end
|
@@ -1040,6 +1082,10 @@ module ActiveRecord
|
|
1040
1082
|
source_type = @previous_reflection.options[:source_type]
|
1041
1083
|
lambda { |object| where(type => source_type) }
|
1042
1084
|
end
|
1085
|
+
|
1086
|
+
def get_join_keys(association_klass)
|
1087
|
+
@reflection.get_join_keys(association_klass)
|
1088
|
+
end
|
1043
1089
|
end
|
1044
1090
|
|
1045
1091
|
class RuntimeReflection < PolymorphicReflection # :nodoc:
|
@@ -1071,7 +1117,7 @@ module ActiveRecord
|
|
1071
1117
|
end
|
1072
1118
|
|
1073
1119
|
def alias_name
|
1074
|
-
Arel::Table.new(table_name)
|
1120
|
+
Arel::Table.new(table_name, type_caster: klass.type_caster)
|
1075
1121
|
end
|
1076
1122
|
|
1077
1123
|
def all_includes; yield; end
|
@@ -2,7 +2,7 @@ require "active_record/relation/batches/batch_enumerator"
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Batches
|
5
|
-
|
5
|
+
ORDER_IGNORE_MESSAGE = "Scoped order is ignored, it's forced to be batch order."
|
6
6
|
|
7
7
|
# Looping through a collection of records from the database
|
8
8
|
# (using the Scoping::Named::ClassMethods.all method, for example)
|
@@ -34,15 +34,19 @@ module ActiveRecord
|
|
34
34
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
35
35
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
36
36
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
37
|
-
#
|
37
|
+
# an order is present in the relation.
|
38
38
|
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
|
42
|
-
# (by setting the +:start+ and +:finish+ option on each worker).
|
39
|
+
# Limits are honored, and if present there is no requirement for the batch
|
40
|
+
# size: it can be less than, equal to, or greater than the limit.
|
43
41
|
#
|
44
|
-
#
|
45
|
-
#
|
42
|
+
# The options +start+ and +finish+ are especially useful if you want
|
43
|
+
# multiple workers dealing with the same processing queue. You can make
|
44
|
+
# worker 1 handle all the records between id 1 and 9999 and worker 2
|
45
|
+
# handle from 10000 and beyond by setting the +:start+ and +:finish+
|
46
|
+
# option on each worker.
|
47
|
+
#
|
48
|
+
# # Let's process from record 10_000 on.
|
49
|
+
# Person.find_each(start: 10_000) do |person|
|
46
50
|
# person.party_all_night!
|
47
51
|
# end
|
48
52
|
#
|
@@ -51,8 +55,8 @@ module ActiveRecord
|
|
51
55
|
# work. This also means that this method only works when the primary key is
|
52
56
|
# orderable (e.g. an integer or string).
|
53
57
|
#
|
54
|
-
# NOTE:
|
55
|
-
# the
|
58
|
+
# NOTE: By its nature, batch processing is subject to race conditions if
|
59
|
+
# other processes are modifying the database.
|
56
60
|
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
57
61
|
if block_given?
|
58
62
|
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
|
@@ -89,15 +93,19 @@ module ActiveRecord
|
|
89
93
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
90
94
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
91
95
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
92
|
-
#
|
96
|
+
# an order is present in the relation.
|
97
|
+
#
|
98
|
+
# Limits are honored, and if present there is no requirement for the batch
|
99
|
+
# size: it can be less than, equal to, or greater than the limit.
|
93
100
|
#
|
94
|
-
#
|
95
|
-
# the same processing queue. You can make
|
96
|
-
# between id
|
97
|
-
#
|
101
|
+
# The options +start+ and +finish+ are especially useful if you want
|
102
|
+
# multiple workers dealing with the same processing queue. You can make
|
103
|
+
# worker 1 handle all the records between id 1 and 9999 and worker 2
|
104
|
+
# handle from 10000 and beyond by setting the +:start+ and +:finish+
|
105
|
+
# option on each worker.
|
98
106
|
#
|
99
|
-
# # Let's process
|
100
|
-
# Person.find_in_batches(start:
|
107
|
+
# # Let's process from record 10_000 on.
|
108
|
+
# Person.find_in_batches(start: 10_000) do |group|
|
101
109
|
# group.each { |person| person.party_all_night! }
|
102
110
|
# end
|
103
111
|
#
|
@@ -106,8 +114,8 @@ module ActiveRecord
|
|
106
114
|
# work. This also means that this method only works when the primary key is
|
107
115
|
# orderable (e.g. an integer or string).
|
108
116
|
#
|
109
|
-
# NOTE:
|
110
|
-
# the
|
117
|
+
# NOTE: By its nature, batch processing is subject to race conditions if
|
118
|
+
# other processes are modifying the database.
|
111
119
|
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
112
120
|
relation = self
|
113
121
|
unless block_given?
|
@@ -149,17 +157,19 @@ module ActiveRecord
|
|
149
157
|
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
|
150
158
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
151
159
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
152
|
-
#
|
160
|
+
# an order is present in the relation.
|
153
161
|
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
# you want multiple workers dealing with the same processing queue. You can
|
157
|
-
# make worker 1 handle all the records between id 0 and 10,000 and worker 2
|
158
|
-
# handle from 10,000 and beyond (by setting the +:start+ and +:finish+
|
159
|
-
# option on each worker).
|
162
|
+
# Limits are honored, and if present there is no requirement for the batch
|
163
|
+
# size, it can be less than, equal, or greater than the limit.
|
160
164
|
#
|
161
|
-
#
|
162
|
-
#
|
165
|
+
# The options +start+ and +finish+ are especially useful if you want
|
166
|
+
# multiple workers dealing with the same processing queue. You can make
|
167
|
+
# worker 1 handle all the records between id 1 and 9999 and worker 2
|
168
|
+
# handle from 10000 and beyond by setting the +:start+ and +:finish+
|
169
|
+
# option on each worker.
|
170
|
+
#
|
171
|
+
# # Let's process from record 10_000 on.
|
172
|
+
# Person.in_batches(start: 10_000).update_all(awesome: true)
|
163
173
|
#
|
164
174
|
# An example of calling where query method on the relation:
|
165
175
|
#
|
@@ -179,19 +189,25 @@ module ActiveRecord
|
|
179
189
|
# consistent. Therefore the primary key must be orderable, e.g. an integer
|
180
190
|
# or a string.
|
181
191
|
#
|
182
|
-
# NOTE:
|
183
|
-
#
|
192
|
+
# NOTE: By its nature, batch processing is subject to race conditions if
|
193
|
+
# other processes are modifying the database.
|
184
194
|
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
|
185
195
|
relation = self
|
186
196
|
unless block_given?
|
187
197
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
188
198
|
end
|
189
199
|
|
190
|
-
if arel.orders.present?
|
191
|
-
|
200
|
+
if arel.orders.present?
|
201
|
+
act_on_ignored_order(error_on_ignore)
|
202
|
+
end
|
203
|
+
|
204
|
+
batch_limit = of
|
205
|
+
if limit_value
|
206
|
+
remaining = limit_value
|
207
|
+
batch_limit = remaining if remaining < batch_limit
|
192
208
|
end
|
193
209
|
|
194
|
-
relation = relation.reorder(batch_order).limit(
|
210
|
+
relation = relation.reorder(batch_order).limit(batch_limit)
|
195
211
|
relation = apply_limits(relation, start, finish)
|
196
212
|
batch_relation = relation
|
197
213
|
|
@@ -199,11 +215,11 @@ module ActiveRecord
|
|
199
215
|
if load
|
200
216
|
records = batch_relation.records
|
201
217
|
ids = records.map(&:id)
|
202
|
-
yielded_relation =
|
218
|
+
yielded_relation = where(primary_key => ids)
|
203
219
|
yielded_relation.load_records(records)
|
204
220
|
else
|
205
221
|
ids = batch_relation.pluck(primary_key)
|
206
|
-
yielded_relation =
|
222
|
+
yielded_relation = where(primary_key => ids)
|
207
223
|
end
|
208
224
|
|
209
225
|
break if ids.empty?
|
@@ -213,31 +229,44 @@ module ActiveRecord
|
|
213
229
|
|
214
230
|
yield yielded_relation
|
215
231
|
|
216
|
-
break if ids.length <
|
232
|
+
break if ids.length < batch_limit
|
233
|
+
|
234
|
+
if limit_value
|
235
|
+
remaining -= ids.length
|
236
|
+
|
237
|
+
if remaining == 0
|
238
|
+
# Saves a useless iteration when the limit is a multiple of the
|
239
|
+
# batch size.
|
240
|
+
break
|
241
|
+
elsif remaining < batch_limit
|
242
|
+
relation = relation.limit(remaining)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
217
246
|
batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
|
218
247
|
end
|
219
248
|
end
|
220
249
|
|
221
250
|
private
|
222
251
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
252
|
+
def apply_limits(relation, start, finish)
|
253
|
+
relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
|
254
|
+
relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
|
255
|
+
relation
|
256
|
+
end
|
228
257
|
|
229
|
-
|
230
|
-
|
231
|
-
|
258
|
+
def batch_order
|
259
|
+
"#{quoted_table_name}.#{quoted_primary_key} ASC"
|
260
|
+
end
|
232
261
|
|
233
|
-
|
234
|
-
|
262
|
+
def act_on_ignored_order(error_on_ignore)
|
263
|
+
raise_error = (error_on_ignore.nil? ? klass.error_on_ignored_order : error_on_ignore)
|
235
264
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
265
|
+
if raise_error
|
266
|
+
raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
|
267
|
+
elsif logger
|
268
|
+
logger.warn(ORDER_IGNORE_MESSAGE)
|
269
|
+
end
|
240
270
|
end
|
241
|
-
end
|
242
271
|
end
|
243
272
|
end
|