omg-activerecord 8.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +355 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- 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 +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +121 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +70 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -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 +177 -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 +159 -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 +53 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- 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 +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -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 +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -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/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/cte.rb +36 -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/fragments.rb +35 -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 +68 -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/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -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 +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -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 +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -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 +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- 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 +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- 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
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
|
@@ -0,0 +1,153 @@
|
|
|
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 = if association
|
|
13
|
+
begin
|
|
14
|
+
@association = association.to_sym
|
|
15
|
+
rescue NoMethodError
|
|
16
|
+
raise ArgumentError, "Association names must be Symbol or String, got: #{association.class.name}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
@parent = parent
|
|
20
|
+
@scope = scope
|
|
21
|
+
@associate_by_default = associate_by_default
|
|
22
|
+
|
|
23
|
+
@children = build_children(children)
|
|
24
|
+
@loaders = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def future_classes
|
|
28
|
+
(immediate_future_classes + children.flat_map(&:future_classes)).uniq
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def immediate_future_classes
|
|
32
|
+
if parent.done?
|
|
33
|
+
loaders.flat_map(&:future_classes).uniq
|
|
34
|
+
else
|
|
35
|
+
likely_reflections.reject(&:polymorphic?).flat_map do |reflection|
|
|
36
|
+
reflection.
|
|
37
|
+
chain.
|
|
38
|
+
map(&:klass)
|
|
39
|
+
end.uniq
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def target_classes
|
|
44
|
+
if done?
|
|
45
|
+
preloaded_records.map(&:klass).uniq
|
|
46
|
+
elsif parent.done?
|
|
47
|
+
loaders.map(&:klass).uniq
|
|
48
|
+
else
|
|
49
|
+
likely_reflections.reject(&:polymorphic?).map(&:klass).uniq
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def likely_reflections
|
|
54
|
+
parent_classes = parent.target_classes
|
|
55
|
+
parent_classes.filter_map do |parent_klass|
|
|
56
|
+
parent_klass._reflect_on_association(@association)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def root?
|
|
61
|
+
parent.nil?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def source_records
|
|
65
|
+
@parent.preloaded_records
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def preloaded_records
|
|
69
|
+
@preloaded_records ||= loaders.flat_map(&:preloaded_records)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def done?
|
|
73
|
+
root? || (@loaders && @loaders.all?(&:run?))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def runnable_loaders
|
|
77
|
+
loaders.flat_map(&:runnable_loaders).reject(&:run?)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def grouped_records
|
|
81
|
+
h = {}
|
|
82
|
+
polymorphic_parent = !root? && parent.polymorphic?
|
|
83
|
+
source_records.each do |record|
|
|
84
|
+
reflection = record.class._reflect_on_association(association)
|
|
85
|
+
next if polymorphic_parent && !reflection || !record.association(association).klass
|
|
86
|
+
(h[reflection] ||= []) << record
|
|
87
|
+
end
|
|
88
|
+
h
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def preloaders_for_reflection(reflection, reflection_records)
|
|
92
|
+
reflection_records.group_by do |record|
|
|
93
|
+
klass = record.association(association).klass
|
|
94
|
+
|
|
95
|
+
if reflection.scope && reflection.scope.arity != 0
|
|
96
|
+
# For instance dependent scopes, the scope is potentially
|
|
97
|
+
# different for each record. To allow this we'll group each
|
|
98
|
+
# object separately into its own preloader
|
|
99
|
+
reflection_scope = reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass, record).inject(&:merge!)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
[klass, reflection_scope]
|
|
103
|
+
end.map do |(rhs_klass, reflection_scope), rs|
|
|
104
|
+
preloader_for(reflection).new(rhs_klass, rs, reflection, scope, reflection_scope, associate_by_default)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def polymorphic?
|
|
109
|
+
return false if root?
|
|
110
|
+
return @polymorphic if defined?(@polymorphic)
|
|
111
|
+
|
|
112
|
+
@polymorphic = source_records.any? do |record|
|
|
113
|
+
reflection = record.class._reflect_on_association(association)
|
|
114
|
+
reflection && reflection.options[:polymorphic]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def loaders
|
|
119
|
+
@loaders ||=
|
|
120
|
+
grouped_records.flat_map do |reflection, reflection_records|
|
|
121
|
+
preloaders_for_reflection(reflection, reflection_records)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
def build_children(children)
|
|
127
|
+
Array.wrap(children).flat_map { |association|
|
|
128
|
+
Array(association).flat_map { |parent, child|
|
|
129
|
+
Branch.new(
|
|
130
|
+
parent: self,
|
|
131
|
+
association: parent,
|
|
132
|
+
children: child,
|
|
133
|
+
associate_by_default: associate_by_default,
|
|
134
|
+
scope: scope
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Returns a class containing the logic needed to load preload the data
|
|
141
|
+
# and attach it to a relation. The class returned implements a `run` method
|
|
142
|
+
# that accepts a preloader.
|
|
143
|
+
def preloader_for(reflection)
|
|
144
|
+
if reflection.options[:through]
|
|
145
|
+
ThroughAssociation
|
|
146
|
+
else
|
|
147
|
+
Association
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Associations
|
|
5
|
+
class Preloader
|
|
6
|
+
class ThroughAssociation < Association # :nodoc:
|
|
7
|
+
def preloaded_records
|
|
8
|
+
@preloaded_records ||= source_preloaders.flat_map(&:preloaded_records)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def records_by_owner
|
|
12
|
+
@records_by_owner ||= owners.each_with_object({}) do |owner, result|
|
|
13
|
+
if loaded?(owner)
|
|
14
|
+
result[owner] = target_for(owner)
|
|
15
|
+
next
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
through_records = through_records_by_owner[owner] || []
|
|
19
|
+
|
|
20
|
+
if owners.first.association(through_reflection.name).loaded?
|
|
21
|
+
if source_type = reflection.options[:source_type]
|
|
22
|
+
through_records = through_records.select do |record|
|
|
23
|
+
record[reflection.foreign_type] == source_type
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
records = through_records.flat_map do |record|
|
|
29
|
+
source_records_by_owner[record]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
records.compact!
|
|
33
|
+
records.sort_by! { |rhs| preload_index[rhs] } if scope.order_values.any?
|
|
34
|
+
records.uniq! if scope.distinct_value
|
|
35
|
+
result[owner] = records
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def runnable_loaders
|
|
40
|
+
if data_available?
|
|
41
|
+
[self]
|
|
42
|
+
elsif through_preloaders.all?(&:run?)
|
|
43
|
+
source_preloaders.flat_map(&:runnable_loaders)
|
|
44
|
+
else
|
|
45
|
+
through_preloaders.flat_map(&:runnable_loaders)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def future_classes
|
|
50
|
+
if run?
|
|
51
|
+
[]
|
|
52
|
+
elsif through_preloaders.all?(&:run?)
|
|
53
|
+
source_preloaders.flat_map(&:future_classes).uniq
|
|
54
|
+
else
|
|
55
|
+
through_classes = through_preloaders.flat_map(&:future_classes)
|
|
56
|
+
source_classes = source_reflection.
|
|
57
|
+
chain.
|
|
58
|
+
reject { |reflection| reflection.respond_to?(:polymorphic?) && reflection.polymorphic? }.
|
|
59
|
+
map(&:klass)
|
|
60
|
+
(through_classes + source_classes).uniq
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
def data_available?
|
|
66
|
+
owners.all? { |owner| loaded?(owner) } ||
|
|
67
|
+
through_preloaders.all?(&:run?) && source_preloaders.all?(&:run?)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def source_preloaders
|
|
71
|
+
@source_preloaders ||= ActiveRecord::Associations::Preloader.new(records: middle_records, associations: source_reflection.name, scope: scope, associate_by_default: false).loaders
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def middle_records
|
|
75
|
+
through_records_by_owner.values.flatten
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def through_preloaders
|
|
79
|
+
@through_preloaders ||= ActiveRecord::Associations::Preloader.new(records: owners, associations: through_reflection.name, scope: through_scope, associate_by_default: false).loaders
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def through_reflection
|
|
83
|
+
reflection.through_reflection
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def source_reflection
|
|
87
|
+
reflection.source_reflection
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def source_records_by_owner
|
|
91
|
+
@source_records_by_owner ||= source_preloaders.map(&:records_by_owner).reduce(:merge)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def through_records_by_owner
|
|
95
|
+
@through_records_by_owner ||= through_preloaders.map(&:records_by_owner).reduce(:merge)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def preload_index
|
|
99
|
+
@preload_index ||= preloaded_records.each_with_object({}).with_index do |(record, result), index|
|
|
100
|
+
result[record] = index
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def through_scope
|
|
105
|
+
scope = through_reflection.klass.unscoped
|
|
106
|
+
options = reflection.options
|
|
107
|
+
|
|
108
|
+
return scope if options[:disable_joins]
|
|
109
|
+
|
|
110
|
+
values = reflection_scope.values
|
|
111
|
+
if annotations = values[:annotate]
|
|
112
|
+
scope.annotate!(*annotations)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if options[:source_type]
|
|
116
|
+
scope.where! reflection.foreign_type => options[:source_type]
|
|
117
|
+
elsif !reflection_scope.where_clause.empty?
|
|
118
|
+
scope.where_clause = reflection_scope.where_clause
|
|
119
|
+
|
|
120
|
+
if includes = values[:includes]
|
|
121
|
+
scope.includes!(source_reflection.name => includes)
|
|
122
|
+
else
|
|
123
|
+
scope.includes!(source_reflection.name)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if values[:references] && !values[:references].empty?
|
|
127
|
+
scope.references_values |= values[:references]
|
|
128
|
+
else
|
|
129
|
+
scope.references!(source_reflection.table_name)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if joins = values[:joins]
|
|
133
|
+
scope.joins!(source_reflection.name => joins)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if left_outer_joins = values[:left_outer_joins]
|
|
137
|
+
scope.left_outer_joins!(source_reflection.name => left_outer_joins)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if scope.eager_loading? && order_values = values[:order]
|
|
141
|
+
scope = scope.order(order_values)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
cascade_strict_loading(scope)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Associations
|
|
7
|
+
# = Active Record \Preloader
|
|
8
|
+
#
|
|
9
|
+
# Implements the details of eager loading of Active Record associations.
|
|
10
|
+
#
|
|
11
|
+
# Suppose that you have the following two Active Record models:
|
|
12
|
+
#
|
|
13
|
+
# class Author < ActiveRecord::Base
|
|
14
|
+
# # columns: name, age
|
|
15
|
+
# has_many :books
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# class Book < ActiveRecord::Base
|
|
19
|
+
# # columns: title, sales, author_id
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# When you load an author with all associated books Active Record will make
|
|
23
|
+
# multiple queries like this:
|
|
24
|
+
#
|
|
25
|
+
# Author.includes(:books).where(name: ['bell hooks', 'Homer']).to_a
|
|
26
|
+
#
|
|
27
|
+
# # SELECT `authors`.* FROM `authors` WHERE `name` IN ('bell hooks', 'Homer')
|
|
28
|
+
# # SELECT `books`.* FROM `books` WHERE `author_id` IN (2, 5)
|
|
29
|
+
#
|
|
30
|
+
# Active Record saves the ids of the records from the first query to use in
|
|
31
|
+
# the second. Depending on the number of associations involved there can be
|
|
32
|
+
# arbitrarily many SQL queries made.
|
|
33
|
+
#
|
|
34
|
+
# However, if there is a WHERE clause that spans across tables Active
|
|
35
|
+
# Record will fall back to a slightly more resource-intensive single query:
|
|
36
|
+
#
|
|
37
|
+
# Author.includes(:books).where(books: {title: 'Illiad'}).to_a
|
|
38
|
+
# # SELECT `authors`.`id` AS t0_r0, `authors`.`name` AS t0_r1, `authors`.`age` AS t0_r2,
|
|
39
|
+
# # `books`.`id` AS t1_r0, `books`.`title` AS t1_r1, `books`.`sales` AS t1_r2
|
|
40
|
+
# # FROM `authors`
|
|
41
|
+
# # LEFT OUTER JOIN `books` ON `authors`.`id` = `books`.`author_id`
|
|
42
|
+
# # WHERE `books`.`title` = 'Illiad'
|
|
43
|
+
#
|
|
44
|
+
# This could result in many rows that contain redundant data and it performs poorly at scale
|
|
45
|
+
# and is therefore only used when necessary.
|
|
46
|
+
class Preloader # :nodoc:
|
|
47
|
+
extend ActiveSupport::Autoload
|
|
48
|
+
|
|
49
|
+
eager_autoload do
|
|
50
|
+
autoload :Association, "active_record/associations/preloader/association"
|
|
51
|
+
autoload :Batch, "active_record/associations/preloader/batch"
|
|
52
|
+
autoload :Branch, "active_record/associations/preloader/branch"
|
|
53
|
+
autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
attr_reader :records, :associations, :scope, :associate_by_default
|
|
57
|
+
|
|
58
|
+
# Eager loads the named associations for the given Active Record record(s).
|
|
59
|
+
#
|
|
60
|
+
# In this description, 'association name' shall refer to the name passed
|
|
61
|
+
# to an association creation method. For example, a model that specifies
|
|
62
|
+
# <tt>belongs_to :author</tt>, <tt>has_many :buyers</tt> has association
|
|
63
|
+
# names +:author+ and +:buyers+.
|
|
64
|
+
#
|
|
65
|
+
# == Parameters
|
|
66
|
+
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
|
|
67
|
+
# i.e. +records+ itself may also contain arrays of records. In any case,
|
|
68
|
+
# +preload_associations+ will preload all associations records by
|
|
69
|
+
# flattening +records+.
|
|
70
|
+
#
|
|
71
|
+
# +associations+ specifies one or more associations that you want to
|
|
72
|
+
# preload. It may be:
|
|
73
|
+
# - a Symbol or a String which specifies a single association name. For
|
|
74
|
+
# example, specifying +:books+ allows this method to preload all books
|
|
75
|
+
# for an Author.
|
|
76
|
+
# - an Array which specifies multiple association names. This array
|
|
77
|
+
# is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
|
|
78
|
+
# allows this method to preload an author's avatar as well as all of their
|
|
79
|
+
# books.
|
|
80
|
+
# - a Hash which specifies multiple association names, as well as
|
|
81
|
+
# association names for the to-be-preloaded association objects. For
|
|
82
|
+
# example, specifying <tt>{ author: :avatar }</tt> will preload a
|
|
83
|
+
# book's author, as well as that author's avatar.
|
|
84
|
+
#
|
|
85
|
+
# +:associations+ has the same format as the arguments to
|
|
86
|
+
# ActiveRecord::QueryMethods#includes. So +associations+ could look like
|
|
87
|
+
# this:
|
|
88
|
+
#
|
|
89
|
+
# :books
|
|
90
|
+
# [ :books, :author ]
|
|
91
|
+
# { author: :avatar }
|
|
92
|
+
# [ :books, { author: :avatar } ]
|
|
93
|
+
#
|
|
94
|
+
# +available_records+ is an array of ActiveRecord::Base. The Preloader
|
|
95
|
+
# will try to use the objects in this array to preload the requested
|
|
96
|
+
# associations before querying the database. This can save database
|
|
97
|
+
# queries by reusing in-memory objects. The optimization is only applied
|
|
98
|
+
# to single associations (i.e. :belongs_to, :has_one) with no scopes.
|
|
99
|
+
def initialize(records:, associations:, scope: nil, available_records: [], associate_by_default: true)
|
|
100
|
+
@records = records
|
|
101
|
+
@associations = associations
|
|
102
|
+
@scope = scope
|
|
103
|
+
@available_records = available_records || []
|
|
104
|
+
@associate_by_default = associate_by_default
|
|
105
|
+
|
|
106
|
+
@tree = Branch.new(
|
|
107
|
+
parent: nil,
|
|
108
|
+
association: nil,
|
|
109
|
+
children: @associations,
|
|
110
|
+
associate_by_default: @associate_by_default,
|
|
111
|
+
scope: @scope
|
|
112
|
+
)
|
|
113
|
+
@tree.preloaded_records = @records
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def empty?
|
|
117
|
+
associations.nil? || records.length == 0
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def call
|
|
121
|
+
Batch.new([self], available_records: @available_records).call
|
|
122
|
+
|
|
123
|
+
loaders
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def branches
|
|
127
|
+
@tree.children
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def loaders
|
|
131
|
+
branches.flat_map(&:loaders)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Associations
|
|
5
|
+
class SingularAssociation < Association # :nodoc:
|
|
6
|
+
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
|
|
7
|
+
def reader
|
|
8
|
+
ensure_klass_exists!
|
|
9
|
+
|
|
10
|
+
if !loaded? || stale_target?
|
|
11
|
+
reload
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
target
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
|
18
|
+
def reset
|
|
19
|
+
super
|
|
20
|
+
@target = nil
|
|
21
|
+
@future_target = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
|
|
25
|
+
def writer(record)
|
|
26
|
+
replace(record)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def build(attributes = nil, &block)
|
|
30
|
+
record = build_record(attributes, &block)
|
|
31
|
+
set_new_record(record)
|
|
32
|
+
record
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Implements the reload reader method, e.g. foo.reload_bar for
|
|
36
|
+
# Foo.has_one :bar
|
|
37
|
+
def force_reload_reader
|
|
38
|
+
reload(true)
|
|
39
|
+
target
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def scope_for_create
|
|
44
|
+
super.except!(*Array(klass.primary_key))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def find_target(async: false)
|
|
48
|
+
if disable_joins
|
|
49
|
+
if async
|
|
50
|
+
scope.load_async.then(&:first)
|
|
51
|
+
else
|
|
52
|
+
scope.first
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
super.then(&:first)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def replace(record)
|
|
60
|
+
raise NotImplementedError, "Subclasses must implement a replace(record) method"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def set_new_record(record)
|
|
64
|
+
replace(record)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def _create_record(attributes, raise_error = false, &block)
|
|
68
|
+
record = build_record(attributes, &block)
|
|
69
|
+
saved = record.save
|
|
70
|
+
set_new_record(record)
|
|
71
|
+
raise RecordInvalid.new(record) if !saved && raise_error
|
|
72
|
+
record
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Associations
|
|
5
|
+
# = Active Record Through Association
|
|
6
|
+
module ThroughAssociation # :nodoc:
|
|
7
|
+
delegate :source_reflection, to: :reflection
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
def transaction(&block)
|
|
11
|
+
through_reflection.klass.transaction(&block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def through_reflection
|
|
15
|
+
@through_reflection ||= begin
|
|
16
|
+
refl = reflection.through_reflection
|
|
17
|
+
|
|
18
|
+
while refl.through_reflection?
|
|
19
|
+
refl = refl.through_reflection
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
refl
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def through_association
|
|
27
|
+
@through_association ||= owner.association(through_reflection.name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# We merge in these scopes for two reasons:
|
|
31
|
+
#
|
|
32
|
+
# 1. To get the default_scope conditions for any of the other reflections in the chain
|
|
33
|
+
# 2. To get the type conditions for any STI models in the chain
|
|
34
|
+
def target_scope
|
|
35
|
+
scope = super
|
|
36
|
+
reflection.chain.drop(1).each do |reflection|
|
|
37
|
+
relation = reflection.klass.scope_for_association
|
|
38
|
+
scope.merge!(
|
|
39
|
+
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
scope
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Construct attributes for :through pointing to owner and associate. This is used by the
|
|
46
|
+
# methods which create and delete records on the association.
|
|
47
|
+
#
|
|
48
|
+
# We only support indirectly modifying through associations which have a belongs_to source.
|
|
49
|
+
# This is the "has_many :tags, through: :taggings" situation, where the join model
|
|
50
|
+
# typically has a belongs_to on both side. In other words, associations which could also
|
|
51
|
+
# be represented as has_and_belongs_to_many associations.
|
|
52
|
+
#
|
|
53
|
+
# We do not support creating/deleting records on the association where the source has
|
|
54
|
+
# some other type, because this opens up a whole can of worms, and in basically any
|
|
55
|
+
# situation it is more natural for the user to just create or modify their join records
|
|
56
|
+
# directly as required.
|
|
57
|
+
def construct_join_attributes(*records)
|
|
58
|
+
ensure_mutable
|
|
59
|
+
|
|
60
|
+
association_primary_key = source_reflection.association_primary_key(reflection.klass)
|
|
61
|
+
|
|
62
|
+
if Array(association_primary_key) == reflection.klass.composite_query_constraints_list && !options[:source_type]
|
|
63
|
+
join_attributes = { source_reflection.name => records }
|
|
64
|
+
else
|
|
65
|
+
assoc_pk_values = records.map { |record| record._read_attribute(association_primary_key) }
|
|
66
|
+
join_attributes = { source_reflection.foreign_key => assoc_pk_values }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if options[:source_type]
|
|
70
|
+
join_attributes[source_reflection.foreign_type] = [ options[:source_type] ]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if records.count == 1
|
|
74
|
+
join_attributes.transform_values!(&:first)
|
|
75
|
+
else
|
|
76
|
+
join_attributes
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Note: this does not capture all cases, for example it would be impractical
|
|
81
|
+
# to try to properly support stale-checking for nested associations.
|
|
82
|
+
def stale_state
|
|
83
|
+
if through_reflection.belongs_to?
|
|
84
|
+
Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
|
|
85
|
+
owner[foreign_key_column]
|
|
86
|
+
end.presence
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def foreign_key_present?
|
|
91
|
+
through_reflection.belongs_to? && Array(through_reflection.foreign_key).all? do |foreign_key_column|
|
|
92
|
+
!owner[foreign_key_column].nil?
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def ensure_mutable
|
|
97
|
+
unless source_reflection.belongs_to?
|
|
98
|
+
if reflection.has_one?
|
|
99
|
+
raise HasOneThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
|
|
100
|
+
else
|
|
101
|
+
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def ensure_not_nested
|
|
107
|
+
if reflection.nested?
|
|
108
|
+
if reflection.has_one?
|
|
109
|
+
raise HasOneThroughNestedAssociationsAreReadonly.new(owner, reflection)
|
|
110
|
+
else
|
|
111
|
+
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def build_record(attributes)
|
|
117
|
+
if source_reflection.collection?
|
|
118
|
+
inverse = source_reflection.inverse_of
|
|
119
|
+
target = through_association.target
|
|
120
|
+
|
|
121
|
+
if inverse && target && !target.is_a?(Array)
|
|
122
|
+
Array(target.id).zip(Array(inverse.foreign_key)).map do |primary_key_value, foreign_key_column|
|
|
123
|
+
attributes[foreign_key_column] = primary_key_value
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
super
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|