activerecord 5.2.8 → 7.0.2
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 +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- 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 +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- 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 +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- 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 +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- 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 +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- 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 +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
- 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/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- 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 +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- 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 +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- 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 +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- 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 +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- 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 +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +9 -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 +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -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 +48 -0
- data/lib/arel/delete_manager.rb +32 -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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -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 +44 -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/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -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 +46 -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 +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -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 +55 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -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
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +162 -32
- 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
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Associations
|
5
|
+
class Preloader
|
6
|
+
class Branch # :nodoc:
|
7
|
+
attr_reader :association, :children, :parent
|
8
|
+
attr_reader :scope, :associate_by_default
|
9
|
+
attr_writer :preloaded_records
|
10
|
+
|
11
|
+
def initialize(association:, children:, parent:, associate_by_default:, scope:)
|
12
|
+
@association = association
|
13
|
+
@parent = parent
|
14
|
+
@scope = scope
|
15
|
+
@associate_by_default = associate_by_default
|
16
|
+
|
17
|
+
@children = build_children(children)
|
18
|
+
@loaders = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def future_classes
|
22
|
+
(immediate_future_classes + children.flat_map(&:future_classes)).uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
def immediate_future_classes
|
26
|
+
if parent.done?
|
27
|
+
loaders.flat_map(&:future_classes).uniq
|
28
|
+
else
|
29
|
+
likely_reflections.reject(&:polymorphic?).flat_map do |reflection|
|
30
|
+
reflection.
|
31
|
+
chain.
|
32
|
+
map(&:klass)
|
33
|
+
end.uniq
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def target_classes
|
38
|
+
if done?
|
39
|
+
preloaded_records.map(&:klass).uniq
|
40
|
+
elsif parent.done?
|
41
|
+
loaders.map(&:klass).uniq
|
42
|
+
else
|
43
|
+
likely_reflections.reject(&:polymorphic?).map(&:klass).uniq
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def likely_reflections
|
48
|
+
parent_classes = parent.target_classes
|
49
|
+
parent_classes.filter_map do |parent_klass|
|
50
|
+
parent_klass._reflect_on_association(@association)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def root?
|
55
|
+
parent.nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
def source_records
|
59
|
+
@parent.preloaded_records
|
60
|
+
end
|
61
|
+
|
62
|
+
def preloaded_records
|
63
|
+
@preloaded_records ||= loaders.flat_map(&:preloaded_records)
|
64
|
+
end
|
65
|
+
|
66
|
+
def done?
|
67
|
+
root? || (@loaders && @loaders.all?(&:run?))
|
68
|
+
end
|
69
|
+
|
70
|
+
def runnable_loaders
|
71
|
+
loaders.flat_map(&:runnable_loaders).reject(&:run?)
|
72
|
+
end
|
73
|
+
|
74
|
+
def grouped_records
|
75
|
+
h = {}
|
76
|
+
polymorphic_parent = !root? && parent.polymorphic?
|
77
|
+
source_records.each do |record|
|
78
|
+
reflection = record.class._reflect_on_association(association)
|
79
|
+
next if polymorphic_parent && !reflection || !record.association(association).klass
|
80
|
+
(h[reflection] ||= []) << record
|
81
|
+
end
|
82
|
+
h
|
83
|
+
end
|
84
|
+
|
85
|
+
def preloaders_for_reflection(reflection, reflection_records)
|
86
|
+
reflection_records.group_by do |record|
|
87
|
+
klass = record.association(association).klass
|
88
|
+
|
89
|
+
if reflection.scope && reflection.scope.arity != 0
|
90
|
+
# For instance dependent scopes, the scope is potentially
|
91
|
+
# different for each record. To allow this we'll group each
|
92
|
+
# object separately into its own preloader
|
93
|
+
reflection_scope = reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass, record).inject(&:merge!)
|
94
|
+
end
|
95
|
+
|
96
|
+
[klass, reflection_scope]
|
97
|
+
end.map do |(rhs_klass, reflection_scope), rs|
|
98
|
+
preloader_for(reflection).new(rhs_klass, rs, reflection, scope, reflection_scope, associate_by_default)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def polymorphic?
|
103
|
+
return false if root?
|
104
|
+
return @polymorphic if defined?(@polymorphic)
|
105
|
+
|
106
|
+
@polymorphic = source_records.any? do |record|
|
107
|
+
reflection = record.class._reflect_on_association(association)
|
108
|
+
reflection && reflection.options[:polymorphic]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def loaders
|
113
|
+
@loaders ||=
|
114
|
+
grouped_records.flat_map do |reflection, reflection_records|
|
115
|
+
preloaders_for_reflection(reflection, reflection_records)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def build_children(children)
|
121
|
+
Array.wrap(children).flat_map { |association|
|
122
|
+
Array(association).flat_map { |parent, child|
|
123
|
+
Branch.new(
|
124
|
+
parent: self,
|
125
|
+
association: parent,
|
126
|
+
children: child,
|
127
|
+
associate_by_default: associate_by_default,
|
128
|
+
scope: scope
|
129
|
+
)
|
130
|
+
}
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a class containing the logic needed to load preload the data
|
135
|
+
# and attach it to a relation. The class returned implements a `run` method
|
136
|
+
# that accepts a preloader.
|
137
|
+
def preloader_for(reflection)
|
138
|
+
if reflection.options[:through]
|
139
|
+
ThroughAssociation
|
140
|
+
else
|
141
|
+
Association
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -4,42 +4,83 @@ module ActiveRecord
|
|
4
4
|
module Associations
|
5
5
|
class Preloader
|
6
6
|
class ThroughAssociation < Association # :nodoc:
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
def preloaded_records
|
8
|
+
@preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
|
9
|
+
end
|
10
|
+
|
11
|
+
def records_by_owner
|
12
|
+
return @records_by_owner if defined?(@records_by_owner)
|
13
|
+
|
14
|
+
@records_by_owner = owners.each_with_object({}) do |owner, result|
|
15
|
+
if loaded?(owner)
|
16
|
+
result[owner] = target_for(owner)
|
17
|
+
next
|
18
|
+
end
|
19
|
+
|
20
|
+
through_records = through_records_by_owner[owner] || []
|
21
|
+
|
22
|
+
if owners.first.association(through_reflection.name).loaded?
|
19
23
|
if source_type = reflection.options[:source_type]
|
20
24
|
through_records = through_records.select do |record|
|
21
25
|
record[reflection.foreign_type] == source_type
|
22
26
|
end
|
23
27
|
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
28
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
result.uniq! if reflection_scope.distinct_value
|
29
|
+
|
30
|
+
records = through_records.flat_map do |record|
|
31
|
+
source_records_by_owner[record]
|
37
32
|
end
|
38
|
-
|
33
|
+
|
34
|
+
records.compact!
|
35
|
+
records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
|
36
|
+
records.uniq! if scope.distinct_value
|
37
|
+
result[owner] = records
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def runnable_loaders
|
42
|
+
if data_available?
|
43
|
+
[self]
|
44
|
+
elsif through_preloaders.all?(&:run?)
|
45
|
+
source_preloaders.flat_map(&:runnable_loaders)
|
46
|
+
else
|
47
|
+
through_preloaders.flat_map(&:runnable_loaders)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def future_classes
|
52
|
+
if run?
|
53
|
+
[]
|
54
|
+
elsif through_preloaders.all?(&:run?)
|
55
|
+
source_preloaders.flat_map(&:future_classes).uniq
|
56
|
+
else
|
57
|
+
through_classes = through_preloaders.flat_map(&:future_classes)
|
58
|
+
source_classes = source_reflection.
|
59
|
+
chain.
|
60
|
+
reject { |reflection| reflection.respond_to?(:polymorphic?) && reflection.polymorphic? }.
|
61
|
+
map(&:klass)
|
62
|
+
(through_classes + source_classes).uniq
|
39
63
|
end
|
40
64
|
end
|
41
65
|
|
42
66
|
private
|
67
|
+
def data_available?
|
68
|
+
owners.all? { |owner| loaded?(owner) } ||
|
69
|
+
through_preloaders.all?(&:run?) && source_preloaders.all?(&:run?)
|
70
|
+
end
|
71
|
+
|
72
|
+
def source_preloaders
|
73
|
+
@source_preloaders ||= ActiveRecord::Associations::Preloader.new(records: middle_records, associations: source_reflection.name, scope: scope, associate_by_default: false).loaders
|
74
|
+
end
|
75
|
+
|
76
|
+
def middle_records
|
77
|
+
through_preloaders.flat_map(&:preloaded_records)
|
78
|
+
end
|
79
|
+
|
80
|
+
def through_preloaders
|
81
|
+
@through_preloaders ||= ActiveRecord::Associations::Preloader.new(records: owners, associations: through_reflection.name, scope: through_scope, associate_by_default: false).loaders
|
82
|
+
end
|
83
|
+
|
43
84
|
def through_reflection
|
44
85
|
reflection.through_reflection
|
45
86
|
end
|
@@ -48,9 +89,17 @@ module ActiveRecord
|
|
48
89
|
reflection.source_reflection
|
49
90
|
end
|
50
91
|
|
92
|
+
def source_records_by_owner
|
93
|
+
@source_records_by_owner ||= source_preloaders.map(&:records_by_owner).reduce(:merge)
|
94
|
+
end
|
95
|
+
|
96
|
+
def through_records_by_owner
|
97
|
+
@through_records_by_owner ||= through_preloaders.map(&:records_by_owner).reduce(:merge)
|
98
|
+
end
|
99
|
+
|
51
100
|
def preload_index
|
52
|
-
@preload_index ||=
|
53
|
-
result[
|
101
|
+
@preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
|
102
|
+
result[record] = index
|
54
103
|
end
|
55
104
|
end
|
56
105
|
|
@@ -58,11 +107,17 @@ module ActiveRecord
|
|
58
107
|
scope = through_reflection.klass.unscoped
|
59
108
|
options = reflection.options
|
60
109
|
|
110
|
+
return scope if options[:disable_joins]
|
111
|
+
|
112
|
+
values = reflection_scope.values
|
113
|
+
if annotations = values[:annotate]
|
114
|
+
scope.annotate!(*annotations)
|
115
|
+
end
|
116
|
+
|
61
117
|
if options[:source_type]
|
62
118
|
scope.where! reflection.foreign_type => options[:source_type]
|
63
119
|
elsif !reflection_scope.where_clause.empty?
|
64
120
|
scope.where_clause = reflection_scope.where_clause
|
65
|
-
values = reflection_scope.values
|
66
121
|
|
67
122
|
if includes = values[:includes]
|
68
123
|
scope.includes!(source_reflection.name => includes)
|
@@ -71,7 +126,7 @@ module ActiveRecord
|
|
71
126
|
end
|
72
127
|
|
73
128
|
if values[:references] && !values[:references].empty?
|
74
|
-
scope.
|
129
|
+
scope.references_values |= values[:references]
|
75
130
|
else
|
76
131
|
scope.references!(source_reflection.table_name)
|
77
132
|
end
|
@@ -89,17 +144,7 @@ module ActiveRecord
|
|
89
144
|
end
|
90
145
|
end
|
91
146
|
|
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
|
147
|
+
cascade_strict_loading(scope)
|
103
148
|
end
|
104
149
|
end
|
105
150
|
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.
|
@@ -39,15 +41,18 @@ module ActiveRecord
|
|
39
41
|
#
|
40
42
|
# This could result in many rows that contain redundant data and it performs poorly at scale
|
41
43
|
# and is therefore only used when necessary.
|
42
|
-
#
|
43
|
-
class Preloader #:nodoc:
|
44
|
+
class Preloader # :nodoc:
|
44
45
|
extend ActiveSupport::Autoload
|
45
46
|
|
46
47
|
eager_autoload do
|
47
48
|
autoload :Association, "active_record/associations/preloader/association"
|
49
|
+
autoload :Batch, "active_record/associations/preloader/batch"
|
50
|
+
autoload :Branch, "active_record/associations/preloader/branch"
|
48
51
|
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
49
52
|
end
|
50
53
|
|
54
|
+
attr_reader :records, :associations, :scope, :associate_by_default
|
55
|
+
|
51
56
|
# Eager loads the named associations for the given Active Record record(s).
|
52
57
|
#
|
53
58
|
# In this description, 'association name' shall refer to the name passed
|
@@ -58,7 +63,7 @@ module ActiveRecord
|
|
58
63
|
# == Parameters
|
59
64
|
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
60
65
|
# i.e. +records+ itself may also contain arrays of records. In any case,
|
61
|
-
# +preload_associations+ will preload
|
66
|
+
# +preload_associations+ will preload all associations records by
|
62
67
|
# flattening +records+.
|
63
68
|
#
|
64
69
|
# +associations+ specifies one or more associations that you want to
|
@@ -75,119 +80,53 @@ module ActiveRecord
|
|
75
80
|
# example, specifying <tt>{ author: :avatar }</tt> will preload a
|
76
81
|
# book's author, as well as that author's avatar.
|
77
82
|
#
|
78
|
-
# +:associations+ has the same format as the +:include+
|
79
|
-
# <tt>ActiveRecord::
|
83
|
+
# +:associations+ has the same format as the +:include+ method in
|
84
|
+
# <tt>ActiveRecord::QueryMethods</tt>. So +associations+ could look like this:
|
80
85
|
#
|
81
86
|
# :books
|
82
87
|
# [ :books, :author ]
|
83
88
|
# { author: :avatar }
|
84
89
|
# [ :books, { author: :avatar } ]
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
#
|
91
|
+
# +available_records+ is an array of ActiveRecord::Base. The Preloader
|
92
|
+
# will try to use the objects in this array to preload the requested
|
93
|
+
# associations before querying the database. This can save database
|
94
|
+
# queries by reusing in-memory objects. The optimization is only applied
|
95
|
+
# to single associations (i.e. :belongs_to, :has_one) with no scopes.
|
96
|
+
def initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true)
|
97
|
+
@records = records
|
98
|
+
@associations = associations
|
99
|
+
@scope = scope
|
100
|
+
@available_records = available_records || []
|
101
|
+
@associate_by_default = associate_by_default
|
102
|
+
|
103
|
+
@tree = Branch.new(
|
104
|
+
parent: nil,
|
105
|
+
association: nil,
|
106
|
+
children: @associations,
|
107
|
+
associate_by_default: @associate_by_default,
|
108
|
+
scope: @scope
|
109
|
+
)
|
110
|
+
@tree.preloaded_records = @records
|
96
111
|
end
|
97
112
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
def preloaders_on(association, records, scope)
|
102
|
-
case association
|
103
|
-
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)
|
109
|
-
else
|
110
|
-
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def preloaders_for_hash(association, records, scope)
|
115
|
-
association.flat_map { |parent, child|
|
116
|
-
loaders = preloaders_for_one parent, records, scope
|
117
|
-
|
118
|
-
recs = loaders.flat_map(&:preloaded_records).uniq
|
119
|
-
loaders.concat Array.wrap(child).flat_map { |assoc|
|
120
|
-
preloaders_on assoc, recs, scope
|
121
|
-
}
|
122
|
-
loaders
|
123
|
-
}
|
124
|
-
end
|
125
|
-
|
126
|
-
# Loads all the given data into +records+ for a singular +association+.
|
127
|
-
#
|
128
|
-
# Functions by instantiating a preloader class such as Preloader::HasManyThrough and
|
129
|
-
# call the +run+ method for each passed in class in the +records+ argument.
|
130
|
-
#
|
131
|
-
# Not all records have the same class, so group then preload group on the reflection
|
132
|
-
# itself so that if various subclass share the same association then we do not split
|
133
|
-
# them unnecessarily
|
134
|
-
#
|
135
|
-
# Additionally, polymorphic belongs_to associations can have multiple associated
|
136
|
-
# classes, depending on the polymorphic_type field. So we group by the classes as
|
137
|
-
# well.
|
138
|
-
def preloaders_for_one(association, records, scope)
|
139
|
-
grouped_records(association, records).flat_map do |reflection, klasses|
|
140
|
-
klasses.map do |rhs_klass, rs|
|
141
|
-
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
|
142
|
-
loader.run self
|
143
|
-
loader
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def grouped_records(association, records)
|
149
|
-
h = {}
|
150
|
-
records.each do |record|
|
151
|
-
next unless record
|
152
|
-
assoc = record.association(association)
|
153
|
-
next unless assoc.klass
|
154
|
-
klasses = h[assoc.reflection] ||= {}
|
155
|
-
(klasses[assoc.klass] ||= []) << record
|
156
|
-
end
|
157
|
-
h
|
158
|
-
end
|
159
|
-
|
160
|
-
class AlreadyLoaded # :nodoc:
|
161
|
-
def initialize(klass, owners, reflection, preload_scope)
|
162
|
-
@owners = owners
|
163
|
-
@reflection = reflection
|
164
|
-
end
|
165
|
-
|
166
|
-
def run(preloader); end
|
113
|
+
def empty?
|
114
|
+
associations.nil? || records.length == 0
|
115
|
+
end
|
167
116
|
|
168
|
-
|
169
|
-
|
170
|
-
end
|
117
|
+
def call
|
118
|
+
Batch.new([self], available_records: @available_records).call
|
171
119
|
|
172
|
-
|
173
|
-
|
174
|
-
end
|
120
|
+
loaders
|
121
|
+
end
|
175
122
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
def preloader_for(reflection, owners)
|
180
|
-
if owners.all? { |o| o.association(reflection.name).loaded? }
|
181
|
-
return AlreadyLoaded
|
182
|
-
end
|
183
|
-
reflection.check_preloadable!
|
123
|
+
def branches
|
124
|
+
@tree.children
|
125
|
+
end
|
184
126
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
Association
|
189
|
-
end
|
190
|
-
end
|
127
|
+
def loaders
|
128
|
+
branches.flat_map(&:loaders)
|
129
|
+
end
|
191
130
|
end
|
192
131
|
end
|
193
132
|
end
|
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
class SingularAssociation < Association
|
5
|
+
class SingularAssociation < Association # :nodoc:
|
6
6
|
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
|
7
7
|
def reader
|
8
|
+
ensure_klass_exists!
|
9
|
+
|
8
10
|
if !loaded? || stale_target?
|
9
11
|
reload
|
10
12
|
end
|
@@ -17,7 +19,7 @@ module ActiveRecord
|
|
17
19
|
replace(record)
|
18
20
|
end
|
19
21
|
|
20
|
-
def build(attributes =
|
22
|
+
def build(attributes = nil, &block)
|
21
23
|
record = build_record(attributes, &block)
|
22
24
|
set_new_record(record)
|
23
25
|
record
|
@@ -26,7 +28,7 @@ module ActiveRecord
|
|
26
28
|
# Implements the reload reader method, e.g. foo.reload_bar for
|
27
29
|
# Foo.has_one :bar
|
28
30
|
def force_reload_reader
|
29
|
-
|
31
|
+
reload(true)
|
30
32
|
target
|
31
33
|
end
|
32
34
|
|
@@ -36,21 +38,11 @@ module ActiveRecord
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def find_target
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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)
|
41
|
+
if disable_joins
|
42
|
+
scope.first
|
43
|
+
else
|
44
|
+
super.first
|
46
45
|
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
|
54
46
|
end
|
55
47
|
|
56
48
|
def replace(record)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Through Association
|
6
|
-
module ThroughAssociation
|
6
|
+
module ThroughAssociation # :nodoc:
|
7
7
|
delegate :source_reflection, to: :reflection
|
8
8
|
|
9
9
|
private
|
@@ -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
|
@@ -74,8 +74,8 @@ module ActiveRecord
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
# Note: this does not capture all cases, for example it would be
|
78
|
-
# properly support stale-checking for nested associations.
|
77
|
+
# Note: this does not capture all cases, for example it would be impractical
|
78
|
+
# to try to properly support stale-checking for nested associations.
|
79
79
|
def stale_state
|
80
80
|
if through_reflection.belongs_to?
|
81
81
|
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
|