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,535 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Associations
|
|
7
|
+
# = Active Record Association Collection
|
|
8
|
+
#
|
|
9
|
+
# CollectionAssociation is an abstract class that provides common stuff to
|
|
10
|
+
# ease the implementation of association proxies that represent
|
|
11
|
+
# collections. See the class hierarchy in Association.
|
|
12
|
+
#
|
|
13
|
+
# CollectionAssociation:
|
|
14
|
+
# HasManyAssociation => has_many
|
|
15
|
+
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
|
16
|
+
#
|
|
17
|
+
# The CollectionAssociation class provides common methods to the collections
|
|
18
|
+
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
|
19
|
+
# the <tt>:through association</tt> option.
|
|
20
|
+
#
|
|
21
|
+
# You need to be careful with assumptions regarding the target: The proxy
|
|
22
|
+
# does not fetch records from the database until it needs them, but new
|
|
23
|
+
# ones created with +build+ are added to the target. So, the target may be
|
|
24
|
+
# non-empty and still lack children waiting to be read from the database.
|
|
25
|
+
# If you look directly to the database you cannot assume that's the entire
|
|
26
|
+
# collection because new records may have been added to the target, etc.
|
|
27
|
+
#
|
|
28
|
+
# If you need to work on all current children, new and existing records,
|
|
29
|
+
# +load_target+ and the +loaded+ flag are your friends.
|
|
30
|
+
class CollectionAssociation < Association # :nodoc:
|
|
31
|
+
attr_accessor :nested_attributes_target
|
|
32
|
+
|
|
33
|
+
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
|
34
|
+
def reader
|
|
35
|
+
ensure_klass_exists!
|
|
36
|
+
|
|
37
|
+
if stale_target?
|
|
38
|
+
reload
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
|
42
|
+
@proxy.reset_scope
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
|
46
|
+
def writer(records)
|
|
47
|
+
replace(records)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
|
51
|
+
def ids_reader
|
|
52
|
+
if loaded?
|
|
53
|
+
target.pluck(*reflection.association_primary_key)
|
|
54
|
+
elsif !target.empty?
|
|
55
|
+
load_target.pluck(*reflection.association_primary_key)
|
|
56
|
+
else
|
|
57
|
+
@association_ids ||= scope.pluck(*reflection.association_primary_key)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
|
62
|
+
def ids_writer(ids)
|
|
63
|
+
primary_key = reflection.association_primary_key
|
|
64
|
+
pk_type = klass.type_for_attribute(primary_key)
|
|
65
|
+
ids = Array(ids).compact_blank
|
|
66
|
+
ids.map! { |id| pk_type.cast(id) }
|
|
67
|
+
|
|
68
|
+
records = if klass.composite_primary_key?
|
|
69
|
+
klass.where(primary_key => ids).index_by do |record|
|
|
70
|
+
primary_key.map { |primary_key| record._read_attribute(primary_key) }
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
klass.where(primary_key => ids).index_by do |record|
|
|
74
|
+
record._read_attribute(primary_key)
|
|
75
|
+
end
|
|
76
|
+
end.values_at(*ids).compact
|
|
77
|
+
|
|
78
|
+
if records.size != ids.size
|
|
79
|
+
found_ids = records.map { |record| record._read_attribute(primary_key) }
|
|
80
|
+
not_found_ids = ids - found_ids
|
|
81
|
+
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
|
82
|
+
else
|
|
83
|
+
replace(records)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def reset
|
|
88
|
+
super
|
|
89
|
+
@target = []
|
|
90
|
+
@replaced_or_added_targets = Set.new.compare_by_identity
|
|
91
|
+
@association_ids = nil
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def find(*args)
|
|
95
|
+
if options[:inverse_of] && loaded?
|
|
96
|
+
args_flatten = args.flatten
|
|
97
|
+
model = scope.model
|
|
98
|
+
|
|
99
|
+
if args_flatten.blank?
|
|
100
|
+
error_message = "Couldn't find #{model.name} without an ID"
|
|
101
|
+
raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
result = find_by_scan(*args)
|
|
105
|
+
|
|
106
|
+
result_size = Array(result).size
|
|
107
|
+
if !result || result_size != args_flatten.size
|
|
108
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
|
109
|
+
else
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
scope.find(*args)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def build(attributes = nil, &block)
|
|
118
|
+
if attributes.is_a?(Array)
|
|
119
|
+
attributes.collect { |attr| build(attr, &block) }
|
|
120
|
+
else
|
|
121
|
+
add_to_target(build_record(attributes, &block), replace: true)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
|
126
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
|
127
|
+
def concat(*records)
|
|
128
|
+
records = records.flatten
|
|
129
|
+
if owner.new_record?
|
|
130
|
+
skip_strict_loading { load_target }
|
|
131
|
+
concat_records(records)
|
|
132
|
+
else
|
|
133
|
+
transaction { concat_records(records) }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Removes all records from the association without calling callbacks
|
|
138
|
+
# on the associated records. It honors the +:dependent+ option. However
|
|
139
|
+
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
|
140
|
+
# deletion strategy for the association is applied.
|
|
141
|
+
#
|
|
142
|
+
# You can force a particular deletion strategy by passing a parameter.
|
|
143
|
+
#
|
|
144
|
+
# Example:
|
|
145
|
+
#
|
|
146
|
+
# @author.books.delete_all(:nullify)
|
|
147
|
+
# @author.books.delete_all(:delete_all)
|
|
148
|
+
#
|
|
149
|
+
# See delete for more info.
|
|
150
|
+
def delete_all(dependent = nil)
|
|
151
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
|
152
|
+
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
dependent = if dependent
|
|
156
|
+
dependent
|
|
157
|
+
elsif options[:dependent] == :destroy
|
|
158
|
+
:delete_all
|
|
159
|
+
else
|
|
160
|
+
options[:dependent]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
delete_or_nullify_all_records(dependent).tap do
|
|
164
|
+
reset
|
|
165
|
+
loaded!
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Destroy all the records from this association.
|
|
170
|
+
#
|
|
171
|
+
# See destroy for more info.
|
|
172
|
+
def destroy_all
|
|
173
|
+
destroy(load_target).tap do
|
|
174
|
+
reset
|
|
175
|
+
loaded!
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Removes +records+ from this association calling +before_remove+ and
|
|
180
|
+
# +after_remove+ callbacks.
|
|
181
|
+
#
|
|
182
|
+
# This method is abstract in the sense that +delete_records+ has to be
|
|
183
|
+
# provided by descendants. Note this method does not imply the records
|
|
184
|
+
# are actually removed from the database, that depends precisely on
|
|
185
|
+
# +delete_records+. They are in any case removed from the collection.
|
|
186
|
+
def delete(*records)
|
|
187
|
+
delete_or_destroy(records, options[:dependent])
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Deletes the +records+ and removes them from this association calling
|
|
191
|
+
# +before_remove+, +after_remove+, +before_destroy+ and +after_destroy+ callbacks.
|
|
192
|
+
#
|
|
193
|
+
# Note that this method removes records from the database ignoring the
|
|
194
|
+
# +:dependent+ option.
|
|
195
|
+
def destroy(*records)
|
|
196
|
+
delete_or_destroy(records, :destroy)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Returns the size of the collection by executing a SELECT COUNT(*)
|
|
200
|
+
# query if the collection hasn't been loaded, and calling
|
|
201
|
+
# <tt>collection.size</tt> if it has.
|
|
202
|
+
#
|
|
203
|
+
# If the collection has been already loaded +size+ and +length+ are
|
|
204
|
+
# equivalent. If not and you are going to need the records anyway
|
|
205
|
+
# +length+ will take one less query. Otherwise +size+ is more efficient.
|
|
206
|
+
#
|
|
207
|
+
# This method is abstract in the sense that it relies on
|
|
208
|
+
# +count_records+, which is a method descendants have to provide.
|
|
209
|
+
def size
|
|
210
|
+
if !find_target? || loaded?
|
|
211
|
+
target.size
|
|
212
|
+
elsif @association_ids
|
|
213
|
+
@association_ids.size
|
|
214
|
+
elsif !association_scope.group_values.empty?
|
|
215
|
+
load_target.size
|
|
216
|
+
elsif !association_scope.distinct_value && !target.empty?
|
|
217
|
+
unsaved_records = target.select(&:new_record?)
|
|
218
|
+
unsaved_records.size + count_records
|
|
219
|
+
else
|
|
220
|
+
count_records
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Returns true if the collection is empty.
|
|
225
|
+
#
|
|
226
|
+
# If the collection has been loaded
|
|
227
|
+
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
|
228
|
+
# collection has not been loaded, it is equivalent to
|
|
229
|
+
# <tt>!collection.exists?</tt>. If the collection has not already been
|
|
230
|
+
# loaded and you are going to fetch the records anyway it is better to
|
|
231
|
+
# check <tt>collection.length.zero?</tt>.
|
|
232
|
+
def empty?
|
|
233
|
+
if loaded? || @association_ids || reflection.has_active_cached_counter?
|
|
234
|
+
size.zero?
|
|
235
|
+
else
|
|
236
|
+
target.empty? && !scope.exists?
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Replace this collection with +other_array+. This will perform a diff
|
|
241
|
+
# and delete/add only records that have changed.
|
|
242
|
+
def replace(other_array)
|
|
243
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
|
244
|
+
original_target = skip_strict_loading { load_target }.dup
|
|
245
|
+
|
|
246
|
+
if owner.new_record?
|
|
247
|
+
replace_records(other_array, original_target)
|
|
248
|
+
else
|
|
249
|
+
replace_common_records_in_memory(other_array, original_target)
|
|
250
|
+
if other_array != original_target
|
|
251
|
+
transaction { replace_records(other_array, original_target) }
|
|
252
|
+
else
|
|
253
|
+
other_array
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def include?(record)
|
|
259
|
+
klass = reflection.klass
|
|
260
|
+
return false unless record.is_a?(klass)
|
|
261
|
+
|
|
262
|
+
if record.new_record?
|
|
263
|
+
include_in_memory?(record)
|
|
264
|
+
elsif loaded?
|
|
265
|
+
target.include?(record)
|
|
266
|
+
else
|
|
267
|
+
record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
|
|
268
|
+
scope.exists?(record_id)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def load_target
|
|
273
|
+
if find_target?
|
|
274
|
+
@target = merge_target_lists(find_target, target)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
loaded!
|
|
278
|
+
target
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
|
282
|
+
replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def target=(record)
|
|
286
|
+
return super unless reflection.klass.has_many_inversing
|
|
287
|
+
|
|
288
|
+
case record
|
|
289
|
+
when nil
|
|
290
|
+
# It's not possible to remove the record from the inverse association.
|
|
291
|
+
when Array
|
|
292
|
+
super
|
|
293
|
+
else
|
|
294
|
+
replace_on_target(record, true, replace: true, inversing: true)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def scope
|
|
299
|
+
scope = super
|
|
300
|
+
scope.none! if null_scope?
|
|
301
|
+
scope
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def null_scope?
|
|
305
|
+
owner.new_record? && !foreign_key_present?
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def find_from_target?
|
|
309
|
+
loaded? ||
|
|
310
|
+
(owner.strict_loading? && owner.strict_loading_all?) ||
|
|
311
|
+
reflection.strict_loading? ||
|
|
312
|
+
owner.new_record? ||
|
|
313
|
+
target.any? { |record| record.new_record? || record.changed? }
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def collection?
|
|
317
|
+
true
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
private
|
|
321
|
+
def transaction(&block)
|
|
322
|
+
reflection.klass.transaction(&block)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# We have some records loaded from the database (persisted) and some that are
|
|
326
|
+
# in-memory (memory). The same record may be represented in the persisted array
|
|
327
|
+
# and in the memory array.
|
|
328
|
+
#
|
|
329
|
+
# So the task of this method is to merge them according to the following rules:
|
|
330
|
+
#
|
|
331
|
+
# * The final array must not have duplicates
|
|
332
|
+
# * The order of the persisted array is to be preserved
|
|
333
|
+
# * Any changes made to attributes on objects in the memory array are to be preserved
|
|
334
|
+
# * Otherwise, attributes should have the value found in the database
|
|
335
|
+
def merge_target_lists(persisted, memory)
|
|
336
|
+
return persisted if memory.empty?
|
|
337
|
+
|
|
338
|
+
persisted.map! do |record|
|
|
339
|
+
if mem_record = memory.delete(record)
|
|
340
|
+
|
|
341
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
|
|
342
|
+
mem_record._write_attribute(name, record[name])
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
mem_record
|
|
346
|
+
else
|
|
347
|
+
record
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
persisted + memory.reject(&:persisted?)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def _create_record(attributes, raise = false, &block)
|
|
355
|
+
unless owner.persisted?
|
|
356
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
if attributes.is_a?(Array)
|
|
360
|
+
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
|
361
|
+
else
|
|
362
|
+
record = build_record(attributes, &block)
|
|
363
|
+
transaction do
|
|
364
|
+
result = nil
|
|
365
|
+
add_to_target(record) do
|
|
366
|
+
result = insert_record(record, true, raise) {
|
|
367
|
+
@_was_loaded = loaded?
|
|
368
|
+
}
|
|
369
|
+
end
|
|
370
|
+
raise ActiveRecord::Rollback unless result
|
|
371
|
+
end
|
|
372
|
+
record
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Do the relevant stuff to insert the given record into the association collection.
|
|
377
|
+
def insert_record(record, validate = true, raise = false, &block)
|
|
378
|
+
if raise
|
|
379
|
+
record.save!(validate: validate, &block)
|
|
380
|
+
else
|
|
381
|
+
record.save(validate: validate, &block)
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def delete_or_destroy(records, method)
|
|
386
|
+
return if records.empty?
|
|
387
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
|
388
|
+
records = records.flatten
|
|
389
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
|
390
|
+
existing_records = records.reject(&:new_record?)
|
|
391
|
+
|
|
392
|
+
if existing_records.empty?
|
|
393
|
+
remove_records(existing_records, records, method)
|
|
394
|
+
else
|
|
395
|
+
transaction { remove_records(existing_records, records, method) }
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def remove_records(existing_records, records, method)
|
|
400
|
+
catch(:abort) do
|
|
401
|
+
records.each { |record| callback(:before_remove, record) }
|
|
402
|
+
end || return
|
|
403
|
+
|
|
404
|
+
delete_records(existing_records, method) if existing_records.any?
|
|
405
|
+
@target -= records
|
|
406
|
+
@association_ids = nil
|
|
407
|
+
|
|
408
|
+
records.each { |record| callback(:after_remove, record) }
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Delete the given records from the association,
|
|
412
|
+
# using one of the methods +:destroy+, +:delete_all+
|
|
413
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
|
414
|
+
def delete_records(records, method)
|
|
415
|
+
raise NotImplementedError
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def replace_records(new_target, original_target)
|
|
419
|
+
delete(difference(target, new_target))
|
|
420
|
+
|
|
421
|
+
unless concat(difference(new_target, target))
|
|
422
|
+
@target = original_target
|
|
423
|
+
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
|
424
|
+
"new records could not be saved."
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
target
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def replace_common_records_in_memory(new_target, original_target)
|
|
431
|
+
common_records = intersection(new_target, original_target)
|
|
432
|
+
common_records.each do |record|
|
|
433
|
+
skip_callbacks = true
|
|
434
|
+
replace_on_target(record, skip_callbacks, replace: true)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def concat_records(records, raise = false)
|
|
439
|
+
result = true
|
|
440
|
+
|
|
441
|
+
records.each do |record|
|
|
442
|
+
raise_on_type_mismatch!(record)
|
|
443
|
+
add_to_target(record) do
|
|
444
|
+
unless owner.new_record?
|
|
445
|
+
result &&= insert_record(record, true, raise) {
|
|
446
|
+
@_was_loaded = loaded?
|
|
447
|
+
}
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
raise ActiveRecord::Rollback unless result
|
|
453
|
+
|
|
454
|
+
records
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def replace_on_target(record, skip_callbacks, replace:, inversing: false)
|
|
458
|
+
if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
|
|
459
|
+
index = @target.index(record)
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
catch(:abort) do
|
|
463
|
+
callback(:before_add, record)
|
|
464
|
+
end || return unless skip_callbacks
|
|
465
|
+
|
|
466
|
+
set_inverse_instance(record)
|
|
467
|
+
|
|
468
|
+
@_was_loaded = true
|
|
469
|
+
|
|
470
|
+
yield(record) if block_given?
|
|
471
|
+
|
|
472
|
+
if !index && @replaced_or_added_targets.include?(record)
|
|
473
|
+
index = @target.index(record)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
|
477
|
+
|
|
478
|
+
if index
|
|
479
|
+
target[index] = record
|
|
480
|
+
elsif @_was_loaded || !loaded?
|
|
481
|
+
@association_ids = nil
|
|
482
|
+
target << record
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
callback(:after_add, record) unless skip_callbacks
|
|
486
|
+
|
|
487
|
+
record
|
|
488
|
+
ensure
|
|
489
|
+
@_was_loaded = nil
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def callback(method, record)
|
|
493
|
+
callbacks_for(method).each do |callback|
|
|
494
|
+
callback.call(method, owner, record)
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def callbacks_for(callback_name)
|
|
499
|
+
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
|
500
|
+
if owner.class.respond_to?(full_callback_name)
|
|
501
|
+
owner.class.send(full_callback_name)
|
|
502
|
+
else
|
|
503
|
+
[]
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def include_in_memory?(record)
|
|
508
|
+
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
|
509
|
+
assoc = owner.association(reflection.through_reflection.name)
|
|
510
|
+
assoc.reader.any? { |source|
|
|
511
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
|
512
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
|
513
|
+
} || target.include?(record)
|
|
514
|
+
else
|
|
515
|
+
target.include?(record)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# If the :inverse_of option has been
|
|
520
|
+
# specified, then #find scans the entire collection.
|
|
521
|
+
def find_by_scan(*args)
|
|
522
|
+
expects_array = args.first.kind_of?(Array)
|
|
523
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
|
524
|
+
|
|
525
|
+
if ids.size == 1
|
|
526
|
+
id = ids.first
|
|
527
|
+
record = load_target.detect { |r| id == r.id.to_s }
|
|
528
|
+
expects_array ? [ record ] : record
|
|
529
|
+
else
|
|
530
|
+
load_target.select { |r| ids.include?(r.id.to_s) }
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|