activerecord 5.2.8.1 → 6.1.6.1
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 +1255 -596
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +9 -8
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +100 -41
- data/lib/active_record/associations/association_scope.rb +23 -21
- data/lib/active_record/associations/belongs_to_association.rb +55 -48
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
- data/lib/active_record/associations/builder/association.rb +45 -22
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +8 -17
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +44 -34
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +24 -18
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/preloader/association.rb +69 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/preloader.rb +47 -34
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +137 -25
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attributes.rb +46 -9
- data/lib/active_record/autosave_association.rb +57 -42
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
- data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
- data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- 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 +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
- data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +293 -33
- data/lib/active_record/core.rb +333 -98
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +273 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +108 -36
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- 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 +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +200 -481
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +28 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +37 -23
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +35 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +145 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +206 -157
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +116 -59
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +411 -80
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +109 -93
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/calculations.rb +157 -90
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +64 -39
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +62 -45
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +476 -187
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +115 -62
- data/lib/active_record/relation.rb +379 -115
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +4 -8
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/scoping.rb +8 -9
- 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 +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +277 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +62 -118
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +13 -12
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -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 +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -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 +15 -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 +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -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 +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -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 +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -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_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- 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
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +116 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -4,42 +4,57 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class ThroughAssociation < Association # :nodoc:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
PRELOADER = ActiveRecord::Associations::Preloader.new(associate_by_default: false)
|
8
|
+
|
9
|
+
def initialize(*)
|
10
|
+
super
|
11
|
+
@already_loaded = owners.first.association(through_reflection.name).loaded?
|
12
|
+
end
|
13
|
+
|
14
|
+
def preloaded_records
|
15
|
+
@preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
|
16
|
+
end
|
17
|
+
|
18
|
+
def records_by_owner
|
19
|
+
return @records_by_owner if defined?(@records_by_owner)
|
20
|
+
source_records_by_owner = source_preloaders.map(&:records_by_owner).reduce(:merge)
|
21
|
+
through_records_by_owner = through_preloaders.map(&:records_by_owner).reduce(:merge)
|
22
|
+
|
23
|
+
@records_by_owner = owners.each_with_object({}) do |owner, result|
|
24
|
+
through_records = through_records_by_owner[owner] || []
|
25
|
+
|
26
|
+
if @already_loaded
|
19
27
|
if source_type = reflection.options[:source_type]
|
20
28
|
through_records = through_records.select do |record|
|
21
29
|
record[reflection.foreign_type] == source_type
|
22
30
|
end
|
23
31
|
end
|
24
|
-
else
|
25
|
-
owner.association(through_reflection.name).reset if through_scope
|
26
|
-
end
|
27
|
-
result = through_records.flat_map do |record|
|
28
|
-
association = record.association(source_reflection.name)
|
29
|
-
target = association.target
|
30
|
-
association.reset if preload_scope
|
31
|
-
target
|
32
32
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
result.uniq! if reflection_scope.distinct_value
|
33
|
+
|
34
|
+
records = through_records.flat_map do |record|
|
35
|
+
source_records_by_owner[record]
|
37
36
|
end
|
38
|
-
|
37
|
+
|
38
|
+
records.compact!
|
39
|
+
records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
|
40
|
+
records.uniq! if scope.distinct_value
|
41
|
+
result[owner] = records
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
private
|
46
|
+
def source_preloaders
|
47
|
+
@source_preloaders ||= PRELOADER.preload(middle_records, source_reflection.name, scope)
|
48
|
+
end
|
49
|
+
|
50
|
+
def middle_records
|
51
|
+
through_preloaders.flat_map(&:preloaded_records)
|
52
|
+
end
|
53
|
+
|
54
|
+
def through_preloaders
|
55
|
+
@through_preloaders ||= PRELOADER.preload(owners, through_reflection.name, through_scope)
|
56
|
+
end
|
57
|
+
|
43
58
|
def through_reflection
|
44
59
|
reflection.through_reflection
|
45
60
|
end
|
@@ -49,8 +64,8 @@ module ActiveRecord
|
|
49
64
|
end
|
50
65
|
|
51
66
|
def preload_index
|
52
|
-
@preload_index ||=
|
53
|
-
result[
|
67
|
+
@preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
|
68
|
+
result[record] = index
|
54
69
|
end
|
55
70
|
end
|
56
71
|
|
@@ -58,11 +73,15 @@ module ActiveRecord
|
|
58
73
|
scope = through_reflection.klass.unscoped
|
59
74
|
options = reflection.options
|
60
75
|
|
76
|
+
values = reflection_scope.values
|
77
|
+
if annotations = values[:annotate]
|
78
|
+
scope.annotate!(*annotations)
|
79
|
+
end
|
80
|
+
|
61
81
|
if options[:source_type]
|
62
82
|
scope.where! reflection.foreign_type => options[:source_type]
|
63
83
|
elsif !reflection_scope.where_clause.empty?
|
64
84
|
scope.where_clause = reflection_scope.where_clause
|
65
|
-
values = reflection_scope.values
|
66
85
|
|
67
86
|
if includes = values[:includes]
|
68
87
|
scope.includes!(source_reflection.name => includes)
|
@@ -71,7 +90,7 @@ module ActiveRecord
|
|
71
90
|
end
|
72
91
|
|
73
92
|
if values[:references] && !values[:references].empty?
|
74
|
-
scope.
|
93
|
+
scope.references_values |= values[:references]
|
75
94
|
else
|
76
95
|
scope.references!(source_reflection.table_name)
|
77
96
|
end
|
@@ -89,17 +108,7 @@ module ActiveRecord
|
|
89
108
|
end
|
90
109
|
end
|
91
110
|
|
92
|
-
scope
|
93
|
-
end
|
94
|
-
|
95
|
-
def target_reflection_scope
|
96
|
-
if preload_scope
|
97
|
-
reflection_scope.merge(preload_scope)
|
98
|
-
elsif reflection.scope
|
99
|
-
reflection_scope
|
100
|
-
else
|
101
|
-
nil
|
102
|
-
end
|
111
|
+
scope
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
@@ -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 Associations
|
5
7
|
# Implements the details of eager loading of Active Record associations.
|
@@ -58,7 +60,7 @@ module ActiveRecord
|
|
58
60
|
# == Parameters
|
59
61
|
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
60
62
|
# i.e. +records+ itself may also contain arrays of records. In any case,
|
61
|
-
# +preload_associations+ will preload
|
63
|
+
# +preload_associations+ will preload all associations records by
|
62
64
|
# flattening +records+.
|
63
65
|
#
|
64
66
|
# +associations+ specifies one or more associations that you want to
|
@@ -88,44 +90,46 @@ module ActiveRecord
|
|
88
90
|
if records.empty?
|
89
91
|
[]
|
90
92
|
else
|
91
|
-
records.uniq!
|
92
93
|
Array.wrap(associations).flat_map { |association|
|
93
94
|
preloaders_on association, records, preload_scope
|
94
95
|
}
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
|
-
|
99
|
+
def initialize(associate_by_default: true)
|
100
|
+
@associate_by_default = associate_by_default
|
101
|
+
end
|
99
102
|
|
103
|
+
private
|
100
104
|
# Loads all the given data into +records+ for the +association+.
|
101
|
-
def preloaders_on(association, records, scope)
|
105
|
+
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
102
106
|
case association
|
103
107
|
when Hash
|
104
|
-
preloaders_for_hash(association, records, scope)
|
105
|
-
when Symbol
|
106
|
-
preloaders_for_one(association, records, scope)
|
107
|
-
when String
|
108
|
-
preloaders_for_one(association.to_sym, records, scope)
|
108
|
+
preloaders_for_hash(association, records, scope, polymorphic_parent)
|
109
|
+
when Symbol, String
|
110
|
+
preloaders_for_one(association, records, scope, polymorphic_parent)
|
109
111
|
else
|
110
112
|
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
114
|
-
def preloaders_for_hash(association, records, scope)
|
116
|
+
def preloaders_for_hash(association, records, scope, polymorphic_parent)
|
115
117
|
association.flat_map { |parent, child|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
|
119
|
+
loaders = preloaders_for_reflection(reflection, reflection_records, scope)
|
120
|
+
recs = loaders.flat_map(&:preloaded_records).uniq
|
121
|
+
child_polymorphic_parent = reflection && reflection.options[:polymorphic]
|
122
|
+
loaders.concat Array.wrap(child).flat_map { |assoc|
|
123
|
+
preloaders_on assoc, recs, scope, child_polymorphic_parent
|
124
|
+
}
|
125
|
+
loaders
|
126
|
+
end
|
123
127
|
}
|
124
128
|
end
|
125
129
|
|
126
130
|
# Loads all the given data into +records+ for a singular +association+.
|
127
131
|
#
|
128
|
-
# Functions by instantiating a preloader class such as Preloader::
|
132
|
+
# Functions by instantiating a preloader class such as Preloader::Association and
|
129
133
|
# call the +run+ method for each passed in class in the +records+ argument.
|
130
134
|
#
|
131
135
|
# Not all records have the same class, so group then preload group on the reflection
|
@@ -135,41 +139,50 @@ module ActiveRecord
|
|
135
139
|
# Additionally, polymorphic belongs_to associations can have multiple associated
|
136
140
|
# classes, depending on the polymorphic_type field. So we group by the classes as
|
137
141
|
# well.
|
138
|
-
def preloaders_for_one(association, records, scope)
|
139
|
-
grouped_records(association, records
|
140
|
-
|
141
|
-
|
142
|
-
loader.run self
|
143
|
-
loader
|
142
|
+
def preloaders_for_one(association, records, scope, polymorphic_parent)
|
143
|
+
grouped_records(association, records, polymorphic_parent)
|
144
|
+
.flat_map do |reflection, reflection_records|
|
145
|
+
preloaders_for_reflection reflection, reflection_records, scope
|
144
146
|
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def preloaders_for_reflection(reflection, records, scope)
|
150
|
+
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
151
|
+
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, @associate_by_default).run
|
145
152
|
end
|
146
153
|
end
|
147
154
|
|
148
|
-
def grouped_records(association, records)
|
155
|
+
def grouped_records(association, records, polymorphic_parent)
|
149
156
|
h = {}
|
150
157
|
records.each do |record|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
klasses = h[assoc.reflection] ||= {}
|
155
|
-
(klasses[assoc.klass] ||= []) << record
|
158
|
+
reflection = record.class._reflect_on_association(association)
|
159
|
+
next if polymorphic_parent && !reflection || !record.association(association).klass
|
160
|
+
(h[reflection] ||= []) << record
|
156
161
|
end
|
157
162
|
h
|
158
163
|
end
|
159
164
|
|
160
165
|
class AlreadyLoaded # :nodoc:
|
161
|
-
def initialize(klass, owners, reflection, preload_scope)
|
166
|
+
def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
|
162
167
|
@owners = owners
|
163
168
|
@reflection = reflection
|
164
169
|
end
|
165
170
|
|
166
|
-
def run
|
171
|
+
def run
|
172
|
+
self
|
173
|
+
end
|
167
174
|
|
168
175
|
def preloaded_records
|
169
|
-
|
176
|
+
@preloaded_records ||= records_by_owner.flat_map(&:last)
|
177
|
+
end
|
178
|
+
|
179
|
+
def records_by_owner
|
180
|
+
@records_by_owner ||= owners.index_with do |owner|
|
181
|
+
Array(owner.association(reflection.name).target)
|
182
|
+
end
|
170
183
|
end
|
171
184
|
|
172
|
-
|
185
|
+
private
|
173
186
|
attr_reader :owners, :reflection
|
174
187
|
end
|
175
188
|
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
replace(record)
|
18
18
|
end
|
19
19
|
|
20
|
-
def build(attributes =
|
20
|
+
def build(attributes = nil, &block)
|
21
21
|
record = build_record(attributes, &block)
|
22
22
|
set_new_record(record)
|
23
23
|
record
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
# Implements the reload reader method, e.g. foo.reload_bar for
|
27
27
|
# Foo.has_one :bar
|
28
28
|
def force_reload_reader
|
29
|
-
|
29
|
+
reload(true)
|
30
30
|
target
|
31
31
|
end
|
32
32
|
|
@@ -36,21 +36,7 @@ module ActiveRecord
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def find_target
|
39
|
-
|
40
|
-
return scope.take if skip_statement_cache?(scope)
|
41
|
-
|
42
|
-
conn = klass.connection
|
43
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
44
|
-
as = AssociationScope.create { params.bind }
|
45
|
-
target_scope.merge!(as.scope(self)).limit(1)
|
46
|
-
end
|
47
|
-
|
48
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
49
|
-
sc.execute(binds, conn) do |record|
|
50
|
-
set_inverse_instance record
|
51
|
-
end.first
|
52
|
-
rescue ::RangeError
|
53
|
-
nil
|
39
|
+
super.first
|
54
40
|
end
|
55
41
|
|
56
42
|
def replace(record)
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
reflection.chain.drop(1).each do |reflection|
|
33
33
|
relation = reflection.klass.scope_for_association
|
34
34
|
scope.merge!(
|
35
|
-
relation.except(:select, :create_with, :includes, :preload, :joins, :
|
35
|
+
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
|
36
36
|
)
|
37
37
|
end
|
38
38
|
scope
|
@@ -2,38 +2,116 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
4
4
|
require "active_support/core_ext/string/conversions"
|
5
|
-
require "active_support/core_ext/module/remove_method"
|
6
|
-
require "active_record/errors"
|
7
5
|
|
8
6
|
module ActiveRecord
|
9
7
|
class AssociationNotFoundError < ConfigurationError #:nodoc:
|
8
|
+
attr_reader :record, :association_name
|
10
9
|
def initialize(record = nil, association_name = nil)
|
10
|
+
@record = record
|
11
|
+
@association_name = association_name
|
11
12
|
if record && association_name
|
12
13
|
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
13
14
|
else
|
14
15
|
super("Association was not found.")
|
15
16
|
end
|
16
17
|
end
|
18
|
+
|
19
|
+
class Correction
|
20
|
+
def initialize(error)
|
21
|
+
@error = error
|
22
|
+
end
|
23
|
+
|
24
|
+
def corrections
|
25
|
+
if @error.association_name
|
26
|
+
maybe_these = @error.record.class.reflections.keys
|
27
|
+
|
28
|
+
maybe_these.sort_by { |n|
|
29
|
+
DidYouMean::Jaro.distance(@error.association_name.to_s, n)
|
30
|
+
}.reverse.first(4)
|
31
|
+
else
|
32
|
+
[]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
38
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
39
|
+
DidYouMean.correct_error(self, Correction)
|
40
|
+
end
|
17
41
|
end
|
18
42
|
|
19
43
|
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
44
|
+
attr_reader :reflection, :associated_class
|
20
45
|
def initialize(reflection = nil, associated_class = nil)
|
21
46
|
if reflection
|
47
|
+
@reflection = reflection
|
48
|
+
@associated_class = associated_class.nil? ? reflection.klass : associated_class
|
22
49
|
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
23
50
|
else
|
24
51
|
super("Could not find the inverse association.")
|
25
52
|
end
|
26
53
|
end
|
54
|
+
|
55
|
+
class Correction
|
56
|
+
def initialize(error)
|
57
|
+
@error = error
|
58
|
+
end
|
59
|
+
|
60
|
+
def corrections
|
61
|
+
if @error.reflection && @error.associated_class
|
62
|
+
maybe_these = @error.associated_class.reflections.keys
|
63
|
+
|
64
|
+
maybe_these.sort_by { |n|
|
65
|
+
DidYouMean::Jaro.distance(@error.reflection.options[:inverse_of].to_s, n)
|
66
|
+
}.reverse.first(4)
|
67
|
+
else
|
68
|
+
[]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
74
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
75
|
+
DidYouMean.correct_error(self, Correction)
|
76
|
+
end
|
27
77
|
end
|
28
78
|
|
29
79
|
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
|
30
|
-
|
31
|
-
|
32
|
-
|
80
|
+
attr_reader :owner_class, :reflection
|
81
|
+
|
82
|
+
def initialize(owner_class = nil, reflection = nil)
|
83
|
+
if owner_class && reflection
|
84
|
+
@owner_class = owner_class
|
85
|
+
@reflection = reflection
|
86
|
+
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
|
33
87
|
else
|
34
88
|
super("Could not find the association.")
|
35
89
|
end
|
36
90
|
end
|
91
|
+
|
92
|
+
class Correction
|
93
|
+
def initialize(error)
|
94
|
+
@error = error
|
95
|
+
end
|
96
|
+
|
97
|
+
def corrections
|
98
|
+
if @error.reflection && @error.owner_class
|
99
|
+
maybe_these = @error.owner_class.reflections.keys
|
100
|
+
maybe_these -= [@error.reflection.name.to_s] # remove failing reflection
|
101
|
+
|
102
|
+
maybe_these.sort_by { |n|
|
103
|
+
DidYouMean::Jaro.distance(@error.reflection.options[:through].to_s, n)
|
104
|
+
}.reverse.first(4)
|
105
|
+
else
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
112
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
113
|
+
DidYouMean.correct_error(self, Correction)
|
114
|
+
end
|
37
115
|
end
|
38
116
|
|
39
117
|
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
|
@@ -92,7 +170,7 @@ module ActiveRecord
|
|
92
170
|
through_reflection = reflection.through_reflection
|
93
171
|
source_reflection_names = reflection.source_reflection_names
|
94
172
|
source_associations = reflection.through_reflection.klass._reflections.keys
|
95
|
-
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or '
|
173
|
+
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
|
96
174
|
else
|
97
175
|
super("Could not find the source association(s).")
|
98
176
|
end
|
@@ -292,13 +370,13 @@ module ActiveRecord
|
|
292
370
|
#
|
293
371
|
# The project class now has the following methods (and more) to ease the traversal and
|
294
372
|
# manipulation of its relationships:
|
295
|
-
# * <tt>Project#portfolio
|
296
|
-
# * <tt>Project#project_manager
|
297
|
-
# * <tt>Project#milestones.empty
|
298
|
-
# <tt>Project#milestones.delete(milestone)
|
299
|
-
# <tt>Project#milestones.build
|
300
|
-
# * <tt>Project#categories.empty
|
301
|
-
# <tt>Project#categories.delete(category1)
|
373
|
+
# * <tt>Project#portfolio</tt>, <tt>Project#portfolio=(portfolio)</tt>, <tt>Project#reload_portfolio</tt>
|
374
|
+
# * <tt>Project#project_manager</tt>, <tt>Project#project_manager=(project_manager)</tt>, <tt>Project#reload_project_manager</tt>
|
375
|
+
# * <tt>Project#milestones.empty?</tt>, <tt>Project#milestones.size</tt>, <tt>Project#milestones</tt>, <tt>Project#milestones<<(milestone)</tt>,
|
376
|
+
# <tt>Project#milestones.delete(milestone)</tt>, <tt>Project#milestones.destroy(milestone)</tt>, <tt>Project#milestones.find(milestone_id)</tt>,
|
377
|
+
# <tt>Project#milestones.build</tt>, <tt>Project#milestones.create</tt>
|
378
|
+
# * <tt>Project#categories.empty?</tt>, <tt>Project#categories.size</tt>, <tt>Project#categories</tt>, <tt>Project#categories<<(category1)</tt>,
|
379
|
+
# <tt>Project#categories.delete(category1)</tt>, <tt>Project#categories.destroy(category1)</tt>
|
302
380
|
#
|
303
381
|
# === A word of warning
|
304
382
|
#
|
@@ -702,9 +780,9 @@ module ActiveRecord
|
|
702
780
|
# inverse detection only works on #has_many, #has_one, and
|
703
781
|
# #belongs_to associations.
|
704
782
|
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
#
|
783
|
+
# <tt>:foreign_key</tt> and <tt>:through</tt> options on the associations,
|
784
|
+
# or a custom scope, will also prevent the association's inverse
|
785
|
+
# from being found automatically.
|
708
786
|
#
|
709
787
|
# The automatic guessing of the inverse association uses a heuristic based
|
710
788
|
# on the name of the class, so it may not work for all associations,
|
@@ -1291,10 +1369,15 @@ module ActiveRecord
|
|
1291
1369
|
# similar callbacks may affect the <tt>:dependent</tt> behavior, and the
|
1292
1370
|
# <tt>:dependent</tt> behavior may affect other callbacks.
|
1293
1371
|
#
|
1372
|
+
# * <tt>nil</tt> do nothing (default).
|
1294
1373
|
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
|
1374
|
+
# * <tt>:destroy_async</tt> destroys all the associated objects in a background job. <b>WARNING:</b> Do not use
|
1375
|
+
# this option if the association is backed by foreign key constraints in your database. The foreign key
|
1376
|
+
# constraint actions will occur inside the same transaction that deletes its owner.
|
1295
1377
|
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
|
1296
|
-
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+.
|
1297
|
-
#
|
1378
|
+
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
|
1379
|
+
# on polymorphic associations. Callbacks are not executed.
|
1380
|
+
# * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
|
1298
1381
|
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
|
1299
1382
|
#
|
1300
1383
|
# If using with the <tt>:through</tt> option, the association on the join model must be
|
@@ -1355,6 +1438,11 @@ module ActiveRecord
|
|
1355
1438
|
# Specifies a module or array of modules that will be extended into the association object returned.
|
1356
1439
|
# Useful for defining methods on associations, especially when they should be shared between multiple
|
1357
1440
|
# association objects.
|
1441
|
+
# [:strict_loading]
|
1442
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1443
|
+
# [:ensuring_owner_was]
|
1444
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1445
|
+
# associated records to be deleted in a background job.
|
1358
1446
|
#
|
1359
1447
|
# Option examples:
|
1360
1448
|
# has_many :comments, -> { order("posted_on") }
|
@@ -1365,6 +1453,7 @@ module ActiveRecord
|
|
1365
1453
|
# has_many :tags, as: :taggable
|
1366
1454
|
# has_many :reports, -> { readonly }
|
1367
1455
|
# has_many :subscribers, through: :subscriptions, source: :user
|
1456
|
+
# has_many :comments, strict_loading: true
|
1368
1457
|
def has_many(name, scope = nil, **options, &extension)
|
1369
1458
|
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
|
1370
1459
|
Reflection.add_reflection self, name, reflection
|
@@ -1434,10 +1523,15 @@ module ActiveRecord
|
|
1434
1523
|
# Controls what happens to the associated object when
|
1435
1524
|
# its owner is destroyed:
|
1436
1525
|
#
|
1526
|
+
# * <tt>nil</tt> do nothing (default).
|
1437
1527
|
# * <tt>:destroy</tt> causes the associated object to also be destroyed
|
1528
|
+
# * <tt>:destroy_async</tt> causes the associated object to be destroyed in a background job. <b>WARNING:</b> Do not use
|
1529
|
+
# this option if the association is backed by foreign key constraints in your database. The foreign key
|
1530
|
+
# constraint actions will occur inside the same transaction that deletes its owner.
|
1438
1531
|
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
|
1439
|
-
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+.
|
1440
|
-
#
|
1532
|
+
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
|
1533
|
+
# on polymorphic associations. Callbacks are not executed.
|
1534
|
+
# * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
|
1441
1535
|
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
|
1442
1536
|
#
|
1443
1537
|
# Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
|
@@ -1492,6 +1586,11 @@ module ActiveRecord
|
|
1492
1586
|
# When set to +true+, the association will also have its presence validated.
|
1493
1587
|
# This will validate the association itself, not the id. You can use
|
1494
1588
|
# +:inverse_of+ to avoid an extra query during validation.
|
1589
|
+
# [:strict_loading]
|
1590
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1591
|
+
# [:ensuring_owner_was]
|
1592
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1593
|
+
# associated records to be deleted in a background job.
|
1495
1594
|
#
|
1496
1595
|
# Option examples:
|
1497
1596
|
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
|
@@ -1504,6 +1603,7 @@ module ActiveRecord
|
|
1504
1603
|
# has_one :club, through: :membership
|
1505
1604
|
# has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
|
1506
1605
|
# has_one :credit_card, required: true
|
1606
|
+
# has_one :credit_card, strict_loading: true
|
1507
1607
|
def has_one(name, scope = nil, **options)
|
1508
1608
|
reflection = Builder::HasOne.build(self, name, scope, options)
|
1509
1609
|
Reflection.add_reflection self, name, reflection
|
@@ -1524,6 +1624,7 @@ module ActiveRecord
|
|
1524
1624
|
# Returns the associated object. +nil+ is returned if none is found.
|
1525
1625
|
# [association=(associate)]
|
1526
1626
|
# Assigns the associate object, extracts the primary key, and sets it as the foreign key.
|
1627
|
+
# No modification or deletion of existing records takes place.
|
1527
1628
|
# [build_association(attributes = {})]
|
1528
1629
|
# Returns a new object of the associated type that has been instantiated
|
1529
1630
|
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
|
@@ -1581,10 +1682,11 @@ module ActiveRecord
|
|
1581
1682
|
# association will use "taggable_type" as the default <tt>:foreign_type</tt>.
|
1582
1683
|
# [:primary_key]
|
1583
1684
|
# Specify the method that returns the primary key of associated object used for the association.
|
1584
|
-
# By default this is id
|
1685
|
+
# By default this is +id+.
|
1585
1686
|
# [:dependent]
|
1586
1687
|
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
|
1587
|
-
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
|
1688
|
+
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to
|
1689
|
+
# <tt>:destroy_async</tt>, the associated object is scheduled to be destroyed in a background job.
|
1588
1690
|
# This option should not be specified when #belongs_to is used in conjunction with
|
1589
1691
|
# a #has_many relationship on another class because of the potential to leave
|
1590
1692
|
# orphaned records behind.
|
@@ -1636,6 +1738,11 @@ module ActiveRecord
|
|
1636
1738
|
# [:default]
|
1637
1739
|
# Provide a callable (i.e. proc or lambda) to specify that the association should
|
1638
1740
|
# be initialized with a particular record before validation.
|
1741
|
+
# [:strict_loading]
|
1742
|
+
# Enforces strict loading every time the associated record is loaded through this association.
|
1743
|
+
# [:ensuring_owner_was]
|
1744
|
+
# Specifies an instance method to be called on the owner. The method must return true in order for the
|
1745
|
+
# associated records to be deleted in a background job.
|
1639
1746
|
#
|
1640
1747
|
# Option examples:
|
1641
1748
|
# belongs_to :firm, foreign_key: "client_of"
|
@@ -1650,6 +1757,7 @@ module ActiveRecord
|
|
1650
1757
|
# belongs_to :company, touch: :employees_last_updated_at
|
1651
1758
|
# belongs_to :user, optional: true
|
1652
1759
|
# belongs_to :account, default: -> { company.account }
|
1760
|
+
# belongs_to :account, strict_loading: true
|
1653
1761
|
def belongs_to(name, scope = nil, **options)
|
1654
1762
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1655
1763
|
Reflection.add_reflection self, name, reflection
|
@@ -1672,7 +1780,7 @@ module ActiveRecord
|
|
1672
1780
|
# The join table should not have a primary key or a model associated with it. You must manually generate the
|
1673
1781
|
# join table with a migration such as this:
|
1674
1782
|
#
|
1675
|
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[
|
1783
|
+
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[6.0]
|
1676
1784
|
# def change
|
1677
1785
|
# create_join_table :developers, :projects
|
1678
1786
|
# end
|
@@ -1761,6 +1869,7 @@ module ActiveRecord
|
|
1761
1869
|
# has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
|
1762
1870
|
# has_and_belongs_to_many :categories, ->(post) {
|
1763
1871
|
# where("default_category = ?", post.default_category)
|
1872
|
+
# }
|
1764
1873
|
#
|
1765
1874
|
# === Extensions
|
1766
1875
|
#
|
@@ -1811,6 +1920,8 @@ module ActiveRecord
|
|
1811
1920
|
#
|
1812
1921
|
# Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
|
1813
1922
|
# <tt>:autosave</tt> to <tt>true</tt>.
|
1923
|
+
# [:strict_loading]
|
1924
|
+
# Enforces strict loading every time an associated record is loaded through this association.
|
1814
1925
|
#
|
1815
1926
|
# Option examples:
|
1816
1927
|
# has_and_belongs_to_many :projects
|
@@ -1818,6 +1929,7 @@ module ActiveRecord
|
|
1818
1929
|
# has_and_belongs_to_many :nations, class_name: "Country"
|
1819
1930
|
# has_and_belongs_to_many :categories, join_table: "prods_cats"
|
1820
1931
|
# has_and_belongs_to_many :categories, -> { readonly }
|
1932
|
+
# has_and_belongs_to_many :categories, strict_loading: true
|
1821
1933
|
def has_and_belongs_to_many(name, scope = nil, **options, &extension)
|
1822
1934
|
habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
|
1823
1935
|
|
@@ -1848,11 +1960,11 @@ module ActiveRecord
|
|
1848
1960
|
hm_options[:through] = middle_reflection.name
|
1849
1961
|
hm_options[:source] = join_model.right_reflection.name
|
1850
1962
|
|
1851
|
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
|
1963
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend, :strict_loading].each do |k|
|
1852
1964
|
hm_options[k] = options[k] if options.key? k
|
1853
1965
|
end
|
1854
1966
|
|
1855
|
-
has_many name, scope, hm_options, &extension
|
1967
|
+
has_many name, scope, **hm_options, &extension
|
1856
1968
|
_reflections[name.to_s].parent_reflection = habtm_reflection
|
1857
1969
|
end
|
1858
1970
|
end
|