activerecord 5.0.7.2 → 6.0.3.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +708 -2040
- data/MIT-LICENSE +3 -1
- data/README.rdoc +9 -7
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -22
- data/lib/active_record/advisory_lock_base.rb +18 -0
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +18 -14
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/associations/alias_tracker.rb +24 -34
- data/lib/active_record/associations/association.rb +114 -55
- data/lib/active_record/associations/association_scope.rb +94 -94
- data/lib/active_record/associations/belongs_to_association.rb +58 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +18 -25
- data/lib/active_record/associations/builder/belongs_to.rb +43 -54
- data/lib/active_record/associations/builder/collection_association.rb +7 -18
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +41 -62
- data/lib/active_record/associations/builder/has_many.rb +4 -0
- data/lib/active_record/associations/builder/has_one.rb +37 -1
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +86 -254
- data/lib/active_record/associations/collection_proxy.rb +158 -122
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -30
- data/lib/active_record/associations/has_many_through_association.rb +58 -44
- data/lib/active_record/associations/has_one_association.rb +59 -54
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +143 -176
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/preloader.rb +90 -103
- data/lib/active_record/associations/preloader/association.rb +86 -100
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +26 -14
- data/lib/active_record/attribute_assignment.rb +54 -61
- data/lib/active_record/attribute_decorators.rb +38 -17
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -8
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +85 -92
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +20 -49
- data/lib/active_record/attribute_methods/serialization.rb +29 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -66
- data/lib/active_record/attribute_methods/write.rb +34 -33
- data/lib/active_record/attributes.rb +38 -25
- data/lib/active_record/autosave_association.rb +54 -35
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +64 -35
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +552 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +215 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -28
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +228 -147
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +400 -213
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -79
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +396 -562
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -139
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +137 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +48 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +19 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -54
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -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/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -31
- 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 +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +20 -26
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +378 -308
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +383 -275
- data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
- 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 +259 -266
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +143 -40
- data/lib/active_record/core.rb +201 -163
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +233 -0
- 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 +78 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +60 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -4
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +13 -8
- 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 +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +194 -504
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +150 -99
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +16 -19
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +77 -87
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +48 -29
- data/lib/active_record/middleware/database_selector.rb +74 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +369 -302
- data/lib/active_record/migration/command_recorder.rb +134 -100
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +131 -127
- data/lib/active_record/nested_attributes.rb +213 -202
- data/lib/active_record/no_touching.rb +12 -3
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +446 -77
- data/lib/active_record/query_cache.rb +13 -12
- data/lib/active_record/querying.rb +37 -24
- data/lib/active_record/railtie.rb +128 -36
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +312 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +214 -252
- data/lib/active_record/relation.rb +440 -318
- data/lib/active_record/relation/batches.rb +98 -52
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/calculations.rb +212 -173
- data/lib/active_record/relation/delegation.rb +72 -69
- data/lib/active_record/relation/finder_methods.rb +207 -247
- data/lib/active_record/relation/from_clause.rb +6 -8
- data/lib/active_record/relation/merger.rb +78 -62
- data/lib/active_record/relation/predicate_builder.rb +83 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +476 -334
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -96
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/result.rb +69 -40
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +83 -99
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +71 -69
- data/lib/active_record/schema_migration.rb +16 -6
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +47 -27
- data/lib/active_record/secure_token.rb +4 -2
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +63 -28
- data/lib/active_record/store.rb +121 -41
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +39 -18
- data/lib/active_record/tasks/database_tasks.rb +271 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -91
- data/lib/active_record/tasks/postgresql_database_tasks.rb +77 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +33 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +225 -0
- data/lib/active_record/timestamp.rb +70 -36
- data/lib/active_record/touch_later.rb +8 -6
- data/lib/active_record/transactions.rb +141 -157
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +23 -18
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +16 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +2 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -12
- data/lib/active_record/type_caster/map.rb +5 -4
- data/lib/active_record/validations.rb +7 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +29 -42
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +62 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -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.rb +68 -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/order_predications.rb +13 -0
- data/lib/arel/predications.rb +256 -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.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +203 -0
- data/lib/arel/visitors/dot.rb +296 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +156 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +158 -0
- data/lib/arel/visitors/oracle12.rb +65 -0
- data/lib/arel/visitors/postgresql.rb +109 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +888 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors/where_sql.rb +22 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +17 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -30
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +137 -52
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- 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 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,38 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
2
4
|
class HasAndBelongsToMany # :nodoc:
|
3
|
-
class JoinTableResolver # :nodoc:
|
4
|
-
KnownTable = Struct.new :join_table
|
5
|
-
|
6
|
-
class KnownClass # :nodoc:
|
7
|
-
def initialize(lhs_class, rhs_class_name)
|
8
|
-
@lhs_class = lhs_class
|
9
|
-
@rhs_class_name = rhs_class_name
|
10
|
-
@join_table = nil
|
11
|
-
end
|
12
|
-
|
13
|
-
def join_table
|
14
|
-
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def klass
|
20
|
-
@lhs_class.send(:compute_type, @rhs_class_name)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.build(lhs_class, name, options)
|
25
|
-
if options[:join_table]
|
26
|
-
KnownTable.new options[:join_table].to_s
|
27
|
-
else
|
28
|
-
class_name = options.fetch(:class_name) {
|
29
|
-
name.to_s.camelize.singularize
|
30
|
-
}
|
31
|
-
KnownClass.new lhs_class, class_name
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
5
|
attr_reader :lhs_model, :association_name, :options
|
37
6
|
|
38
7
|
def initialize(association_name, lhs_model, options)
|
@@ -42,10 +11,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
42
11
|
end
|
43
12
|
|
44
13
|
def through_model
|
45
|
-
habtm = JoinTableResolver.build lhs_model, association_name, options
|
46
|
-
|
47
14
|
join_model = Class.new(ActiveRecord::Base) {
|
48
|
-
class << self
|
15
|
+
class << self
|
49
16
|
attr_accessor :left_model
|
50
17
|
attr_accessor :name
|
51
18
|
attr_accessor :table_name_resolver
|
@@ -54,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
54
21
|
end
|
55
22
|
|
56
23
|
def self.table_name
|
57
|
-
|
24
|
+
# Table name needs to be resolved lazily
|
25
|
+
# because RHS class might not have been loaded
|
26
|
+
@table_name ||= table_name_resolver.call
|
58
27
|
end
|
59
28
|
|
60
29
|
def self.compute_type(class_name)
|
@@ -77,14 +46,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
77
46
|
end
|
78
47
|
|
79
48
|
private
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
49
|
+
def self.suppress_composite_primary_key(pk)
|
50
|
+
pk unless pk.is_a?(Array)
|
51
|
+
end
|
84
52
|
}
|
85
53
|
|
86
54
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
87
|
-
join_model.table_name_resolver =
|
55
|
+
join_model.table_name_resolver = -> { table_name }
|
88
56
|
join_model.left_model = lhs_model
|
89
57
|
|
90
58
|
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
@@ -94,7 +62,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
94
62
|
|
95
63
|
def middle_reflection(join_model)
|
96
64
|
middle_name = [lhs_model.name.downcase.pluralize,
|
97
|
-
association_name].join(
|
65
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
98
66
|
middle_options = middle_options join_model
|
99
67
|
|
100
68
|
HasMany.create_reflection(lhs_model,
|
@@ -104,30 +72,41 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
104
72
|
end
|
105
73
|
|
106
74
|
private
|
75
|
+
def middle_options(join_model)
|
76
|
+
middle_options = {}
|
77
|
+
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
78
|
+
middle_options[:source] = join_model.left_reflection.name
|
79
|
+
if options.key? :foreign_key
|
80
|
+
middle_options[:foreign_key] = options[:foreign_key]
|
81
|
+
end
|
82
|
+
middle_options
|
83
|
+
end
|
107
84
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
85
|
+
def table_name
|
86
|
+
if options[:join_table]
|
87
|
+
options[:join_table].to_s
|
88
|
+
else
|
89
|
+
class_name = options.fetch(:class_name) {
|
90
|
+
association_name.to_s.camelize.singularize
|
91
|
+
}
|
92
|
+
klass = lhs_model.send(:compute_type, class_name.to_s)
|
93
|
+
[lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
94
|
+
end
|
114
95
|
end
|
115
|
-
middle_options
|
116
|
-
end
|
117
96
|
|
118
|
-
|
119
|
-
|
97
|
+
def belongs_to_options(options)
|
98
|
+
rhs_options = {}
|
120
99
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
100
|
+
if options.key? :class_name
|
101
|
+
rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
|
102
|
+
rhs_options[:class_name] = options[:class_name]
|
103
|
+
end
|
125
104
|
|
126
|
-
|
127
|
-
|
128
|
-
|
105
|
+
if options.key? :association_foreign_key
|
106
|
+
rhs_options[:foreign_key] = options[:association_foreign_key]
|
107
|
+
end
|
129
108
|
|
130
|
-
|
131
|
-
|
109
|
+
rhs_options
|
110
|
+
end
|
132
111
|
end
|
133
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
2
4
|
class HasMany < CollectionAssociation #:nodoc:
|
3
5
|
def self.macro
|
@@ -11,5 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
11
13
|
def self.valid_dependent_options
|
12
14
|
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
13
15
|
end
|
16
|
+
|
17
|
+
private_class_method :macro, :valid_options, :valid_dependent_options
|
14
18
|
end
|
15
19
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
2
4
|
class HasOne < SingularAssociation #:nodoc:
|
3
5
|
def self.macro
|
@@ -5,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.valid_options(options)
|
8
|
-
valid = super + [:as]
|
10
|
+
valid = super + [:as, :touch]
|
9
11
|
valid += [:through, :source, :source_type] if options[:through]
|
10
12
|
valid
|
11
13
|
end
|
@@ -14,6 +16,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
14
16
|
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
17
|
end
|
16
18
|
|
19
|
+
def self.define_callbacks(model, reflection)
|
20
|
+
super
|
21
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
22
|
+
end
|
23
|
+
|
17
24
|
def self.add_destroy_callbacks(model, reflection)
|
18
25
|
super unless reflection.options[:through]
|
19
26
|
end
|
@@ -24,5 +31,34 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
24
31
|
model.validates_presence_of reflection.name, message: :required
|
25
32
|
end
|
26
33
|
end
|
34
|
+
|
35
|
+
def self.touch_record(o, name, touch)
|
36
|
+
record = o.send name
|
37
|
+
|
38
|
+
return unless record && record.persisted?
|
39
|
+
|
40
|
+
if touch != true
|
41
|
+
record.touch(touch)
|
42
|
+
else
|
43
|
+
record.touch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.add_touch_callbacks(model, reflection)
|
48
|
+
name = reflection.name
|
49
|
+
touch = reflection.options[:touch]
|
50
|
+
|
51
|
+
callback = lambda { |record|
|
52
|
+
HasOne.touch_record(record, name, touch)
|
53
|
+
}
|
54
|
+
|
55
|
+
model.after_create callback, if: :saved_changes?
|
56
|
+
model.after_update callback, if: :saved_changes?
|
57
|
+
model.after_destroy callback
|
58
|
+
model.after_touch callback
|
59
|
+
end
|
60
|
+
|
61
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
|
62
|
+
:define_callbacks, :define_validations, :add_touch_callbacks
|
27
63
|
end
|
28
64
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This class is inherited by the has_one and belongs_to association classes
|
2
4
|
|
3
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
@@ -36,5 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
36
38
|
end
|
37
39
|
CODE
|
38
40
|
end
|
41
|
+
|
42
|
+
private_class_method :valid_options, :define_accessors, :define_constructors
|
39
43
|
end
|
40
44
|
end
|
@@ -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,18 +26,9 @@ 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
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
32
|
-
Passing an argument to force an association to reload is now
|
33
|
-
deprecated and will be removed in Rails 5.1. Please call `reload`
|
34
|
-
on the result collection proxy instead.
|
35
|
-
MSG
|
36
|
-
|
37
|
-
klass.uncached { reload }
|
38
|
-
elsif stale_target?
|
30
|
+
def reader
|
31
|
+
if stale_target?
|
39
32
|
reload
|
40
33
|
end
|
41
34
|
|
@@ -51,30 +44,29 @@ module ActiveRecord
|
|
51
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
52
45
|
def ids_reader
|
53
46
|
if loaded?
|
54
|
-
|
55
|
-
|
56
|
-
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
57
50
|
else
|
58
|
-
@association_ids ||= (
|
59
|
-
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
60
|
-
scope.pluck(column)
|
61
|
-
)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
62
52
|
end
|
63
53
|
end
|
64
54
|
|
65
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
66
56
|
def ids_writer(ids)
|
67
|
-
|
57
|
+
primary_key = reflection.association_primary_key
|
58
|
+
pk_type = klass.type_for_attribute(primary_key)
|
68
59
|
ids = Array(ids).reject(&:blank?)
|
69
60
|
ids.map! { |i| pk_type.cast(i) }
|
70
61
|
|
71
|
-
primary_key = reflection.association_primary_key
|
72
62
|
records = klass.where(primary_key => ids).index_by do |r|
|
73
63
|
r.public_send(primary_key)
|
74
64
|
end.values_at(*ids).compact
|
75
65
|
|
76
66
|
if records.size != ids.size
|
77
|
-
|
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)
|
78
70
|
else
|
79
71
|
replace(records)
|
80
72
|
end
|
@@ -83,80 +75,29 @@ module ActiveRecord
|
|
83
75
|
def reset
|
84
76
|
super
|
85
77
|
@target = []
|
86
|
-
|
87
|
-
|
88
|
-
def select(*fields)
|
89
|
-
if block_given?
|
90
|
-
load_target.select.each { |e| yield e }
|
91
|
-
else
|
92
|
-
scope.select(*fields)
|
93
|
-
end
|
78
|
+
@association_ids = nil
|
94
79
|
end
|
95
80
|
|
96
81
|
def find(*args)
|
97
|
-
if
|
98
|
-
|
99
|
-
|
100
|
-
if options[:inverse_of] && loaded?
|
101
|
-
args_flatten = args.flatten
|
102
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
103
|
-
result = find_by_scan(*args)
|
104
|
-
|
105
|
-
result_size = Array(result).size
|
106
|
-
if !result || result_size != args_flatten.size
|
107
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
108
|
-
else
|
109
|
-
result
|
110
|
-
end
|
111
|
-
else
|
112
|
-
scope.find(*args)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def first(*args)
|
118
|
-
first_nth_or_last(:first, *args)
|
119
|
-
end
|
120
|
-
|
121
|
-
def second(*args)
|
122
|
-
first_nth_or_last(:second, *args)
|
123
|
-
end
|
124
|
-
|
125
|
-
def third(*args)
|
126
|
-
first_nth_or_last(:third, *args)
|
127
|
-
end
|
128
|
-
|
129
|
-
def fourth(*args)
|
130
|
-
first_nth_or_last(:fourth, *args)
|
131
|
-
end
|
132
|
-
|
133
|
-
def fifth(*args)
|
134
|
-
first_nth_or_last(:fifth, *args)
|
135
|
-
end
|
136
|
-
|
137
|
-
def forty_two(*args)
|
138
|
-
first_nth_or_last(:forty_two, *args)
|
139
|
-
end
|
140
|
-
|
141
|
-
def third_to_last(*args)
|
142
|
-
first_nth_or_last(:third_to_last, *args)
|
143
|
-
end
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
144
85
|
|
145
|
-
|
146
|
-
|
147
|
-
|
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)
|
89
|
+
end
|
148
90
|
|
149
|
-
|
150
|
-
first_nth_or_last(:last, *args)
|
151
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
152
92
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
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
|
159
98
|
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
160
101
|
end
|
161
102
|
end
|
162
103
|
|
@@ -164,23 +105,12 @@ module ActiveRecord
|
|
164
105
|
if attributes.is_a?(Array)
|
165
106
|
attributes.collect { |attr| build(attr, &block) }
|
166
107
|
else
|
167
|
-
add_to_target(build_record(attributes))
|
168
|
-
yield(record) if block_given?
|
169
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
170
109
|
end
|
171
110
|
end
|
172
111
|
|
173
|
-
|
174
|
-
|
175
|
-
end
|
176
|
-
|
177
|
-
def create!(attributes = {}, &block)
|
178
|
-
_create_record(attributes, true, &block)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
182
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
183
|
-
# +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.
|
184
114
|
def concat(*records)
|
185
115
|
records = records.flatten
|
186
116
|
if owner.new_record?
|
@@ -225,12 +155,12 @@ module ActiveRecord
|
|
225
155
|
end
|
226
156
|
|
227
157
|
dependent = if dependent
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
234
164
|
|
235
165
|
delete_or_nullify_all_records(dependent).tap do
|
236
166
|
reset
|
@@ -248,28 +178,6 @@ module ActiveRecord
|
|
248
178
|
end
|
249
179
|
end
|
250
180
|
|
251
|
-
# Count all records using SQL. Construct options and pass them with
|
252
|
-
# scope to the target class's +count+.
|
253
|
-
def count(column_name = nil)
|
254
|
-
relation = scope
|
255
|
-
if association_scope.distinct_value
|
256
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
257
|
-
column_name ||= reflection.klass.primary_key
|
258
|
-
relation = relation.distinct
|
259
|
-
end
|
260
|
-
|
261
|
-
value = relation.count(column_name)
|
262
|
-
|
263
|
-
limit = options[:limit]
|
264
|
-
offset = options[:offset]
|
265
|
-
|
266
|
-
if limit || offset
|
267
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
268
|
-
else
|
269
|
-
value
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
181
|
# Removes +records+ from this association calling +before_remove+ and
|
274
182
|
# +after_remove+ callbacks.
|
275
183
|
#
|
@@ -278,12 +186,7 @@ module ActiveRecord
|
|
278
186
|
# are actually removed from the database, that depends precisely on
|
279
187
|
# +delete_records+. They are in any case removed from the collection.
|
280
188
|
def delete(*records)
|
281
|
-
|
282
|
-
_options = records.extract_options!
|
283
|
-
dependent = _options[:dependent] || options[:dependent]
|
284
|
-
|
285
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
286
|
-
delete_or_destroy(records, dependent)
|
189
|
+
delete_or_destroy(records, options[:dependent])
|
287
190
|
end
|
288
191
|
|
289
192
|
# Deletes the +records+ and removes them from this association calling
|
@@ -292,8 +195,6 @@ module ActiveRecord
|
|
292
195
|
# Note that this method removes records from the database ignoring the
|
293
196
|
# +:dependent+ option.
|
294
197
|
def destroy(*records)
|
295
|
-
return if records.empty?
|
296
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
297
198
|
delete_or_destroy(records, :destroy)
|
298
199
|
end
|
299
200
|
|
@@ -309,14 +210,12 @@ module ActiveRecord
|
|
309
210
|
# +count_records+, which is a method descendants have to provide.
|
310
211
|
def size
|
311
212
|
if !find_target? || loaded?
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
target.size
|
316
|
-
end
|
213
|
+
target.size
|
214
|
+
elsif @association_ids
|
215
|
+
@association_ids.size
|
317
216
|
elsif !association_scope.group_values.empty?
|
318
217
|
load_target.size
|
319
|
-
elsif !association_scope.distinct_value && target.
|
218
|
+
elsif !association_scope.distinct_value && !target.empty?
|
320
219
|
unsaved_records = target.select(&:new_record?)
|
321
220
|
unsaved_records.size + count_records
|
322
221
|
else
|
@@ -324,15 +223,6 @@ module ActiveRecord
|
|
324
223
|
end
|
325
224
|
end
|
326
225
|
|
327
|
-
# Returns the size of the collection calling +size+ on the target.
|
328
|
-
#
|
329
|
-
# If the collection has been already loaded +length+ and +size+ are
|
330
|
-
# equivalent. If not and you are going to need the records anyway this
|
331
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
332
|
-
def length
|
333
|
-
load_target.size
|
334
|
-
end
|
335
|
-
|
336
226
|
# Returns true if the collection is empty.
|
337
227
|
#
|
338
228
|
# If the collection has been loaded
|
@@ -342,42 +232,12 @@ module ActiveRecord
|
|
342
232
|
# loaded and you are going to fetch the records anyway it is better to
|
343
233
|
# check <tt>collection.length.zero?</tt>.
|
344
234
|
def empty?
|
345
|
-
if loaded?
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
346
236
|
size.zero?
|
347
237
|
else
|
348
|
-
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
# Returns true if the collections is not empty.
|
353
|
-
# If block given, loads all records and checks for one or more matches.
|
354
|
-
# Otherwise, equivalent to +!collection.empty?+.
|
355
|
-
def any?
|
356
|
-
if block_given?
|
357
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
358
|
-
else
|
359
|
-
!empty?
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
# Returns true if the collection has more than 1 record.
|
364
|
-
# If block given, loads all records and checks for two or more matches.
|
365
|
-
# Otherwise, equivalent to +collection.size > 1+.
|
366
|
-
def many?
|
367
|
-
if block_given?
|
368
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
369
|
-
else
|
370
|
-
size > 1
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
def distinct
|
375
|
-
seen = {}
|
376
|
-
load_target.find_all do |record|
|
377
|
-
seen[record.id] = true unless seen.key?(record.id)
|
238
|
+
target.empty? && !scope.exists?
|
378
239
|
end
|
379
240
|
end
|
380
|
-
alias uniq distinct
|
381
241
|
|
382
242
|
# Replace this collection with +other_array+. This will perform a diff
|
383
243
|
# and delete/add only records that have changed.
|
@@ -435,28 +295,13 @@ module ActiveRecord
|
|
435
295
|
owner.new_record? && !foreign_key_present?
|
436
296
|
end
|
437
297
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
conn = klass.connection
|
443
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
444
|
-
StatementCache.create(conn) { |params|
|
445
|
-
as = AssociationScope.create { params.bind }
|
446
|
-
target_scope.merge as.scope(self, conn)
|
447
|
-
}
|
448
|
-
end
|
449
|
-
|
450
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
451
|
-
sc.execute(binds, klass, klass.connection, &block)
|
298
|
+
def find_from_target?
|
299
|
+
loaded? ||
|
300
|
+
owner.new_record? ||
|
301
|
+
target.any? { |record| record.new_record? || record.changed? }
|
452
302
|
end
|
453
303
|
|
454
|
-
|
455
|
-
get_records do |record|
|
456
|
-
set_inverse_instance(record)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
304
|
+
private
|
460
305
|
# We have some records loaded from the database (persisted) and some that are
|
461
306
|
# in-memory (memory). The same record may be represented in the persisted array
|
462
307
|
# and in the memory array.
|
@@ -474,7 +319,7 @@ module ActiveRecord
|
|
474
319
|
persisted.map! do |record|
|
475
320
|
if mem_record = memory.delete(record)
|
476
321
|
|
477
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.
|
322
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
478
323
|
mem_record[name] = record[name]
|
479
324
|
end
|
480
325
|
|
@@ -495,12 +340,17 @@ module ActiveRecord
|
|
495
340
|
if attributes.is_a?(Array)
|
496
341
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
497
342
|
else
|
343
|
+
record = build_record(attributes, &block)
|
498
344
|
transaction do
|
499
|
-
|
500
|
-
|
501
|
-
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
|
+
}
|
502
350
|
end
|
351
|
+
raise ActiveRecord::Rollback unless result
|
503
352
|
end
|
353
|
+
record
|
504
354
|
end
|
505
355
|
end
|
506
356
|
|
@@ -513,11 +363,9 @@ module ActiveRecord
|
|
513
363
|
end
|
514
364
|
end
|
515
365
|
|
516
|
-
def create_scope
|
517
|
-
scope.scope_for_create.stringify_keys
|
518
|
-
end
|
519
|
-
|
520
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) }
|
521
369
|
records = records.flatten
|
522
370
|
records.each { |record| raise_on_type_mismatch!(record) }
|
523
371
|
existing_records = records.reject(&:new_record?)
|
@@ -530,24 +378,28 @@ module ActiveRecord
|
|
530
378
|
end
|
531
379
|
|
532
380
|
def remove_records(existing_records, records, method)
|
533
|
-
|
381
|
+
catch(:abort) do
|
382
|
+
records.each { |record| callback(:before_remove, record) }
|
383
|
+
end || return
|
534
384
|
|
535
385
|
delete_records(existing_records, method) if existing_records.any?
|
536
|
-
|
386
|
+
@target -= records
|
387
|
+
@association_ids = nil
|
537
388
|
|
538
389
|
records.each { |record| callback(:after_remove, record) }
|
539
390
|
end
|
540
391
|
|
541
|
-
# Delete the given records from the association,
|
542
|
-
#
|
392
|
+
# Delete the given records from the association,
|
393
|
+
# using one of the methods +:destroy+, +:delete_all+
|
394
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
543
395
|
def delete_records(records, method)
|
544
396
|
raise NotImplementedError
|
545
397
|
end
|
546
398
|
|
547
399
|
def replace_records(new_target, original_target)
|
548
|
-
delete(target
|
400
|
+
delete(difference(target, new_target))
|
549
401
|
|
550
|
-
unless concat(new_target
|
402
|
+
unless concat(difference(new_target, target))
|
551
403
|
@target = original_target
|
552
404
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
553
405
|
"new records could not be saved."
|
@@ -557,7 +409,7 @@ module ActiveRecord
|
|
557
409
|
end
|
558
410
|
|
559
411
|
def replace_common_records_in_memory(new_target, original_target)
|
560
|
-
common_records = new_target
|
412
|
+
common_records = intersection(new_target, original_target)
|
561
413
|
common_records.each do |record|
|
562
414
|
skip_callbacks = true
|
563
415
|
replace_on_target(record, @target.index(record), skip_callbacks)
|
@@ -570,15 +422,23 @@ module ActiveRecord
|
|
570
422
|
records.each do |record|
|
571
423
|
raise_on_type_mismatch!(record)
|
572
424
|
add_to_target(record) do
|
573
|
-
|
425
|
+
unless owner.new_record?
|
426
|
+
result &&= insert_record(record, true, raise) {
|
427
|
+
@_was_loaded = loaded?
|
428
|
+
}
|
429
|
+
end
|
574
430
|
end
|
575
431
|
end
|
576
432
|
|
577
|
-
|
433
|
+
raise ActiveRecord::Rollback unless result
|
434
|
+
|
435
|
+
records
|
578
436
|
end
|
579
437
|
|
580
438
|
def replace_on_target(record, index, skip_callbacks)
|
581
|
-
|
439
|
+
catch(:abort) do
|
440
|
+
callback(:before_add, record)
|
441
|
+
end || return unless skip_callbacks
|
582
442
|
|
583
443
|
set_inverse_instance(record)
|
584
444
|
|
@@ -589,6 +449,7 @@ module ActiveRecord
|
|
589
449
|
if index
|
590
450
|
target[index] = record
|
591
451
|
elsif @_was_loaded || !loaded?
|
452
|
+
@association_ids = nil
|
592
453
|
target << record
|
593
454
|
end
|
594
455
|
|
@@ -610,25 +471,6 @@ module ActiveRecord
|
|
610
471
|
owner.class.send(full_callback_name)
|
611
472
|
end
|
612
473
|
|
613
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
614
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
615
|
-
#
|
616
|
-
# If the args is just a non-empty options hash, go to the database.
|
617
|
-
#
|
618
|
-
# Otherwise, go to the database only if none of the following are true:
|
619
|
-
# * target already loaded
|
620
|
-
# * owner is new record
|
621
|
-
# * target contains new or changed record(s)
|
622
|
-
def fetch_first_nth_or_last_using_find?(args)
|
623
|
-
if args.first.is_a?(Hash)
|
624
|
-
true
|
625
|
-
else
|
626
|
-
!(loaded? ||
|
627
|
-
owner.new_record? ||
|
628
|
-
target.any? { |record| record.new_record? || record.changed? })
|
629
|
-
end
|
630
|
-
end
|
631
|
-
|
632
474
|
def include_in_memory?(record)
|
633
475
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
634
476
|
assoc = owner.association(reflection.through_reflection.name)
|
@@ -655,16 +497,6 @@ module ActiveRecord
|
|
655
497
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
656
498
|
end
|
657
499
|
end
|
658
|
-
|
659
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
660
|
-
def first_nth_or_last(type, *args)
|
661
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
662
|
-
|
663
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
664
|
-
collection.send(type, *args).tap do |record|
|
665
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
666
|
-
end
|
667
|
-
end
|
668
500
|
end
|
669
501
|
end
|
670
502
|
end
|