activerecord 6.0.6.1 → 6.1.0.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 +764 -942
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +39 -27
- data/lib/active_record/associations/association_scope.rb +11 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -13
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +13 -5
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +4 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +52 -48
- data/lib/active_record/attributes.rb +27 -7
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +32 -22
- data/lib/active_record/coders/yaml_column.rb +2 -24
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +35 -44
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
- data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -10
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +214 -58
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +33 -23
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +32 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +15 -4
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +13 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/model_schema.rb +88 -42
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +253 -98
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +59 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +100 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/query_methods.rb +319 -198
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +6 -5
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/relation.rb +90 -64
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +36 -52
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +36 -33
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +30 -32
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -162,13 +162,7 @@ module ActiveRecord
|
|
162
162
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
163
163
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
164
164
|
def class_name
|
165
|
-
@class_name ||= (options[:class_name] || derive_class_name)
|
166
|
-
end
|
167
|
-
|
168
|
-
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
169
|
-
|
170
|
-
def join_keys
|
171
|
-
@join_keys ||= get_join_keys(klass)
|
165
|
+
@class_name ||= -(options[:class_name]&.to_s || derive_class_name)
|
172
166
|
end
|
173
167
|
|
174
168
|
# Returns a list of scopes that should be applied for this Reflection
|
@@ -188,10 +182,10 @@ module ActiveRecord
|
|
188
182
|
|
189
183
|
scope_chain_items.inject(klass_scope, &:merge!)
|
190
184
|
|
191
|
-
|
192
|
-
foreign_key =
|
185
|
+
primary_key = join_primary_key
|
186
|
+
foreign_key = join_foreign_key
|
193
187
|
|
194
|
-
klass_scope.where!(table[
|
188
|
+
klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
|
195
189
|
|
196
190
|
if klass.finder_needs_type_condition?
|
197
191
|
klass_scope.where!(klass.send(:type_condition, table))
|
@@ -218,14 +212,14 @@ module ActiveRecord
|
|
218
212
|
end
|
219
213
|
|
220
214
|
def counter_cache_column
|
221
|
-
if belongs_to?
|
215
|
+
@counter_cache_column ||= if belongs_to?
|
222
216
|
if options[:counter_cache] == true
|
223
|
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
217
|
+
-"#{active_record.name.demodulize.underscore.pluralize}_count"
|
224
218
|
elsif options[:counter_cache]
|
225
|
-
options[:counter_cache].to_s
|
219
|
+
-options[:counter_cache].to_s
|
226
220
|
end
|
227
221
|
else
|
228
|
-
options[:counter_cache]
|
222
|
+
-(options[:counter_cache]&.to_s || "#{name}_count")
|
229
223
|
end
|
230
224
|
end
|
231
225
|
|
@@ -272,7 +266,7 @@ module ActiveRecord
|
|
272
266
|
def has_cached_counter?
|
273
267
|
options[:counter_cache] ||
|
274
268
|
inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
|
275
|
-
|
269
|
+
active_record.has_attribute?(counter_cache_column)
|
276
270
|
end
|
277
271
|
|
278
272
|
def counter_must_be_updated_by_has_many?
|
@@ -287,10 +281,6 @@ module ActiveRecord
|
|
287
281
|
collect_join_chain
|
288
282
|
end
|
289
283
|
|
290
|
-
def get_join_keys(association_klass)
|
291
|
-
JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
|
292
|
-
end
|
293
|
-
|
294
284
|
def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
|
295
285
|
Relation.create(
|
296
286
|
klass,
|
@@ -299,12 +289,8 @@ module ActiveRecord
|
|
299
289
|
)
|
300
290
|
end
|
301
291
|
|
302
|
-
def
|
303
|
-
|
304
|
-
end
|
305
|
-
|
306
|
-
def join_foreign_key
|
307
|
-
active_record_primary_key
|
292
|
+
def strict_loading?
|
293
|
+
options[:strict_loading]
|
308
294
|
end
|
309
295
|
|
310
296
|
protected
|
@@ -428,8 +414,8 @@ module ActiveRecord
|
|
428
414
|
|
429
415
|
def initialize(name, scope, options, active_record)
|
430
416
|
super
|
431
|
-
@type
|
432
|
-
@foreign_type =
|
417
|
+
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
|
+
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
433
419
|
@constructable = calculate_constructable(macro, options)
|
434
420
|
|
435
421
|
if options[:class_name] && options[:class_name].class == Class
|
@@ -450,24 +436,31 @@ module ActiveRecord
|
|
450
436
|
end
|
451
437
|
|
452
438
|
def join_table
|
453
|
-
@join_table ||= options[:join_table] || derive_join_table
|
439
|
+
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
454
440
|
end
|
455
441
|
|
456
442
|
def foreign_key
|
457
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
443
|
+
@foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
|
458
444
|
end
|
459
445
|
|
460
446
|
def association_foreign_key
|
461
|
-
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
|
447
|
+
@association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
|
462
448
|
end
|
463
449
|
|
464
|
-
# klass option is necessary to support loading polymorphic associations
|
465
450
|
def association_primary_key(klass = nil)
|
466
|
-
|
451
|
+
primary_key(klass || self.klass)
|
467
452
|
end
|
468
453
|
|
469
454
|
def active_record_primary_key
|
470
|
-
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
455
|
+
@active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
|
456
|
+
end
|
457
|
+
|
458
|
+
def join_primary_key(klass = nil)
|
459
|
+
foreign_key
|
460
|
+
end
|
461
|
+
|
462
|
+
def join_foreign_key
|
463
|
+
active_record_primary_key
|
471
464
|
end
|
472
465
|
|
473
466
|
def check_validity!
|
@@ -683,10 +676,6 @@ module ActiveRecord
|
|
683
676
|
Associations::HasManyAssociation
|
684
677
|
end
|
685
678
|
end
|
686
|
-
|
687
|
-
def association_primary_key(klass = nil)
|
688
|
-
primary_key(klass || self.klass)
|
689
|
-
end
|
690
679
|
end
|
691
680
|
|
692
681
|
class HasOneReflection < AssociationReflection # :nodoc:
|
@@ -721,6 +710,15 @@ module ActiveRecord
|
|
721
710
|
end
|
722
711
|
end
|
723
712
|
|
713
|
+
# klass option is necessary to support loading polymorphic associations
|
714
|
+
def association_primary_key(klass = nil)
|
715
|
+
if primary_key = options[:primary_key]
|
716
|
+
@association_primary_key ||= -primary_key.to_s
|
717
|
+
else
|
718
|
+
primary_key(klass || self.klass)
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
724
722
|
def join_primary_key(klass = nil)
|
725
723
|
polymorphic? ? association_primary_key(klass) : association_primary_key
|
726
724
|
end
|
@@ -729,6 +727,10 @@ module ActiveRecord
|
|
729
727
|
foreign_key
|
730
728
|
end
|
731
729
|
|
730
|
+
def join_foreign_type
|
731
|
+
foreign_type
|
732
|
+
end
|
733
|
+
|
732
734
|
private
|
733
735
|
def can_find_inverse_of_automatically?(_)
|
734
736
|
!polymorphic? && super
|
@@ -750,8 +752,8 @@ module ActiveRecord
|
|
750
752
|
# Holds all the metadata about a :through association as it was specified
|
751
753
|
# in the Active Record class.
|
752
754
|
class ThroughReflection < AbstractReflection #:nodoc:
|
753
|
-
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
|
754
|
-
:active_record_primary_key, :
|
755
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
756
|
+
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
755
757
|
|
756
758
|
def initialize(delegate_reflection)
|
757
759
|
@delegate_reflection = delegate_reflection
|
@@ -858,7 +860,15 @@ module ActiveRecord
|
|
858
860
|
def association_primary_key(klass = nil)
|
859
861
|
# Get the "actual" source reflection if the immediate source reflection has a
|
860
862
|
# source reflection itself
|
861
|
-
actual_source_reflection.options[:primary_key]
|
863
|
+
if primary_key = actual_source_reflection.options[:primary_key]
|
864
|
+
@association_primary_key ||= -primary_key.to_s
|
865
|
+
else
|
866
|
+
primary_key(klass || self.klass)
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
def join_primary_key(klass = self.klass)
|
871
|
+
source_reflection.join_primary_key(klass)
|
862
872
|
end
|
863
873
|
|
864
874
|
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
@@ -907,7 +917,7 @@ module ActiveRecord
|
|
907
917
|
|
908
918
|
def check_validity!
|
909
919
|
if through_reflection.nil?
|
910
|
-
raise HasManyThroughAssociationNotFoundError.new(active_record
|
920
|
+
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
911
921
|
end
|
912
922
|
|
913
923
|
if through_reflection.polymorphic?
|
@@ -994,7 +1004,8 @@ module ActiveRecord
|
|
994
1004
|
end
|
995
1005
|
|
996
1006
|
class PolymorphicReflection < AbstractReflection # :nodoc:
|
997
|
-
delegate :klass, :scope, :plural_name, :type, :
|
1007
|
+
delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
|
1008
|
+
:name, :scope_for, to: :@reflection
|
998
1009
|
|
999
1010
|
def initialize(reflection, previous_reflection)
|
1000
1011
|
@reflection = reflection
|
@@ -1019,7 +1030,7 @@ module ActiveRecord
|
|
1019
1030
|
end
|
1020
1031
|
|
1021
1032
|
class RuntimeReflection < AbstractReflection # :nodoc:
|
1022
|
-
delegate :scope, :type, :constraints, :
|
1033
|
+
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1023
1034
|
|
1024
1035
|
def initialize(reflection, association)
|
1025
1036
|
@reflection = reflection
|
@@ -1031,7 +1042,11 @@ module ActiveRecord
|
|
1031
1042
|
end
|
1032
1043
|
|
1033
1044
|
def aliased_table
|
1034
|
-
|
1045
|
+
klass.arel_table
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def join_primary_key(klass = self.klass)
|
1049
|
+
@reflection.join_primary_key(klass)
|
1035
1050
|
end
|
1036
1051
|
|
1037
1052
|
def all_includes; yield; end
|
@@ -41,19 +41,35 @@ module ActiveRecord
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
#
|
44
|
+
# Deletes records in batches. Returns the total number of rows affected.
|
45
45
|
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
46
|
+
# Person.in_batches.delete_all
|
47
|
+
#
|
48
|
+
# See Relation#delete_all for details of how each batch is deleted.
|
49
|
+
def delete_all
|
50
|
+
sum(&:delete_all)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Updates records in batches. Returns the total number of rows affected.
|
54
|
+
#
|
55
|
+
# Person.in_batches.update_all("age = age + 1")
|
56
|
+
#
|
57
|
+
# See Relation#update_all for details of how each batch is updated.
|
58
|
+
def update_all(updates)
|
59
|
+
sum do |relation|
|
60
|
+
relation.update_all(updates)
|
54
61
|
end
|
55
62
|
end
|
56
63
|
|
64
|
+
# Destroys records in batches.
|
65
|
+
#
|
66
|
+
# Person.where("age < 10").in_batches.destroy_all
|
67
|
+
#
|
68
|
+
# See Relation#destroy_all for details of how each batch is destroyed.
|
69
|
+
def destroy_all
|
70
|
+
each(&:destroy_all)
|
71
|
+
end
|
72
|
+
|
57
73
|
# Yields an ActiveRecord::Relation object for each batch of records.
|
58
74
|
#
|
59
75
|
# Person.in_batches.each do |relation|
|
@@ -37,6 +37,7 @@ module ActiveRecord
|
|
37
37
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
38
38
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
39
39
|
# an order is present in the relation.
|
40
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
|
40
41
|
#
|
41
42
|
# Limits are honored, and if present there is no requirement for the batch
|
42
43
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -57,22 +58,22 @@ module ActiveRecord
|
|
57
58
|
# person.party_all_night!
|
58
59
|
# end
|
59
60
|
#
|
60
|
-
# NOTE:
|
61
|
-
# ascending on the primary key ("id ASC")
|
62
|
-
#
|
61
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
62
|
+
# ascending on the primary key ("id ASC").
|
63
|
+
# This also means that this method only works when the primary key is
|
63
64
|
# orderable (e.g. an integer or string).
|
64
65
|
#
|
65
66
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
66
67
|
# other processes are modifying the database.
|
67
|
-
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
68
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
68
69
|
if block_given?
|
69
|
-
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
|
70
|
+
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
|
70
71
|
records.each { |record| yield record }
|
71
72
|
end
|
72
73
|
else
|
73
|
-
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
74
|
+
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
74
75
|
relation = self
|
75
|
-
apply_limits(relation, start, finish).size
|
76
|
+
apply_limits(relation, start, finish, order).size
|
76
77
|
end
|
77
78
|
end
|
78
79
|
end
|
@@ -101,6 +102,7 @@ module ActiveRecord
|
|
101
102
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
102
103
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
103
104
|
# an order is present in the relation.
|
105
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
|
104
106
|
#
|
105
107
|
# Limits are honored, and if present there is no requirement for the batch
|
106
108
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -116,23 +118,23 @@ module ActiveRecord
|
|
116
118
|
# group.each { |person| person.party_all_night! }
|
117
119
|
# end
|
118
120
|
#
|
119
|
-
# NOTE:
|
120
|
-
# ascending on the primary key ("id ASC")
|
121
|
-
#
|
121
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
122
|
+
# ascending on the primary key ("id ASC").
|
123
|
+
# This also means that this method only works when the primary key is
|
122
124
|
# orderable (e.g. an integer or string).
|
123
125
|
#
|
124
126
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
125
127
|
# other processes are modifying the database.
|
126
|
-
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
|
128
|
+
def find_in_batches(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
127
129
|
relation = self
|
128
130
|
unless block_given?
|
129
|
-
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
|
130
|
-
total = apply_limits(relation, start, finish).size
|
131
|
+
return to_enum(:find_in_batches, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
132
|
+
total = apply_limits(relation, start, finish, order).size
|
131
133
|
(total - 1).div(batch_size) + 1
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
135
|
-
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore) do |batch|
|
137
|
+
in_batches(of: batch_size, start: start, finish: finish, load: true, error_on_ignore: error_on_ignore, order: order) do |batch|
|
136
138
|
yield batch.to_a
|
137
139
|
end
|
138
140
|
end
|
@@ -165,6 +167,7 @@ module ActiveRecord
|
|
165
167
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
166
168
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
167
169
|
# an order is present in the relation.
|
170
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be :asc or :desc). Defaults to :asc.
|
168
171
|
#
|
169
172
|
# Limits are honored, and if present there is no requirement for the batch
|
170
173
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -191,19 +194,23 @@ module ActiveRecord
|
|
191
194
|
#
|
192
195
|
# Person.in_batches.each_record(&:party_all_night!)
|
193
196
|
#
|
194
|
-
# NOTE:
|
195
|
-
# ascending on the primary key ("id ASC")
|
196
|
-
#
|
197
|
-
# or
|
197
|
+
# NOTE: Order can be ascending (:asc) or descending (:desc). It is automatically set to
|
198
|
+
# ascending on the primary key ("id ASC").
|
199
|
+
# This also means that this method only works when the primary key is
|
200
|
+
# orderable (e.g. an integer or string).
|
198
201
|
#
|
199
202
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
200
203
|
# other processes are modifying the database.
|
201
|
-
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil)
|
204
|
+
def in_batches(of: 1000, start: nil, finish: nil, load: false, error_on_ignore: nil, order: :asc)
|
202
205
|
relation = self
|
203
206
|
unless block_given?
|
204
207
|
return BatchEnumerator.new(of: of, start: start, finish: finish, relation: self)
|
205
208
|
end
|
206
209
|
|
210
|
+
unless [:asc, :desc].include?(order)
|
211
|
+
raise ArgumentError, ":order must be :asc or :desc, got #{order.inspect}"
|
212
|
+
end
|
213
|
+
|
207
214
|
if arel.orders.present?
|
208
215
|
act_on_ignored_order(error_on_ignore)
|
209
216
|
end
|
@@ -214,8 +221,8 @@ module ActiveRecord
|
|
214
221
|
batch_limit = remaining if remaining < batch_limit
|
215
222
|
end
|
216
223
|
|
217
|
-
relation = relation.reorder(batch_order).limit(batch_limit)
|
218
|
-
relation = apply_limits(relation, start, finish)
|
224
|
+
relation = relation.reorder(batch_order(order)).limit(batch_limit)
|
225
|
+
relation = apply_limits(relation, start, finish, order)
|
219
226
|
relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
|
220
227
|
batch_relation = relation
|
221
228
|
|
@@ -252,28 +259,28 @@ module ActiveRecord
|
|
252
259
|
end
|
253
260
|
|
254
261
|
batch_relation = relation.where(
|
255
|
-
|
262
|
+
predicate_builder[primary_key, primary_key_offset, order == :desc ? :lt : :gt]
|
256
263
|
)
|
257
264
|
end
|
258
265
|
end
|
259
266
|
|
260
267
|
private
|
261
|
-
def apply_limits(relation, start, finish)
|
262
|
-
relation = apply_start_limit(relation, start) if start
|
263
|
-
relation = apply_finish_limit(relation, finish) if finish
|
268
|
+
def apply_limits(relation, start, finish, order)
|
269
|
+
relation = apply_start_limit(relation, start, order) if start
|
270
|
+
relation = apply_finish_limit(relation, finish, order) if finish
|
264
271
|
relation
|
265
272
|
end
|
266
273
|
|
267
|
-
def apply_start_limit(relation, start)
|
268
|
-
relation.where(
|
274
|
+
def apply_start_limit(relation, start, order)
|
275
|
+
relation.where(predicate_builder[primary_key, start, order == :desc ? :lteq : :gteq])
|
269
276
|
end
|
270
277
|
|
271
|
-
def apply_finish_limit(relation, finish)
|
272
|
-
relation.where(
|
278
|
+
def apply_finish_limit(relation, finish, order)
|
279
|
+
relation.where(predicate_builder[primary_key, finish, order == :desc ? :gteq : :lteq])
|
273
280
|
end
|
274
281
|
|
275
|
-
def batch_order
|
276
|
-
|
282
|
+
def batch_order(order)
|
283
|
+
table[primary_key].public_send(order)
|
277
284
|
end
|
278
285
|
|
279
286
|
def act_on_ignored_order(error_on_ignore)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Calculations
|
5
7
|
# Count the records.
|
@@ -179,7 +181,7 @@ module ActiveRecord
|
|
179
181
|
# See also #ids.
|
180
182
|
#
|
181
183
|
def pluck(*column_names)
|
182
|
-
if loaded? && (column_names
|
184
|
+
if loaded? && all_attributes?(column_names)
|
183
185
|
return records.pluck(*column_names)
|
184
186
|
end
|
185
187
|
|
@@ -188,10 +190,17 @@ module ActiveRecord
|
|
188
190
|
relation.pluck(*column_names)
|
189
191
|
else
|
190
192
|
klass.disallow_raw_sql!(column_names)
|
193
|
+
columns = arel_columns(column_names)
|
191
194
|
relation = spawn
|
192
|
-
relation.select_values =
|
193
|
-
result = skip_query_cache_if_necessary
|
194
|
-
|
195
|
+
relation.select_values = columns
|
196
|
+
result = skip_query_cache_if_necessary do
|
197
|
+
if where_clause.contradiction?
|
198
|
+
ActiveRecord::Result.new([], [])
|
199
|
+
else
|
200
|
+
klass.connection.select_all(relation.arel, nil)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
type_cast_pluck_values(result, columns)
|
195
204
|
end
|
196
205
|
end
|
197
206
|
|
@@ -210,6 +219,10 @@ module ActiveRecord
|
|
210
219
|
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
|
211
220
|
# # => [ 'David', 'david@loudthinking.com' ]
|
212
221
|
def pick(*column_names)
|
222
|
+
if loaded? && all_attributes?(column_names)
|
223
|
+
return records.pick(*column_names)
|
224
|
+
end
|
225
|
+
|
213
226
|
limit(1).pluck(*column_names).first
|
214
227
|
end
|
215
228
|
|
@@ -222,6 +235,10 @@ module ActiveRecord
|
|
222
235
|
end
|
223
236
|
|
224
237
|
private
|
238
|
+
def all_attributes?(column_names)
|
239
|
+
(column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
|
240
|
+
end
|
241
|
+
|
225
242
|
def has_include?(column_name)
|
226
243
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
227
244
|
end
|
@@ -266,12 +283,10 @@ module ActiveRecord
|
|
266
283
|
end
|
267
284
|
|
268
285
|
def operation_over_aggregate_column(column, operation, distinct)
|
269
|
-
operation == "count" ? column.count(distinct) : column.
|
286
|
+
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
270
287
|
end
|
271
288
|
|
272
289
|
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
273
|
-
column_alias = column_name
|
274
|
-
|
275
290
|
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
276
291
|
# Shortcut when limit is zero.
|
277
292
|
return 0 if limit_value == 0
|
@@ -282,31 +297,35 @@ module ActiveRecord
|
|
282
297
|
relation = unscope(:order).distinct!(false)
|
283
298
|
|
284
299
|
column = aggregate_column(column_name)
|
285
|
-
|
286
300
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
287
|
-
if operation == "sum" && distinct
|
288
|
-
select_value.distinct = true
|
289
|
-
end
|
301
|
+
select_value.distinct = true if operation == "sum" && distinct
|
290
302
|
|
291
|
-
column_alias = select_value.alias
|
292
|
-
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
293
303
|
relation.select_values = [select_value]
|
294
304
|
|
295
305
|
query_builder = relation.arel
|
296
306
|
end
|
297
307
|
|
298
|
-
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder
|
299
|
-
row = result.first
|
300
|
-
value = row && row.values.first
|
301
|
-
type = result.column_types.fetch(column_alias) do
|
302
|
-
type_for(column_name)
|
303
|
-
end
|
308
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
304
309
|
|
305
|
-
type_cast_calculated_value(
|
310
|
+
type_cast_calculated_value(result.cast_values.first, operation) do |value|
|
311
|
+
type = column.try(:type_caster) ||
|
312
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
|
+
type.deserialize(value)
|
314
|
+
end
|
306
315
|
end
|
307
316
|
|
308
317
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
309
318
|
group_fields = group_values
|
319
|
+
group_fields = group_fields.uniq if group_fields.size > 1
|
320
|
+
|
321
|
+
unless group_fields == group_values
|
322
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
323
|
+
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 6.2.
|
324
|
+
To migrate to Rails 6.2's behavior, use `uniq!(:group)` to deduplicate group fields
|
325
|
+
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
326
|
+
MSG
|
327
|
+
group_fields = group_values
|
328
|
+
end
|
310
329
|
|
311
330
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
312
331
|
association = klass._reflect_on_association(group_fields.first)
|
@@ -321,14 +340,12 @@ module ActiveRecord
|
|
321
340
|
}
|
322
341
|
group_columns = group_aliases.zip(group_fields)
|
323
342
|
|
324
|
-
|
343
|
+
column = aggregate_column(column_name)
|
344
|
+
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
345
|
+
select_value = operation_over_aggregate_column(column, operation, distinct)
|
346
|
+
select_value.as(column_alias)
|
325
347
|
|
326
|
-
select_values = [
|
327
|
-
operation_over_aggregate_column(
|
328
|
-
aggregate_column(column_name),
|
329
|
-
operation,
|
330
|
-
distinct).as(aggregate_alias)
|
331
|
-
]
|
348
|
+
select_values = [select_value]
|
332
349
|
select_values += self.select_values unless having_clause.empty?
|
333
350
|
|
334
351
|
select_values.concat group_columns.map { |aliaz, field|
|
@@ -348,22 +365,33 @@ module ActiveRecord
|
|
348
365
|
if association
|
349
366
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
350
367
|
key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
|
351
|
-
key_records =
|
368
|
+
key_records = key_records.index_by(&:id)
|
352
369
|
end
|
353
370
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
371
|
+
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
372
|
+
types[aliaz] = type_for(col_name) do
|
373
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
378
|
+
calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
|
379
|
+
hash[col_name] = row[i]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
type = nil
|
384
|
+
hash_rows.each_with_object({}) do |row, result|
|
385
|
+
key = group_aliases.map { |aliaz| row[aliaz] }
|
361
386
|
key = key.first if key.size == 1
|
362
387
|
key = key_records[key] if associated
|
363
388
|
|
364
|
-
|
365
|
-
|
366
|
-
|
389
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation) do |value|
|
390
|
+
type ||= column.try(:type_caster) ||
|
391
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
392
|
+
type.deserialize(value)
|
393
|
+
end
|
394
|
+
end
|
367
395
|
end
|
368
396
|
|
369
397
|
# Converts the given field to the value that the database adapter returns as
|
@@ -388,12 +416,41 @@ module ActiveRecord
|
|
388
416
|
@klass.type_for_attribute(field_name, &block)
|
389
417
|
end
|
390
418
|
|
391
|
-
def
|
419
|
+
def lookup_cast_type_from_join_dependencies(name, join_dependencies = build_join_dependencies)
|
420
|
+
each_join_dependencies(join_dependencies) do |join|
|
421
|
+
type = join.base_klass.attribute_types.fetch(name, nil)
|
422
|
+
return type if type
|
423
|
+
end
|
424
|
+
nil
|
425
|
+
end
|
426
|
+
|
427
|
+
def type_cast_pluck_values(result, columns)
|
428
|
+
cast_types = if result.columns.size != columns.size
|
429
|
+
klass.attribute_types
|
430
|
+
else
|
431
|
+
join_dependencies = nil
|
432
|
+
columns.map.with_index do |column, i|
|
433
|
+
column.try(:type_caster) ||
|
434
|
+
klass.attribute_types.fetch(name = result.columns[i]) do
|
435
|
+
join_dependencies ||= build_join_dependencies
|
436
|
+
lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
|
437
|
+
result.column_types[name] || Type.default_value
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
result.cast_values(cast_types)
|
442
|
+
end
|
443
|
+
|
444
|
+
def type_cast_calculated_value(value, operation)
|
392
445
|
case operation
|
393
|
-
when "count"
|
394
|
-
|
395
|
-
when "
|
396
|
-
|
446
|
+
when "count"
|
447
|
+
value.to_i
|
448
|
+
when "sum"
|
449
|
+
yield value || 0
|
450
|
+
when "average"
|
451
|
+
value&.respond_to?(:to_d) ? value.to_d : value
|
452
|
+
else # "minimum", "maximum"
|
453
|
+
yield value
|
397
454
|
end
|
398
455
|
end
|
399
456
|
|