activerecord 3.2.6 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +611 -6417
- data/MIT-LICENSE +4 -2
- data/README.rdoc +44 -47
- data/examples/performance.rb +79 -71
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +268 -238
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +173 -81
- data/lib/active_record/associations/association_scope.rb +124 -92
- data/lib/active_record/associations/belongs_to_association.rb +83 -38
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
- data/lib/active_record/associations/builder/association.rb +113 -32
- data/lib/active_record/associations/builder/belongs_to.rb +105 -60
- data/lib/active_record/associations/builder/collection_association.rb +53 -56
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
- data/lib/active_record/associations/builder/has_many.rb +11 -63
- data/lib/active_record/associations/builder/has_one.rb +47 -45
- data/lib/active_record/associations/builder/singular_association.rb +30 -18
- data/lib/active_record/associations/collection_association.rb +217 -295
- data/lib/active_record/associations/collection_proxy.rb +1074 -77
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +78 -50
- data/lib/active_record/associations/has_many_through_association.rb +99 -61
- data/lib/active_record/associations/has_one_association.rb +75 -30
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +208 -164
- data/lib/active_record/associations/preloader/association.rb +93 -87
- data/lib/active_record/associations/preloader/through_association.rb +87 -38
- data/lib/active_record/associations/preloader.rb +134 -110
- data/lib/active_record/associations/singular_association.rb +19 -24
- data/lib/active_record/associations/through_association.rb +61 -27
- data/lib/active_record/associations.rb +1766 -1505
- data/lib/active_record/attribute_assignment.rb +57 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
- data/lib/active_record/attribute_methods/dirty.rb +187 -67
- data/lib/active_record/attribute_methods/primary_key.rb +100 -78
- data/lib/active_record/attribute_methods/query.rb +10 -8
- data/lib/active_record/attribute_methods/read.rb +29 -118
- data/lib/active_record/attribute_methods/serialization.rb +60 -72
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
- data/lib/active_record/attribute_methods/write.rb +36 -44
- data/lib/active_record/attribute_methods.rb +306 -161
- data/lib/active_record/attributes.rb +279 -0
- data/lib/active_record/autosave_association.rb +324 -238
- data/lib/active_record/base.rb +114 -507
- data/lib/active_record/callbacks.rb +147 -83
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
- data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
- data/lib/active_record/connection_adapters/column.rb +58 -233
- data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -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 +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
- data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -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 +137 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +267 -0
- data/lib/active_record/core.rb +599 -0
- data/lib/active_record/counter_cache.rb +177 -103
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -64
- data/lib/active_record/enum.rb +274 -0
- data/lib/active_record/errors.rb +254 -61
- data/lib/active_record/explain.rb +35 -70
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -8
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +291 -475
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +219 -100
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +175 -17
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +106 -92
- data/lib/active_record/locking/pessimistic.rb +23 -11
- data/lib/active_record/log_subscriber.rb +80 -30
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +235 -56
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +917 -301
- data/lib/active_record/model_schema.rb +351 -175
- data/lib/active_record/nested_attributes.rb +366 -235
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +761 -166
- data/lib/active_record/query_cache.rb +22 -44
- data/lib/active_record/querying.rb +55 -31
- data/lib/active_record/railtie.rb +185 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +35 -33
- data/lib/active_record/railties/databases.rake +366 -463
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +736 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +252 -52
- data/lib/active_record/relation/calculations.rb +340 -270
- data/lib/active_record/relation/delegation.rb +117 -36
- data/lib/active_record/relation/finder_methods.rb +439 -286
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +184 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -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 +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +131 -39
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +1163 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +49 -120
- data/lib/active_record/relation/where_clause.rb +190 -0
- data/lib/active_record/relation/where_clause_factory.rb +33 -0
- data/lib/active_record/relation.rb +671 -349
- data/lib/active_record/result.rb +149 -15
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +153 -133
- data/lib/active_record/schema.rb +22 -19
- data/lib/active_record/schema_dumper.rb +178 -112
- data/lib/active_record/schema_migration.rb +60 -0
- data/lib/active_record/scoping/default.rb +107 -98
- data/lib/active_record/scoping/named.rb +130 -115
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +148 -0
- data/lib/active_record/store.rb +256 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +75 -0
- data/lib/active_record/tasks/database_tasks.rb +506 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +93 -39
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +260 -129
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -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 +25 -0
- 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 +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +78 -0
- data/lib/active_record/type_caster/connection.rb +34 -0
- data/lib/active_record/type_caster/map.rb +20 -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 +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +123 -77
- data/lib/active_record/validations.rb +54 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +97 -49
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -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 +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -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 +18 -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 +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -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 +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -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 +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -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 +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -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 +59 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +41 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +285 -149
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- 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_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- 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_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
@@ -6,7 +6,15 @@ module ActiveRecord
|
|
6
6
|
#
|
7
7
|
# CollectionAssociation is an abstract class that provides common stuff to
|
8
8
|
# ease the implementation of association proxies that represent
|
9
|
-
# collections. See the class hierarchy in
|
9
|
+
# collections. See the class hierarchy in Association.
|
10
|
+
#
|
11
|
+
# CollectionAssociation:
|
12
|
+
# HasManyAssociation => has_many
|
13
|
+
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
14
|
+
#
|
15
|
+
# The CollectionAssociation class provides common methods to the collections
|
16
|
+
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
17
|
+
# the +:through association+ option.
|
10
18
|
#
|
11
19
|
# You need to be careful with assumptions regarding the target: The proxy
|
12
20
|
# does not fetch records from the database until it needs them, but new
|
@@ -18,22 +26,14 @@ module ActiveRecord
|
|
18
26
|
# If you need to work on all current children, new and existing records,
|
19
27
|
# +load_target+ and the +loaded+ flag are your friends.
|
20
28
|
class CollectionAssociation < Association #:nodoc:
|
21
|
-
attr_reader :proxy
|
22
|
-
|
23
|
-
def initialize(owner, reflection)
|
24
|
-
super
|
25
|
-
@proxy = CollectionProxy.new(self)
|
26
|
-
end
|
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
|
-
proxy
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
37
37
|
end
|
38
38
|
|
39
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -43,92 +43,78 @@ module ActiveRecord
|
|
43
43
|
|
44
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
45
|
def ids_reader
|
46
|
-
if loaded?
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
if loaded?
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
50
50
|
else
|
51
|
-
|
52
|
-
relation = scoped
|
53
|
-
|
54
|
-
including = (relation.eager_load_values + relation.includes_values).uniq
|
55
|
-
|
56
|
-
if including.any?
|
57
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
|
58
|
-
relation = join_dependency.join_associations.inject(relation) do |r, association|
|
59
|
-
association.join_relation(r)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
relation.pluck(column)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
64
52
|
end
|
65
53
|
end
|
66
54
|
|
67
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
68
56
|
def ids_writer(ids)
|
69
|
-
|
70
|
-
|
71
|
-
ids
|
72
|
-
|
57
|
+
primary_key = reflection.association_primary_key
|
58
|
+
pk_type = klass.type_for_attribute(primary_key)
|
59
|
+
ids = Array(ids).reject(&: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
|
73
73
|
end
|
74
74
|
|
75
75
|
def reset
|
76
|
-
|
76
|
+
super
|
77
77
|
@target = []
|
78
|
-
|
79
|
-
|
80
|
-
def select(select = nil)
|
81
|
-
if block_given?
|
82
|
-
load_target.select.each { |e| yield e }
|
83
|
-
else
|
84
|
-
scoped.select(select)
|
85
|
-
end
|
78
|
+
@association_ids = nil
|
86
79
|
end
|
87
80
|
|
88
81
|
def find(*args)
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
85
|
+
|
86
|
+
if args_flatten.blank?
|
87
|
+
error_message = "Couldn't find #{model.name} without an ID"
|
88
|
+
raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
|
96
89
|
end
|
97
|
-
end
|
98
|
-
end
|
99
90
|
|
100
|
-
|
101
|
-
first_or_last(:first, *args)
|
102
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
103
92
|
|
104
|
-
|
105
|
-
|
93
|
+
result_size = Array(result).size
|
94
|
+
if !result || result_size != args_flatten.size
|
95
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
96
|
+
else
|
97
|
+
result
|
98
|
+
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
101
|
+
end
|
106
102
|
end
|
107
103
|
|
108
|
-
def build(attributes = {},
|
104
|
+
def build(attributes = {}, &block)
|
109
105
|
if attributes.is_a?(Array)
|
110
|
-
attributes.collect { |attr| build(attr,
|
106
|
+
attributes.collect { |attr| build(attr, &block) }
|
111
107
|
else
|
112
|
-
add_to_target(build_record(attributes,
|
113
|
-
yield(record) if block_given?
|
114
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
115
109
|
end
|
116
110
|
end
|
117
111
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
def create!(attributes = {}, options = {}, &block)
|
123
|
-
create_record(attributes, options, true, &block)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Add +records+ to this association. Returns +self+ so method calls may be chained.
|
127
|
-
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
|
112
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
113
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
128
114
|
def concat(*records)
|
129
|
-
|
130
|
-
|
115
|
+
records = records.flatten
|
131
116
|
if owner.new_record?
|
117
|
+
load_target
|
132
118
|
concat_records(records)
|
133
119
|
else
|
134
120
|
transaction { concat_records(records) }
|
@@ -150,23 +136,38 @@ module ActiveRecord
|
|
150
136
|
end
|
151
137
|
end
|
152
138
|
|
153
|
-
#
|
139
|
+
# Removes all records from the association without calling callbacks
|
140
|
+
# on the associated records. It honors the +:dependent+ option. However
|
141
|
+
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
142
|
+
# deletion strategy for the association is applied.
|
143
|
+
#
|
144
|
+
# You can force a particular deletion strategy by passing a parameter.
|
145
|
+
#
|
146
|
+
# Example:
|
147
|
+
#
|
148
|
+
# @author.books.delete_all(:nullify)
|
149
|
+
# @author.books.delete_all(:delete_all)
|
154
150
|
#
|
155
151
|
# See delete for more info.
|
156
|
-
def delete_all
|
157
|
-
|
152
|
+
def delete_all(dependent = nil)
|
153
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
154
|
+
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
155
|
+
end
|
156
|
+
|
157
|
+
dependent = if dependent
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
164
|
+
|
165
|
+
delete_or_nullify_all_records(dependent).tap do
|
158
166
|
reset
|
159
167
|
loaded!
|
160
168
|
end
|
161
169
|
end
|
162
170
|
|
163
|
-
# Called when the association is declared as :dependent => :delete_all. This is
|
164
|
-
# an optimised version which avoids loading the records into memory. Not really
|
165
|
-
# for public consumption.
|
166
|
-
def delete_all_on_destroy
|
167
|
-
scoped.delete_all
|
168
|
-
end
|
169
|
-
|
170
171
|
# Destroy all the records from this association.
|
171
172
|
#
|
172
173
|
# See destroy for more info.
|
@@ -177,47 +178,6 @@ module ActiveRecord
|
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
180
|
-
# Calculate sum using SQL, not Enumerable
|
181
|
-
def sum(*args)
|
182
|
-
if block_given?
|
183
|
-
scoped.sum(*args) { |*block_args| yield(*block_args) }
|
184
|
-
else
|
185
|
-
scoped.sum(*args)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
|
190
|
-
# association, it will be used for the query. Otherwise, construct options and pass them with
|
191
|
-
# scope to the target class's +count+.
|
192
|
-
def count(column_name = nil, count_options = {})
|
193
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
194
|
-
|
195
|
-
if options[:counter_sql] || options[:finder_sql]
|
196
|
-
unless count_options.blank?
|
197
|
-
raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
|
198
|
-
end
|
199
|
-
|
200
|
-
reflection.klass.count_by_sql(custom_counter_sql)
|
201
|
-
else
|
202
|
-
if options[:uniq]
|
203
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
204
|
-
column_name ||= reflection.klass.primary_key
|
205
|
-
count_options.merge!(:distinct => true)
|
206
|
-
end
|
207
|
-
|
208
|
-
value = scoped.count(column_name, count_options)
|
209
|
-
|
210
|
-
limit = options[:limit]
|
211
|
-
offset = options[:offset]
|
212
|
-
|
213
|
-
if limit || offset
|
214
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
215
|
-
else
|
216
|
-
value
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
181
|
# Removes +records+ from this association calling +before_remove+ and
|
222
182
|
# +after_remove+ callbacks.
|
223
183
|
#
|
@@ -229,13 +189,12 @@ module ActiveRecord
|
|
229
189
|
delete_or_destroy(records, options[:dependent])
|
230
190
|
end
|
231
191
|
|
232
|
-
#
|
233
|
-
# +before_remove+
|
192
|
+
# Deletes the +records+ and removes them from this association calling
|
193
|
+
# +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
|
234
194
|
#
|
235
|
-
# Note that this method
|
236
|
-
#
|
195
|
+
# Note that this method removes records from the database ignoring the
|
196
|
+
# +:dependent+ option.
|
237
197
|
def destroy(*records)
|
238
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
239
198
|
delete_or_destroy(records, :destroy)
|
240
199
|
end
|
241
200
|
|
@@ -250,68 +209,51 @@ module ActiveRecord
|
|
250
209
|
# This method is abstract in the sense that it relies on
|
251
210
|
# +count_records+, which is a method descendants have to provide.
|
252
211
|
def size
|
253
|
-
if !find_target? ||
|
212
|
+
if !find_target? || loaded?
|
254
213
|
target.size
|
255
|
-
elsif
|
214
|
+
elsif @association_ids
|
215
|
+
@association_ids.size
|
216
|
+
elsif !association_scope.group_values.empty?
|
256
217
|
load_target.size
|
257
|
-
elsif !
|
258
|
-
unsaved_records = target.select
|
218
|
+
elsif !association_scope.distinct_value && !target.empty?
|
219
|
+
unsaved_records = target.select(&:new_record?)
|
259
220
|
unsaved_records.size + count_records
|
260
221
|
else
|
261
222
|
count_records
|
262
223
|
end
|
263
224
|
end
|
264
225
|
|
265
|
-
# Returns
|
226
|
+
# Returns true if the collection is empty.
|
266
227
|
#
|
267
|
-
# If the collection has been
|
268
|
-
#
|
269
|
-
#
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
# Equivalent to <tt>collection.size.zero?</tt>. If the collection has
|
275
|
-
# not been already loaded and you are going to fetch the records anyway
|
276
|
-
# it is better to check <tt>collection.length.zero?</tt>.
|
228
|
+
# If the collection has been loaded
|
229
|
+
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
230
|
+
# collection has not been loaded, it is equivalent to
|
231
|
+
# <tt>collection.exists?</tt>. If the collection has not already been
|
232
|
+
# loaded and you are going to fetch the records anyway it is better to
|
233
|
+
# check <tt>collection.length.zero?</tt>.
|
277
234
|
def empty?
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
def any?
|
282
|
-
if block_given?
|
283
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
284
|
-
else
|
285
|
-
!empty?
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
|
-
# Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
|
290
|
-
def many?
|
291
|
-
if block_given?
|
292
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
236
|
+
size.zero?
|
293
237
|
else
|
294
|
-
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
def uniq(collection = load_target)
|
299
|
-
seen = {}
|
300
|
-
collection.find_all do |record|
|
301
|
-
seen[record.id] = true unless seen.key?(record.id)
|
238
|
+
target.empty? && !scope.exists?
|
302
239
|
end
|
303
240
|
end
|
304
241
|
|
305
|
-
# Replace this collection with +other_array
|
306
|
-
#
|
242
|
+
# Replace this collection with +other_array+. This will perform a diff
|
243
|
+
# and delete/add only records that have changed.
|
307
244
|
def replace(other_array)
|
308
|
-
other_array.each { |val| raise_on_type_mismatch(val) }
|
245
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
309
246
|
original_target = load_target.dup
|
310
247
|
|
311
248
|
if owner.new_record?
|
312
249
|
replace_records(other_array, original_target)
|
313
250
|
else
|
314
|
-
|
251
|
+
replace_common_records_in_memory(other_array, original_target)
|
252
|
+
if other_array != original_target
|
253
|
+
transaction { replace_records(other_array, original_target) }
|
254
|
+
else
|
255
|
+
other_array
|
256
|
+
end
|
315
257
|
end
|
316
258
|
end
|
317
259
|
|
@@ -320,8 +262,7 @@ module ActiveRecord
|
|
320
262
|
if record.new_record?
|
321
263
|
include_in_memory?(record)
|
322
264
|
else
|
323
|
-
|
324
|
-
loaded? ? target.include?(record) : scoped.exists?(record)
|
265
|
+
loaded? ? target.include?(record) : scope.exists?(record.id)
|
325
266
|
end
|
326
267
|
else
|
327
268
|
false
|
@@ -337,54 +278,30 @@ module ActiveRecord
|
|
337
278
|
target
|
338
279
|
end
|
339
280
|
|
340
|
-
def add_to_target(record)
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
if options[:uniq] && index = @target.index(record)
|
345
|
-
@target[index] = record
|
346
|
-
else
|
347
|
-
@target << record
|
281
|
+
def add_to_target(record, skip_callbacks = false, &block)
|
282
|
+
if association_scope.distinct_value
|
283
|
+
index = @target.index(record)
|
348
284
|
end
|
349
|
-
|
350
|
-
callback(:after_add, record)
|
351
|
-
set_inverse_instance(record)
|
352
|
-
|
353
|
-
record
|
285
|
+
replace_on_target(record, index, skip_callbacks, &block)
|
354
286
|
end
|
355
287
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
else
|
362
|
-
# replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
|
363
|
-
interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
|
364
|
-
count_with = $2.to_s
|
365
|
-
count_with = '*' if count_with.blank? || count_with =~ /,/
|
366
|
-
"SELECT #{$1}COUNT(#{count_with}) FROM"
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
def custom_finder_sql
|
372
|
-
interpolate(options[:finder_sql])
|
373
|
-
end
|
288
|
+
def scope
|
289
|
+
scope = super
|
290
|
+
scope.none! if null_scope?
|
291
|
+
scope
|
292
|
+
end
|
374
293
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
reflection.klass.find_by_sql(custom_finder_sql)
|
379
|
-
else
|
380
|
-
scoped.all
|
381
|
-
end
|
294
|
+
def null_scope?
|
295
|
+
owner.new_record? && !foreign_key_present?
|
296
|
+
end
|
382
297
|
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
298
|
+
def find_from_target?
|
299
|
+
loaded? ||
|
300
|
+
owner.new_record? ||
|
301
|
+
target.any? { |record| record.new_record? || record.changed? }
|
302
|
+
end
|
387
303
|
|
304
|
+
private
|
388
305
|
# We have some records loaded from the database (persisted) and some that are
|
389
306
|
# in-memory (memory). The same record may be represented in the persisted array
|
390
307
|
# and in the memory array.
|
@@ -400,14 +317,9 @@ module ActiveRecord
|
|
400
317
|
return memory if persisted.empty?
|
401
318
|
|
402
319
|
persisted.map! do |record|
|
403
|
-
|
404
|
-
# record rather than memory.at(memory.index(record)). The behavior is fixed in 1.9.
|
405
|
-
mem_index = memory.index(record)
|
406
|
-
|
407
|
-
if mem_index
|
408
|
-
mem_record = memory.delete_at(mem_index)
|
320
|
+
if mem_record = memory.delete(record)
|
409
321
|
|
410
|
-
(record.attribute_names - mem_record.
|
322
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
411
323
|
mem_record[name] = record[name]
|
412
324
|
end
|
413
325
|
|
@@ -420,36 +332,43 @@ module ActiveRecord
|
|
420
332
|
persisted + memory
|
421
333
|
end
|
422
334
|
|
423
|
-
def
|
335
|
+
def _create_record(attributes, raise = false, &block)
|
424
336
|
unless owner.persisted?
|
425
337
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
426
338
|
end
|
427
339
|
|
428
340
|
if attributes.is_a?(Array)
|
429
|
-
attributes.collect { |attr|
|
341
|
+
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
430
342
|
else
|
343
|
+
record = build_record(attributes, &block)
|
431
344
|
transaction do
|
432
|
-
|
433
|
-
|
434
|
-
insert_record(record, true, raise)
|
345
|
+
result = nil
|
346
|
+
add_to_target(record) do
|
347
|
+
result = insert_record(record, true, raise) {
|
348
|
+
@_was_loaded = loaded?
|
349
|
+
}
|
435
350
|
end
|
351
|
+
raise ActiveRecord::Rollback unless result
|
436
352
|
end
|
353
|
+
record
|
437
354
|
end
|
438
355
|
end
|
439
356
|
|
440
357
|
# Do the relevant stuff to insert the given record into the association collection.
|
441
|
-
def insert_record(record, validate = true, raise = false)
|
442
|
-
raise
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
358
|
+
def insert_record(record, validate = true, raise = false, &block)
|
359
|
+
if raise
|
360
|
+
record.save!(validate: validate, &block)
|
361
|
+
else
|
362
|
+
record.save(validate: validate, &block)
|
363
|
+
end
|
447
364
|
end
|
448
365
|
|
449
366
|
def delete_or_destroy(records, method)
|
367
|
+
return if records.empty?
|
368
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
450
369
|
records = records.flatten
|
451
|
-
records.each { |record| raise_on_type_mismatch(record) }
|
452
|
-
existing_records = records.reject
|
370
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
371
|
+
existing_records = records.reject(&:new_record?)
|
453
372
|
|
454
373
|
if existing_records.empty?
|
455
374
|
remove_records(existing_records, records, method)
|
@@ -462,21 +381,23 @@ module ActiveRecord
|
|
462
381
|
records.each { |record| callback(:before_remove, record) }
|
463
382
|
|
464
383
|
delete_records(existing_records, method) if existing_records.any?
|
465
|
-
|
384
|
+
@target -= records
|
385
|
+
@association_ids = nil
|
466
386
|
|
467
387
|
records.each { |record| callback(:after_remove, record) }
|
468
388
|
end
|
469
389
|
|
470
|
-
# Delete the given records from the association,
|
471
|
-
#
|
390
|
+
# Delete the given records from the association,
|
391
|
+
# using one of the methods +:destroy+, +:delete_all+
|
392
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
472
393
|
def delete_records(records, method)
|
473
394
|
raise NotImplementedError
|
474
395
|
end
|
475
396
|
|
476
397
|
def replace_records(new_target, original_target)
|
477
|
-
delete(target
|
398
|
+
delete(difference(target, new_target))
|
478
399
|
|
479
|
-
unless concat(new_target
|
400
|
+
unless concat(difference(new_target, target))
|
480
401
|
@target = original_target
|
481
402
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
482
403
|
"new records could not be saved."
|
@@ -485,92 +406,93 @@ module ActiveRecord
|
|
485
406
|
target
|
486
407
|
end
|
487
408
|
|
488
|
-
def
|
409
|
+
def replace_common_records_in_memory(new_target, original_target)
|
410
|
+
common_records = intersection(new_target, original_target)
|
411
|
+
common_records.each do |record|
|
412
|
+
skip_callbacks = true
|
413
|
+
replace_on_target(record, @target.index(record), skip_callbacks)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def concat_records(records, raise = false)
|
489
418
|
result = true
|
490
419
|
|
491
|
-
records.
|
492
|
-
raise_on_type_mismatch(record)
|
493
|
-
add_to_target(record) do
|
494
|
-
|
420
|
+
records.each do |record|
|
421
|
+
raise_on_type_mismatch!(record)
|
422
|
+
add_to_target(record) do
|
423
|
+
unless owner.new_record?
|
424
|
+
result &&= insert_record(record, true, raise) {
|
425
|
+
@_was_loaded = loaded?
|
426
|
+
}
|
427
|
+
end
|
495
428
|
end
|
496
429
|
end
|
497
430
|
|
498
|
-
|
431
|
+
raise ActiveRecord::Rollback unless result
|
432
|
+
|
433
|
+
records
|
434
|
+
end
|
435
|
+
|
436
|
+
def replace_on_target(record, index, skip_callbacks)
|
437
|
+
callback(:before_add, record) unless skip_callbacks
|
438
|
+
|
439
|
+
set_inverse_instance(record)
|
440
|
+
|
441
|
+
@_was_loaded = true
|
442
|
+
|
443
|
+
yield(record) if block_given?
|
444
|
+
|
445
|
+
if index
|
446
|
+
target[index] = record
|
447
|
+
elsif @_was_loaded || !loaded?
|
448
|
+
@association_ids = nil
|
449
|
+
target << record
|
450
|
+
end
|
451
|
+
|
452
|
+
callback(:after_add, record) unless skip_callbacks
|
453
|
+
|
454
|
+
record
|
455
|
+
ensure
|
456
|
+
@_was_loaded = nil
|
499
457
|
end
|
500
458
|
|
501
459
|
def callback(method, record)
|
502
460
|
callbacks_for(method).each do |callback|
|
503
|
-
|
504
|
-
when Symbol
|
505
|
-
owner.send(callback, record)
|
506
|
-
when Proc
|
507
|
-
callback.call(owner, record)
|
508
|
-
else
|
509
|
-
callback.send(method, owner, record)
|
510
|
-
end
|
461
|
+
callback.call(method, owner, record)
|
511
462
|
end
|
512
463
|
end
|
513
464
|
|
514
465
|
def callbacks_for(callback_name)
|
515
466
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
516
|
-
owner.class.send(full_callback_name
|
517
|
-
end
|
518
|
-
|
519
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
520
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
521
|
-
#
|
522
|
-
# If the args is just a non-empty options hash, go to the database.
|
523
|
-
#
|
524
|
-
# Otherwise, go to the database only if none of the following are true:
|
525
|
-
# * target already loaded
|
526
|
-
# * owner is new record
|
527
|
-
# * custom :finder_sql exists
|
528
|
-
# * target contains new or changed record(s)
|
529
|
-
# * the first arg is an integer (which indicates the number of records to be returned)
|
530
|
-
def fetch_first_or_last_using_find?(args)
|
531
|
-
if args.first.is_a?(Hash)
|
532
|
-
true
|
533
|
-
else
|
534
|
-
!(loaded? ||
|
535
|
-
owner.new_record? ||
|
536
|
-
options[:finder_sql] ||
|
537
|
-
target.any? { |record| record.new_record? || record.changed? } ||
|
538
|
-
args.first.kind_of?(Integer))
|
539
|
-
end
|
467
|
+
owner.class.send(full_callback_name)
|
540
468
|
end
|
541
469
|
|
542
470
|
def include_in_memory?(record)
|
543
471
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
544
|
-
owner.
|
545
|
-
|
546
|
-
|
472
|
+
assoc = owner.association(reflection.through_reflection.name)
|
473
|
+
assoc.reader.any? { |source|
|
474
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
475
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
547
476
|
} || target.include?(record)
|
548
477
|
else
|
549
478
|
target.include?(record)
|
550
479
|
end
|
551
480
|
end
|
552
481
|
|
553
|
-
# If
|
482
|
+
# If the :inverse_of option has been
|
483
|
+
# specified, then #find scans the entire collection.
|
554
484
|
def find_by_scan(*args)
|
555
485
|
expects_array = args.first.kind_of?(Array)
|
556
|
-
ids = args.flatten.compact.
|
486
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
557
487
|
|
558
488
|
if ids.size == 1
|
559
489
|
id = ids.first
|
560
|
-
record = load_target.detect { |r| id == r.id }
|
490
|
+
record = load_target.detect { |r| id == r.id.to_s }
|
561
491
|
expects_array ? [ record ] : record
|
562
492
|
else
|
563
|
-
load_target.select { |r| ids.include?(r.id) }
|
493
|
+
load_target.select { |r| ids.include?(r.id.to_s) }
|
564
494
|
end
|
565
495
|
end
|
566
|
-
|
567
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
568
|
-
def first_or_last(type, *args)
|
569
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
570
|
-
|
571
|
-
collection = fetch_first_or_last_using_find?(args) ? scoped : load_target
|
572
|
-
collection.send(type, *args)
|
573
|
-
end
|
574
496
|
end
|
575
497
|
end
|
576
498
|
end
|