activerecord 4.2.0 → 6.1.7.1
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 +5 -5
- data/CHANGELOG.md +1221 -796
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +45 -7
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +172 -67
- data/lib/active_record/associations/association_scope.rb +105 -129
- data/lib/active_record/associations/belongs_to_association.rb +85 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +168 -279
- data/lib/active_record/associations/collection_proxy.rb +263 -155
- data/lib/active_record/associations/foreign_association.rb +33 -0
- data/lib/active_record/associations/has_many_association.rb +57 -84
- data/lib/active_record/associations/has_many_through_association.rb +70 -82
- data/lib/active_record/associations/has_one_association.rb +74 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +175 -164
- data/lib/active_record/associations/preloader/association.rb +107 -112
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +99 -96
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1845 -1597
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
- data/lib/active_record/attribute_methods/dirty.rb +168 -138
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -36
- data/lib/active_record/attribute_methods/write.rb +25 -56
- data/lib/active_record/attribute_methods.rb +153 -162
- data/lib/active_record/attributes.rb +234 -70
- data/lib/active_record/autosave_association.rb +157 -69
- data/lib/active_record/base.rb +49 -50
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +46 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
- data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
- data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +271 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +78 -161
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- 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 +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +499 -293
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +595 -382
- data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -389
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +488 -243
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +273 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -106
- data/lib/active_record/enum.rb +212 -94
- data/lib/active_record/errors.rb +225 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +33 -14
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +273 -496
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +64 -0
- data/lib/active_record/legacy_yaml_adapter.rb +52 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +103 -95
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +298 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +685 -309
- data/lib/active_record/model_schema.rb +420 -113
- data/lib/active_record/nested_attributes.rb +265 -216
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +574 -135
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +175 -54
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +533 -216
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +485 -310
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +326 -244
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +318 -256
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +99 -84
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -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 +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +139 -96
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -409
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +23 -21
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -342
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -26
- data/lib/active_record/scoping/default.rb +96 -82
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +133 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +366 -129
- data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +291 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +181 -152
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +33 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- 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 +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +65 -48
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -28
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +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 +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -10
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- 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.rb +7 -5
- metadata +175 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# = Active Record Association Collection
|
@@ -10,9 +12,9 @@ module ActiveRecord
|
|
10
12
|
# HasManyAssociation => has_many
|
11
13
|
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
12
14
|
#
|
13
|
-
# CollectionAssociation class provides common methods to the collections
|
15
|
+
# The CollectionAssociation class provides common methods to the collections
|
14
16
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
15
|
-
# +:through association+ option.
|
17
|
+
# the +:through association+ option.
|
16
18
|
#
|
17
19
|
# You need to be careful with assumptions regarding the target: The proxy
|
18
20
|
# does not fetch records from the database until it needs them, but new
|
@@ -24,22 +26,14 @@ module ActiveRecord
|
|
24
26
|
# If you need to work on all current children, new and existing records,
|
25
27
|
# +load_target+ and the +loaded+ flag are your friends.
|
26
28
|
class CollectionAssociation < Association #:nodoc:
|
27
|
-
|
28
29
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
|
-
def reader
|
30
|
-
if
|
31
|
-
klass.uncached { reload }
|
32
|
-
elsif stale_target?
|
30
|
+
def reader
|
31
|
+
if stale_target?
|
33
32
|
reload
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
43
37
|
end
|
44
38
|
|
45
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -50,107 +44,76 @@ module ActiveRecord
|
|
50
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
51
45
|
def ids_reader
|
52
46
|
if loaded?
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
56
50
|
else
|
57
|
-
|
58
|
-
scope.pluck(column)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
62
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
56
|
def ids_writer(ids)
|
64
|
-
|
65
|
-
|
66
|
-
ids
|
67
|
-
|
57
|
+
primary_key = reflection.association_primary_key
|
58
|
+
pk_type = klass.type_for_attribute(primary_key)
|
59
|
+
ids = Array(ids).compact_blank
|
60
|
+
ids.map! { |i| pk_type.cast(i) }
|
61
|
+
|
62
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
63
|
+
r.public_send(primary_key)
|
64
|
+
end.values_at(*ids).compact
|
65
|
+
|
66
|
+
if records.size != ids.size
|
67
|
+
found_ids = records.map { |record| record.public_send(primary_key) }
|
68
|
+
not_found_ids = ids - found_ids
|
69
|
+
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
70
|
+
else
|
71
|
+
replace(records)
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
def reset
|
71
76
|
super
|
72
77
|
@target = []
|
73
|
-
|
74
|
-
|
75
|
-
def select(*fields)
|
76
|
-
if block_given?
|
77
|
-
load_target.select.each { |e| yield e }
|
78
|
-
else
|
79
|
-
scope.select(*fields)
|
80
|
-
end
|
78
|
+
@replaced_or_added_targets = Set.new
|
79
|
+
@association_ids = nil
|
81
80
|
end
|
82
81
|
|
83
82
|
def find(*args)
|
84
|
-
if
|
85
|
-
|
86
|
-
|
87
|
-
if options[:inverse_of] && loaded?
|
88
|
-
args_flatten = args.flatten
|
89
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
90
|
-
result = find_by_scan(*args)
|
91
|
-
|
92
|
-
result_size = Array(result).size
|
93
|
-
if !result || result_size != args_flatten.size
|
94
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
95
|
-
else
|
96
|
-
result
|
97
|
-
end
|
98
|
-
else
|
99
|
-
scope.find(*args)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def first(*args)
|
105
|
-
first_nth_or_last(:first, *args)
|
106
|
-
end
|
107
|
-
|
108
|
-
def second(*args)
|
109
|
-
first_nth_or_last(:second, *args)
|
110
|
-
end
|
111
|
-
|
112
|
-
def third(*args)
|
113
|
-
first_nth_or_last(:third, *args)
|
114
|
-
end
|
83
|
+
if options[:inverse_of] && loaded?
|
84
|
+
args_flatten = args.flatten
|
85
|
+
model = scope.klass
|
115
86
|
|
116
|
-
|
117
|
-
|
118
|
-
|
87
|
+
if args_flatten.blank?
|
88
|
+
error_message = "Couldn't find #{model.name} without an ID"
|
89
|
+
raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
|
90
|
+
end
|
119
91
|
|
120
|
-
|
121
|
-
first_nth_or_last(:fifth, *args)
|
122
|
-
end
|
92
|
+
result = find_by_scan(*args)
|
123
93
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
94
|
+
result_size = Array(result).size
|
95
|
+
if !result || result_size != args_flatten.size
|
96
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
97
|
+
else
|
98
|
+
result
|
99
|
+
end
|
100
|
+
else
|
101
|
+
scope.find(*args)
|
102
|
+
end
|
130
103
|
end
|
131
104
|
|
132
|
-
def build(attributes =
|
105
|
+
def build(attributes = nil, &block)
|
133
106
|
if attributes.is_a?(Array)
|
134
107
|
attributes.collect { |attr| build(attr, &block) }
|
135
108
|
else
|
136
|
-
add_to_target(build_record(attributes)
|
137
|
-
yield(record) if block_given?
|
138
|
-
end
|
109
|
+
add_to_target(build_record(attributes, &block), replace: true)
|
139
110
|
end
|
140
111
|
end
|
141
112
|
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
def create!(attributes = {}, &block)
|
147
|
-
_create_record(attributes, true, &block)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
151
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
152
|
-
# +push+ and +concat+ behave identically.
|
113
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
114
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
153
115
|
def concat(*records)
|
116
|
+
records = records.flatten
|
154
117
|
if owner.new_record?
|
155
118
|
load_target
|
156
119
|
concat_records(records)
|
@@ -193,12 +156,12 @@ module ActiveRecord
|
|
193
156
|
end
|
194
157
|
|
195
158
|
dependent = if dependent
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
159
|
+
dependent
|
160
|
+
elsif options[:dependent] == :destroy
|
161
|
+
:delete_all
|
162
|
+
else
|
163
|
+
options[:dependent]
|
164
|
+
end
|
202
165
|
|
203
166
|
delete_or_nullify_all_records(dependent).tap do
|
204
167
|
reset
|
@@ -216,32 +179,6 @@ module ActiveRecord
|
|
216
179
|
end
|
217
180
|
end
|
218
181
|
|
219
|
-
# Count all records using SQL. Construct options and pass them with
|
220
|
-
# scope to the target class's +count+.
|
221
|
-
def count(column_name = nil, count_options = {})
|
222
|
-
# TODO: Remove count_options argument as soon we remove support to
|
223
|
-
# activerecord-deprecated_finders.
|
224
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
225
|
-
|
226
|
-
relation = scope
|
227
|
-
if association_scope.distinct_value
|
228
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
229
|
-
column_name ||= reflection.klass.primary_key
|
230
|
-
relation = relation.distinct
|
231
|
-
end
|
232
|
-
|
233
|
-
value = relation.count(column_name)
|
234
|
-
|
235
|
-
limit = options[:limit]
|
236
|
-
offset = options[:offset]
|
237
|
-
|
238
|
-
if limit || offset
|
239
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
240
|
-
else
|
241
|
-
value
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
182
|
# Removes +records+ from this association calling +before_remove+ and
|
246
183
|
# +after_remove+ callbacks.
|
247
184
|
#
|
@@ -250,12 +187,7 @@ module ActiveRecord
|
|
250
187
|
# are actually removed from the database, that depends precisely on
|
251
188
|
# +delete_records+. They are in any case removed from the collection.
|
252
189
|
def delete(*records)
|
253
|
-
|
254
|
-
_options = records.extract_options!
|
255
|
-
dependent = _options[:dependent] || options[:dependent]
|
256
|
-
|
257
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
258
|
-
delete_or_destroy(records, dependent)
|
190
|
+
delete_or_destroy(records, options[:dependent])
|
259
191
|
end
|
260
192
|
|
261
193
|
# Deletes the +records+ and removes them from this association calling
|
@@ -264,8 +196,6 @@ module ActiveRecord
|
|
264
196
|
# Note that this method removes records from the database ignoring the
|
265
197
|
# +:dependent+ option.
|
266
198
|
def destroy(*records)
|
267
|
-
return if records.empty?
|
268
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
269
199
|
delete_or_destroy(records, :destroy)
|
270
200
|
end
|
271
201
|
|
@@ -281,74 +211,35 @@ module ActiveRecord
|
|
281
211
|
# +count_records+, which is a method descendants have to provide.
|
282
212
|
def size
|
283
213
|
if !find_target? || loaded?
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
end
|
289
|
-
elsif !loaded? && !association_scope.group_values.empty?
|
214
|
+
target.size
|
215
|
+
elsif @association_ids
|
216
|
+
@association_ids.size
|
217
|
+
elsif !association_scope.group_values.empty?
|
290
218
|
load_target.size
|
291
|
-
elsif !
|
292
|
-
unsaved_records = target.select
|
219
|
+
elsif !association_scope.distinct_value && !target.empty?
|
220
|
+
unsaved_records = target.select(&:new_record?)
|
293
221
|
unsaved_records.size + count_records
|
294
222
|
else
|
295
223
|
count_records
|
296
224
|
end
|
297
225
|
end
|
298
226
|
|
299
|
-
# Returns the size of the collection calling +size+ on the target.
|
300
|
-
#
|
301
|
-
# If the collection has been already loaded +length+ and +size+ are
|
302
|
-
# equivalent. If not and you are going to need the records anyway this
|
303
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
304
|
-
def length
|
305
|
-
load_target.size
|
306
|
-
end
|
307
|
-
|
308
227
|
# Returns true if the collection is empty.
|
309
228
|
#
|
310
229
|
# If the collection has been loaded
|
311
230
|
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
312
231
|
# collection has not been loaded, it is equivalent to
|
313
|
-
# <tt
|
232
|
+
# <tt>!collection.exists?</tt>. If the collection has not already been
|
314
233
|
# loaded and you are going to fetch the records anyway it is better to
|
315
234
|
# check <tt>collection.length.zero?</tt>.
|
316
235
|
def empty?
|
317
|
-
if loaded?
|
236
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
318
237
|
size.zero?
|
319
238
|
else
|
320
|
-
|
239
|
+
target.empty? && !scope.exists?
|
321
240
|
end
|
322
241
|
end
|
323
242
|
|
324
|
-
# Returns true if the collections is not empty.
|
325
|
-
# Equivalent to +!collection.empty?+.
|
326
|
-
def any?
|
327
|
-
if block_given?
|
328
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
329
|
-
else
|
330
|
-
!empty?
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
# Returns true if the collection has more than 1 record.
|
335
|
-
# Equivalent to +collection.size > 1+.
|
336
|
-
def many?
|
337
|
-
if block_given?
|
338
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
339
|
-
else
|
340
|
-
size > 1
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
def distinct
|
345
|
-
seen = {}
|
346
|
-
load_target.find_all do |record|
|
347
|
-
seen[record.id] = true unless seen.key?(record.id)
|
348
|
-
end
|
349
|
-
end
|
350
|
-
alias uniq distinct
|
351
|
-
|
352
243
|
# Replace this collection with +other_array+. This will perform a diff
|
353
244
|
# and delete/add only records that have changed.
|
354
245
|
def replace(other_array)
|
@@ -361,6 +252,8 @@ module ActiveRecord
|
|
361
252
|
replace_common_records_in_memory(other_array, original_target)
|
362
253
|
if other_array != original_target
|
363
254
|
transaction { replace_records(other_array, original_target) }
|
255
|
+
else
|
256
|
+
other_array
|
364
257
|
end
|
365
258
|
end
|
366
259
|
end
|
@@ -386,32 +279,24 @@ module ActiveRecord
|
|
386
279
|
target
|
387
280
|
end
|
388
281
|
|
389
|
-
def add_to_target(record, skip_callbacks
|
390
|
-
|
391
|
-
index = @target.index(record)
|
392
|
-
end
|
393
|
-
replace_on_target(record, index, skip_callbacks, &block)
|
282
|
+
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
283
|
+
replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
|
394
284
|
end
|
395
285
|
|
396
|
-
def
|
397
|
-
|
398
|
-
yield(record) if block_given?
|
286
|
+
def target=(record)
|
287
|
+
return super unless ActiveRecord::Base.has_many_inversing
|
399
288
|
|
400
|
-
|
401
|
-
|
289
|
+
case record
|
290
|
+
when Array
|
291
|
+
super
|
402
292
|
else
|
403
|
-
|
293
|
+
replace_on_target(record, true, replace: true, inversing: true)
|
404
294
|
end
|
405
|
-
|
406
|
-
callback(:after_add, record) unless skip_callbacks
|
407
|
-
set_inverse_instance(record)
|
408
|
-
|
409
|
-
record
|
410
295
|
end
|
411
296
|
|
412
|
-
def scope
|
413
|
-
scope = super
|
414
|
-
scope.none! if
|
297
|
+
def scope
|
298
|
+
scope = super
|
299
|
+
scope.none! if null_scope?
|
415
300
|
scope
|
416
301
|
end
|
417
302
|
|
@@ -419,34 +304,15 @@ module ActiveRecord
|
|
419
304
|
owner.new_record? && !foreign_key_present?
|
420
305
|
end
|
421
306
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
return scope.to_a
|
430
|
-
end
|
431
|
-
|
432
|
-
conn = klass.connection
|
433
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
434
|
-
StatementCache.create(conn) { |params|
|
435
|
-
as = AssociationScope.create { params.bind }
|
436
|
-
target_scope.merge as.scope(self, conn)
|
437
|
-
}
|
438
|
-
end
|
439
|
-
|
440
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
441
|
-
sc.execute binds, klass, klass.connection
|
307
|
+
def find_from_target?
|
308
|
+
loaded? ||
|
309
|
+
owner.strict_loading? ||
|
310
|
+
reflection.strict_loading? ||
|
311
|
+
owner.new_record? ||
|
312
|
+
target.any? { |record| record.new_record? || record.changed? }
|
442
313
|
end
|
443
314
|
|
444
|
-
|
445
|
-
records = get_records
|
446
|
-
records.each { |record| set_inverse_instance(record) }
|
447
|
-
records
|
448
|
-
end
|
449
|
-
|
315
|
+
private
|
450
316
|
# We have some records loaded from the database (persisted) and some that are
|
451
317
|
# in-memory (memory). The same record may be represented in the persisted array
|
452
318
|
# and in the memory array.
|
@@ -464,7 +330,7 @@ module ActiveRecord
|
|
464
330
|
persisted.map! do |record|
|
465
331
|
if mem_record = memory.delete(record)
|
466
332
|
|
467
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.
|
333
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
468
334
|
mem_record[name] = record[name]
|
469
335
|
end
|
470
336
|
|
@@ -474,7 +340,7 @@ module ActiveRecord
|
|
474
340
|
end
|
475
341
|
end
|
476
342
|
|
477
|
-
persisted + memory
|
343
|
+
persisted + memory.reject(&:persisted?)
|
478
344
|
end
|
479
345
|
|
480
346
|
def _create_record(attributes, raise = false, &block)
|
@@ -485,28 +351,35 @@ module ActiveRecord
|
|
485
351
|
if attributes.is_a?(Array)
|
486
352
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
487
353
|
else
|
354
|
+
record = build_record(attributes, &block)
|
488
355
|
transaction do
|
489
|
-
|
490
|
-
|
491
|
-
insert_record(record, true, raise)
|
356
|
+
result = nil
|
357
|
+
add_to_target(record) do
|
358
|
+
result = insert_record(record, true, raise) {
|
359
|
+
@_was_loaded = loaded?
|
360
|
+
}
|
492
361
|
end
|
362
|
+
raise ActiveRecord::Rollback unless result
|
493
363
|
end
|
364
|
+
record
|
494
365
|
end
|
495
366
|
end
|
496
367
|
|
497
368
|
# Do the relevant stuff to insert the given record into the association collection.
|
498
|
-
def insert_record(record, validate = true, raise = false)
|
499
|
-
raise
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
369
|
+
def insert_record(record, validate = true, raise = false, &block)
|
370
|
+
if raise
|
371
|
+
record.save!(validate: validate, &block)
|
372
|
+
else
|
373
|
+
record.save(validate: validate, &block)
|
374
|
+
end
|
504
375
|
end
|
505
376
|
|
506
377
|
def delete_or_destroy(records, method)
|
378
|
+
return if records.empty?
|
379
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
507
380
|
records = records.flatten
|
508
381
|
records.each { |record| raise_on_type_mismatch!(record) }
|
509
|
-
existing_records = records.reject
|
382
|
+
existing_records = records.reject(&:new_record?)
|
510
383
|
|
511
384
|
if existing_records.empty?
|
512
385
|
remove_records(existing_records, records, method)
|
@@ -516,24 +389,28 @@ module ActiveRecord
|
|
516
389
|
end
|
517
390
|
|
518
391
|
def remove_records(existing_records, records, method)
|
519
|
-
|
392
|
+
catch(:abort) do
|
393
|
+
records.each { |record| callback(:before_remove, record) }
|
394
|
+
end || return
|
520
395
|
|
521
396
|
delete_records(existing_records, method) if existing_records.any?
|
522
|
-
|
397
|
+
@target -= records
|
398
|
+
@association_ids = nil
|
523
399
|
|
524
400
|
records.each { |record| callback(:after_remove, record) }
|
525
401
|
end
|
526
402
|
|
527
|
-
# Delete the given records from the association,
|
528
|
-
#
|
403
|
+
# Delete the given records from the association,
|
404
|
+
# using one of the methods +:destroy+, +:delete_all+
|
405
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
529
406
|
def delete_records(records, method)
|
530
407
|
raise NotImplementedError
|
531
408
|
end
|
532
409
|
|
533
410
|
def replace_records(new_target, original_target)
|
534
|
-
delete(target
|
411
|
+
delete(difference(target, new_target))
|
535
412
|
|
536
|
-
unless concat(new_target
|
413
|
+
unless concat(difference(new_target, target))
|
537
414
|
@target = original_target
|
538
415
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
539
416
|
"new records could not be saved."
|
@@ -543,24 +420,65 @@ module ActiveRecord
|
|
543
420
|
end
|
544
421
|
|
545
422
|
def replace_common_records_in_memory(new_target, original_target)
|
546
|
-
common_records = new_target
|
423
|
+
common_records = intersection(new_target, original_target)
|
547
424
|
common_records.each do |record|
|
548
425
|
skip_callbacks = true
|
549
|
-
replace_on_target(record,
|
426
|
+
replace_on_target(record, skip_callbacks, replace: true)
|
550
427
|
end
|
551
428
|
end
|
552
429
|
|
553
|
-
def concat_records(records,
|
430
|
+
def concat_records(records, raise = false)
|
554
431
|
result = true
|
555
432
|
|
556
|
-
records.
|
433
|
+
records.each do |record|
|
557
434
|
raise_on_type_mismatch!(record)
|
558
|
-
add_to_target(record) do
|
559
|
-
|
435
|
+
add_to_target(record) do
|
436
|
+
unless owner.new_record?
|
437
|
+
result &&= insert_record(record, true, raise) {
|
438
|
+
@_was_loaded = loaded?
|
439
|
+
}
|
440
|
+
end
|
560
441
|
end
|
561
442
|
end
|
562
443
|
|
563
|
-
|
444
|
+
raise ActiveRecord::Rollback unless result
|
445
|
+
|
446
|
+
records
|
447
|
+
end
|
448
|
+
|
449
|
+
def replace_on_target(record, skip_callbacks, replace:, inversing: false)
|
450
|
+
if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
|
451
|
+
index = @target.index(record)
|
452
|
+
end
|
453
|
+
|
454
|
+
catch(:abort) do
|
455
|
+
callback(:before_add, record)
|
456
|
+
end || return unless skip_callbacks
|
457
|
+
|
458
|
+
set_inverse_instance(record)
|
459
|
+
|
460
|
+
@_was_loaded = true
|
461
|
+
|
462
|
+
yield(record) if block_given?
|
463
|
+
|
464
|
+
if !index && @replaced_or_added_targets.include?(record)
|
465
|
+
index = @target.index(record)
|
466
|
+
end
|
467
|
+
|
468
|
+
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
469
|
+
|
470
|
+
if index
|
471
|
+
target[index] = record
|
472
|
+
elsif @_was_loaded || !loaded?
|
473
|
+
@association_ids = nil
|
474
|
+
target << record
|
475
|
+
end
|
476
|
+
|
477
|
+
callback(:after_add, record) unless skip_callbacks
|
478
|
+
|
479
|
+
record
|
480
|
+
ensure
|
481
|
+
@_was_loaded = nil
|
564
482
|
end
|
565
483
|
|
566
484
|
def callback(method, record)
|
@@ -574,31 +492,12 @@ module ActiveRecord
|
|
574
492
|
owner.class.send(full_callback_name)
|
575
493
|
end
|
576
494
|
|
577
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
578
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
579
|
-
#
|
580
|
-
# If the args is just a non-empty options hash, go to the database.
|
581
|
-
#
|
582
|
-
# Otherwise, go to the database only if none of the following are true:
|
583
|
-
# * target already loaded
|
584
|
-
# * owner is new record
|
585
|
-
# * target contains new or changed record(s)
|
586
|
-
def fetch_first_nth_or_last_using_find?(args)
|
587
|
-
if args.first.is_a?(Hash)
|
588
|
-
true
|
589
|
-
else
|
590
|
-
!(loaded? ||
|
591
|
-
owner.new_record? ||
|
592
|
-
target.any? { |record| record.new_record? || record.changed? })
|
593
|
-
end
|
594
|
-
end
|
595
|
-
|
596
495
|
def include_in_memory?(record)
|
597
496
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
598
497
|
assoc = owner.association(reflection.through_reflection.name)
|
599
498
|
assoc.reader.any? { |source|
|
600
|
-
|
601
|
-
|
499
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
500
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
602
501
|
} || target.include?(record)
|
603
502
|
else
|
604
503
|
target.include?(record)
|
@@ -609,7 +508,7 @@ module ActiveRecord
|
|
609
508
|
# specified, then #find scans the entire collection.
|
610
509
|
def find_by_scan(*args)
|
611
510
|
expects_array = args.first.kind_of?(Array)
|
612
|
-
ids = args.flatten.compact.map
|
511
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
613
512
|
|
614
513
|
if ids.size == 1
|
615
514
|
id = ids.first
|
@@ -619,16 +518,6 @@ module ActiveRecord
|
|
619
518
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
620
519
|
end
|
621
520
|
end
|
622
|
-
|
623
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
624
|
-
def first_nth_or_last(type, *args)
|
625
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
626
|
-
|
627
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
628
|
-
collection.send(type, *args).tap do |record|
|
629
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
630
|
-
end
|
631
|
-
end
|
632
521
|
end
|
633
522
|
end
|
634
523
|
end
|