activerecord 4.2.8 → 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 +5 -5
- data/CHANGELOG.md +612 -1583
- data/MIT-LICENSE +4 -2
- data/README.rdoc +13 -12
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record.rb +41 -22
- data/lib/active_record/aggregations.rb +267 -251
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations.rb +1737 -1597
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +125 -58
- data/lib/active_record/associations/association_scope.rb +103 -132
- data/lib/active_record/associations/belongs_to_association.rb +65 -60
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +27 -40
- data/lib/active_record/associations/builder/belongs_to.rb +69 -55
- data/lib/active_record/associations/builder/collection_association.rb +10 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
- data/lib/active_record/associations/builder/has_many.rb +8 -4
- data/lib/active_record/associations/builder/has_one.rb +46 -5
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +134 -286
- data/lib/active_record/associations/collection_proxy.rb +241 -146
- data/lib/active_record/associations/foreign_association.rb +10 -1
- data/lib/active_record/associations/has_many_association.rb +34 -97
- data/lib/active_record/associations/has_many_through_association.rb +60 -87
- data/lib/active_record/associations/has_one_association.rb +61 -49
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +137 -167
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -88
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/preloader.rb +90 -92
- data/lib/active_record/associations/preloader/association.rb +90 -123
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/attribute_assignment.rb +56 -183
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods.rb +120 -135
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
- data/lib/active_record/attribute_methods/dirty.rb +174 -144
- data/lib/active_record/attribute_methods/primary_key.rb +91 -83
- data/lib/active_record/attribute_methods/query.rb +6 -5
- data/lib/active_record/attribute_methods/read.rb +20 -76
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +32 -54
- data/lib/active_record/attributes.rb +214 -82
- data/lib/active_record/autosave_association.rb +91 -37
- data/lib/active_record/base.rb +57 -45
- data/lib/active_record/callbacks.rb +100 -74
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +796 -296
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -115
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -244
- data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
- data/lib/active_record/connection_adapters/abstract_adapter.rb +460 -204
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +510 -627
- data/lib/active_record/connection_adapters/column.rb +56 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
- 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 +58 -188
- data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- 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 +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +551 -356
- data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
- 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 +290 -345
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +176 -41
- data/lib/active_record/core.rb +251 -231
- data/lib/active_record/counter_cache.rb +67 -49
- 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 +79 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +163 -86
- data/lib/active_record/errors.rb +188 -53
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- 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 +228 -499
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +158 -112
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +123 -29
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +87 -96
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +76 -33
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +626 -283
- data/lib/active_record/migration/command_recorder.rb +177 -90
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/model_schema.rb +314 -112
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +14 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +557 -125
- data/lib/active_record/query_cache.rb +19 -23
- data/lib/active_record/querying.rb +43 -29
- data/lib/active_record/railtie.rb +147 -46
- 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 +330 -197
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +428 -279
- data/lib/active_record/relation.rb +518 -341
- data/lib/active_record/relation/batches.rb +207 -55
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/calculations.rb +267 -253
- data/lib/active_record/relation/delegation.rb +70 -80
- data/lib/active_record/relation/finder_methods.rb +277 -241
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +78 -87
- data/lib/active_record/relation/predicate_builder.rb +114 -119
- data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
- 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 +7 -1
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +575 -394
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- 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/result.rb +79 -42
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +144 -121
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +112 -93
- data/lib/active_record/schema_migration.rb +24 -17
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/scoping/default.rb +101 -85
- data/lib/active_record/scoping/named.rb +86 -33
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +73 -36
- data/lib/active_record/store.rb +127 -42
- 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 +308 -99
- data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
- data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +86 -40
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +216 -150
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +78 -23
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- 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 +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type_caster.rb +9 -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/validations.rb +39 -35
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +42 -55
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +51 -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 +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.rb +20 -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/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 +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration.rb +31 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
- 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 → migration.rb.tt} +11 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +19 -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} +0 -0
- metadata +164 -60
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -58
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,28 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_record/associations"
|
4
4
|
|
5
|
-
module ActiveRecord::Associations::Builder
|
5
|
+
module ActiveRecord::Associations::Builder # :nodoc:
|
6
6
|
class CollectionAssociation < Association #:nodoc:
|
7
|
-
|
8
7
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
9
8
|
|
10
|
-
def valid_options
|
9
|
+
def self.valid_options(options)
|
11
10
|
super + [:table_name, :before_add,
|
12
11
|
:after_add, :before_remove, :after_remove, :extend]
|
13
12
|
end
|
14
13
|
|
15
|
-
attr_reader :block_extension
|
16
|
-
|
17
|
-
def initialize(model, name, scope, options)
|
18
|
-
super
|
19
|
-
@mod = nil
|
20
|
-
if block_given?
|
21
|
-
@mod = Module.new(&Proc.new)
|
22
|
-
@scope = wrap_scope @scope, @mod
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
14
|
def self.define_callbacks(model, reflection)
|
27
15
|
super
|
28
16
|
name = reflection.name
|
@@ -32,10 +20,11 @@ module ActiveRecord::Associations::Builder
|
|
32
20
|
}
|
33
21
|
end
|
34
22
|
|
35
|
-
def define_extensions(model)
|
36
|
-
if
|
37
|
-
extension_module_name = "#{
|
38
|
-
|
23
|
+
def self.define_extensions(model, name, &block)
|
24
|
+
if block_given?
|
25
|
+
extension_module_name = "#{name.to_s.camelize}AssociationExtension"
|
26
|
+
extension = Module.new(&block)
|
27
|
+
model.const_set(extension_module_name, extension)
|
39
28
|
end
|
40
29
|
end
|
41
30
|
|
@@ -78,18 +67,6 @@ module ActiveRecord::Associations::Builder
|
|
78
67
|
CODE
|
79
68
|
end
|
80
69
|
|
81
|
-
|
82
|
-
|
83
|
-
def wrap_scope(scope, mod)
|
84
|
-
if scope
|
85
|
-
if scope.arity > 0
|
86
|
-
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
87
|
-
else
|
88
|
-
proc { instance_exec(&scope).extending(mod) }
|
89
|
-
end
|
90
|
-
else
|
91
|
-
proc { extending(mod) }
|
92
|
-
end
|
93
|
-
end
|
70
|
+
private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
|
94
71
|
end
|
95
72
|
end
|
@@ -1,38 +1,7 @@
|
|
1
|
-
|
2
|
-
class HasAndBelongsToMany # :nodoc:
|
3
|
-
class JoinTableResolver
|
4
|
-
KnownTable = Struct.new :join_table
|
5
|
-
|
6
|
-
class KnownClass
|
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
|
1
|
+
# frozen_string_literal: true
|
35
2
|
|
3
|
+
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
+
class HasAndBelongsToMany # :nodoc:
|
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
|
|
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
|
|
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)
|
@@ -62,13 +31,13 @@ module ActiveRecord::Associations::Builder
|
|
62
31
|
end
|
63
32
|
|
64
33
|
def self.add_left_association(name, options)
|
65
|
-
belongs_to name, options
|
34
|
+
belongs_to name, required: false, **options
|
66
35
|
self.left_reflection = _reflect_on_association(name)
|
67
36
|
end
|
68
37
|
|
69
38
|
def self.add_right_association(name, options)
|
70
39
|
rhs_name = name.to_s.singularize.to_sym
|
71
|
-
belongs_to rhs_name, options
|
40
|
+
belongs_to rhs_name, required: false, **options
|
72
41
|
self.right_reflection = _reflect_on_association(rhs_name)
|
73
42
|
end
|
74
43
|
|
@@ -76,10 +45,15 @@ module ActiveRecord::Associations::Builder
|
|
76
45
|
left_model.retrieve_connection
|
77
46
|
end
|
78
47
|
|
48
|
+
private
|
49
|
+
|
50
|
+
def self.suppress_composite_primary_key(pk)
|
51
|
+
pk unless pk.is_a?(Array)
|
52
|
+
end
|
79
53
|
}
|
80
54
|
|
81
55
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
82
|
-
join_model.table_name_resolver =
|
56
|
+
join_model.table_name_resolver = -> { table_name }
|
83
57
|
join_model.left_model = lhs_model
|
84
58
|
|
85
59
|
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
@@ -89,40 +63,52 @@ module ActiveRecord::Associations::Builder
|
|
89
63
|
|
90
64
|
def middle_reflection(join_model)
|
91
65
|
middle_name = [lhs_model.name.downcase.pluralize,
|
92
|
-
association_name].join(
|
66
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
93
67
|
middle_options = middle_options join_model
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
68
|
+
|
69
|
+
HasMany.create_reflection(lhs_model,
|
70
|
+
middle_name,
|
71
|
+
nil,
|
72
|
+
middle_options)
|
99
73
|
end
|
100
74
|
|
101
75
|
private
|
102
76
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
77
|
+
def middle_options(join_model)
|
78
|
+
middle_options = {}
|
79
|
+
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
80
|
+
middle_options[:source] = join_model.left_reflection.name
|
81
|
+
if options.key? :foreign_key
|
82
|
+
middle_options[:foreign_key] = options[:foreign_key]
|
83
|
+
end
|
84
|
+
middle_options
|
109
85
|
end
|
110
|
-
middle_options
|
111
|
-
end
|
112
86
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
87
|
+
def table_name
|
88
|
+
if options[:join_table]
|
89
|
+
options[:join_table].to_s
|
90
|
+
else
|
91
|
+
class_name = options.fetch(:class_name) {
|
92
|
+
association_name.to_s.camelize.singularize
|
93
|
+
}
|
94
|
+
klass = lhs_model.send(:compute_type, class_name.to_s)
|
95
|
+
[lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
96
|
+
end
|
119
97
|
end
|
120
98
|
|
121
|
-
|
122
|
-
rhs_options
|
123
|
-
end
|
99
|
+
def belongs_to_options(options)
|
100
|
+
rhs_options = {}
|
124
101
|
|
125
|
-
|
126
|
-
|
102
|
+
if options.key? :class_name
|
103
|
+
rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
|
104
|
+
rhs_options[:class_name] = options[:class_name]
|
105
|
+
end
|
106
|
+
|
107
|
+
if options.key? :association_foreign_key
|
108
|
+
rhs_options[:foreign_key] = options[:association_foreign_key]
|
109
|
+
end
|
110
|
+
|
111
|
+
rhs_options
|
112
|
+
end
|
127
113
|
end
|
128
114
|
end
|
@@ -1,15 +1,19 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::Associations::Builder # :nodoc:
|
2
4
|
class HasMany < CollectionAssociation #:nodoc:
|
3
|
-
def macro
|
5
|
+
def self.macro
|
4
6
|
:has_many
|
5
7
|
end
|
6
8
|
|
7
|
-
def valid_options
|
8
|
-
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
|
9
|
+
def self.valid_options(options)
|
10
|
+
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
|
9
11
|
end
|
10
12
|
|
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,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord::Associations::Builder # :nodoc:
|
2
4
|
class HasOne < SingularAssociation #:nodoc:
|
3
|
-
def macro
|
5
|
+
def self.macro
|
4
6
|
:has_one
|
5
7
|
end
|
6
8
|
|
7
|
-
def valid_options
|
8
|
-
valid = super + [:as, :
|
9
|
+
def self.valid_options(options)
|
10
|
+
valid = super + [:as, :touch]
|
9
11
|
valid += [:through, :source, :source_type] if options[:through]
|
10
12
|
valid
|
11
13
|
end
|
@@ -14,10 +16,49 @@ module ActiveRecord::Associations::Builder
|
|
14
16
|
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
def self.define_callbacks(model, reflection)
|
20
|
+
super
|
21
|
+
add_touch_callbacks(model, reflection) if reflection.options[:touch]
|
22
|
+
end
|
18
23
|
|
19
24
|
def self.add_destroy_callbacks(model, reflection)
|
20
25
|
super unless reflection.options[:through]
|
21
26
|
end
|
27
|
+
|
28
|
+
def self.define_validations(model, reflection)
|
29
|
+
super
|
30
|
+
if reflection.options[:required]
|
31
|
+
model.validates_presence_of reflection.name, message: :required
|
32
|
+
end
|
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
|
22
63
|
end
|
23
64
|
end
|
@@ -1,14 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This class is inherited by the has_one and belongs_to association classes
|
2
4
|
|
3
|
-
module ActiveRecord::Associations::Builder
|
5
|
+
module ActiveRecord::Associations::Builder # :nodoc:
|
4
6
|
class SingularAssociation < Association #:nodoc:
|
5
|
-
def valid_options
|
6
|
-
super + [:dependent, :primary_key, :inverse_of, :required]
|
7
|
+
def self.valid_options(options)
|
8
|
+
super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
|
7
9
|
end
|
8
10
|
|
9
11
|
def self.define_accessors(model, reflection)
|
10
12
|
super
|
11
|
-
|
13
|
+
mixin = model.generated_association_methods
|
14
|
+
name = reflection.name
|
15
|
+
|
16
|
+
define_constructors(mixin, name) if reflection.constructable?
|
17
|
+
|
18
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
19
|
+
def reload_#{name}
|
20
|
+
association(:#{name}).force_reload_reader
|
21
|
+
end
|
22
|
+
CODE
|
12
23
|
end
|
13
24
|
|
14
25
|
# Defines the (build|create)_association methods for belongs_to or has_one association
|
@@ -28,11 +39,6 @@ module ActiveRecord::Associations::Builder
|
|
28
39
|
CODE
|
29
40
|
end
|
30
41
|
|
31
|
-
|
32
|
-
super
|
33
|
-
if reflection.options[:required]
|
34
|
-
model.validates_presence_of reflection.name
|
35
|
-
end
|
36
|
-
end
|
42
|
+
private_class_method :valid_options, :define_accessors, :define_constructors
|
37
43
|
end
|
38
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,22 +26,14 @@ module ActiveRecord
|
|
24
26
|
# If you need to work on all current children, new and existing records,
|
25
27
|
# +load_target+ and the +loaded+ flag are your friends.
|
26
28
|
class CollectionAssociation < Association #:nodoc:
|
27
|
-
|
28
29
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
|
-
def reader
|
30
|
-
if
|
31
|
-
klass.uncached { reload }
|
32
|
-
elsif stale_target?
|
30
|
+
def reader
|
31
|
+
if stale_target?
|
33
32
|
reload
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
43
37
|
end
|
44
38
|
|
45
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -50,99 +44,60 @@ module ActiveRecord
|
|
50
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
51
45
|
def ids_reader
|
52
46
|
if loaded?
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
56
50
|
else
|
57
|
-
|
58
|
-
scope.pluck(column)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
62
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
56
|
def ids_writer(ids)
|
64
|
-
|
65
|
-
pk_type = klass.type_for_attribute(
|
66
|
-
ids = Array(ids).reject(&:blank?)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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)
|
72
70
|
else
|
73
|
-
|
71
|
+
replace(records)
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
75
|
def reset
|
78
76
|
super
|
79
77
|
@target = []
|
80
|
-
|
81
|
-
|
82
|
-
def select(*fields)
|
83
|
-
if block_given?
|
84
|
-
load_target.select.each { |e| yield e }
|
85
|
-
else
|
86
|
-
scope.select(*fields)
|
87
|
-
end
|
78
|
+
@association_ids = nil
|
88
79
|
end
|
89
80
|
|
90
81
|
def find(*args)
|
91
|
-
if
|
92
|
-
|
93
|
-
|
94
|
-
if options[:inverse_of] && loaded?
|
95
|
-
args_flatten = args.flatten
|
96
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
97
|
-
result = find_by_scan(*args)
|
98
|
-
|
99
|
-
result_size = Array(result).size
|
100
|
-
if !result || result_size != args_flatten.size
|
101
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
102
|
-
else
|
103
|
-
result
|
104
|
-
end
|
105
|
-
else
|
106
|
-
scope.find(*args)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def first(*args)
|
112
|
-
first_nth_or_last(:first, *args)
|
113
|
-
end
|
114
|
-
|
115
|
-
def second(*args)
|
116
|
-
first_nth_or_last(:second, *args)
|
117
|
-
end
|
118
|
-
|
119
|
-
def third(*args)
|
120
|
-
first_nth_or_last(:third, *args)
|
121
|
-
end
|
122
|
-
|
123
|
-
def fourth(*args)
|
124
|
-
first_nth_or_last(:fourth, *args)
|
125
|
-
end
|
126
|
-
|
127
|
-
def fifth(*args)
|
128
|
-
first_nth_or_last(:fifth, *args)
|
129
|
-
end
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
130
85
|
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
134
90
|
|
135
|
-
|
136
|
-
first_nth_or_last(:last, *args)
|
137
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
138
92
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
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
|
145
98
|
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
146
101
|
end
|
147
102
|
end
|
148
103
|
|
@@ -150,24 +105,14 @@ module ActiveRecord
|
|
150
105
|
if attributes.is_a?(Array)
|
151
106
|
attributes.collect { |attr| build(attr, &block) }
|
152
107
|
else
|
153
|
-
add_to_target(build_record(attributes))
|
154
|
-
yield(record) if block_given?
|
155
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
156
109
|
end
|
157
110
|
end
|
158
111
|
|
159
|
-
|
160
|
-
|
161
|
-
end
|
162
|
-
|
163
|
-
def create!(attributes = {}, &block)
|
164
|
-
_create_record(attributes, true, &block)
|
165
|
-
end
|
166
|
-
|
167
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
168
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
169
|
-
# +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.
|
170
114
|
def concat(*records)
|
115
|
+
records = records.flatten
|
171
116
|
if owner.new_record?
|
172
117
|
load_target
|
173
118
|
concat_records(records)
|
@@ -210,12 +155,12 @@ module ActiveRecord
|
|
210
155
|
end
|
211
156
|
|
212
157
|
dependent = if dependent
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
219
164
|
|
220
165
|
delete_or_nullify_all_records(dependent).tap do
|
221
166
|
reset
|
@@ -233,32 +178,6 @@ module ActiveRecord
|
|
233
178
|
end
|
234
179
|
end
|
235
180
|
|
236
|
-
# Count all records using SQL. Construct options and pass them with
|
237
|
-
# scope to the target class's +count+.
|
238
|
-
def count(column_name = nil, count_options = {})
|
239
|
-
# TODO: Remove count_options argument as soon we remove support to
|
240
|
-
# activerecord-deprecated_finders.
|
241
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
242
|
-
|
243
|
-
relation = scope
|
244
|
-
if association_scope.distinct_value
|
245
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
246
|
-
column_name ||= reflection.klass.primary_key
|
247
|
-
relation = relation.distinct
|
248
|
-
end
|
249
|
-
|
250
|
-
value = relation.count(column_name)
|
251
|
-
|
252
|
-
limit = options[:limit]
|
253
|
-
offset = options[:offset]
|
254
|
-
|
255
|
-
if limit || offset
|
256
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
257
|
-
else
|
258
|
-
value
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
181
|
# Removes +records+ from this association calling +before_remove+ and
|
263
182
|
# +after_remove+ callbacks.
|
264
183
|
#
|
@@ -267,12 +186,7 @@ module ActiveRecord
|
|
267
186
|
# are actually removed from the database, that depends precisely on
|
268
187
|
# +delete_records+. They are in any case removed from the collection.
|
269
188
|
def delete(*records)
|
270
|
-
|
271
|
-
_options = records.extract_options!
|
272
|
-
dependent = _options[:dependent] || options[:dependent]
|
273
|
-
|
274
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
275
|
-
delete_or_destroy(records, dependent)
|
189
|
+
delete_or_destroy(records, options[:dependent])
|
276
190
|
end
|
277
191
|
|
278
192
|
# Deletes the +records+ and removes them from this association calling
|
@@ -281,8 +195,6 @@ module ActiveRecord
|
|
281
195
|
# Note that this method removes records from the database ignoring the
|
282
196
|
# +:dependent+ option.
|
283
197
|
def destroy(*records)
|
284
|
-
return if records.empty?
|
285
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
286
198
|
delete_or_destroy(records, :destroy)
|
287
199
|
end
|
288
200
|
|
@@ -298,30 +210,19 @@ module ActiveRecord
|
|
298
210
|
# +count_records+, which is a method descendants have to provide.
|
299
211
|
def size
|
300
212
|
if !find_target? || loaded?
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
end
|
306
|
-
elsif !loaded? && !association_scope.group_values.empty?
|
213
|
+
target.size
|
214
|
+
elsif @association_ids
|
215
|
+
@association_ids.size
|
216
|
+
elsif !association_scope.group_values.empty?
|
307
217
|
load_target.size
|
308
|
-
elsif !
|
309
|
-
unsaved_records = target.select
|
218
|
+
elsif !association_scope.distinct_value && !target.empty?
|
219
|
+
unsaved_records = target.select(&:new_record?)
|
310
220
|
unsaved_records.size + count_records
|
311
221
|
else
|
312
222
|
count_records
|
313
223
|
end
|
314
224
|
end
|
315
225
|
|
316
|
-
# Returns the size of the collection calling +size+ on the target.
|
317
|
-
#
|
318
|
-
# If the collection has been already loaded +length+ and +size+ are
|
319
|
-
# equivalent. If not and you are going to need the records anyway this
|
320
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
321
|
-
def length
|
322
|
-
load_target.size
|
323
|
-
end
|
324
|
-
|
325
226
|
# Returns true if the collection is empty.
|
326
227
|
#
|
327
228
|
# If the collection has been loaded
|
@@ -331,41 +232,13 @@ module ActiveRecord
|
|
331
232
|
# loaded and you are going to fetch the records anyway it is better to
|
332
233
|
# check <tt>collection.length.zero?</tt>.
|
333
234
|
def empty?
|
334
|
-
if loaded?
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
335
236
|
size.zero?
|
336
237
|
else
|
337
|
-
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
# Returns true if the collections is not empty.
|
342
|
-
# Equivalent to +!collection.empty?+.
|
343
|
-
def any?
|
344
|
-
if block_given?
|
345
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
346
|
-
else
|
347
|
-
!empty?
|
238
|
+
target.empty? && !scope.exists?
|
348
239
|
end
|
349
240
|
end
|
350
241
|
|
351
|
-
# Returns true if the collection has more than 1 record.
|
352
|
-
# Equivalent to +collection.size > 1+.
|
353
|
-
def many?
|
354
|
-
if block_given?
|
355
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
356
|
-
else
|
357
|
-
size > 1
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
def distinct
|
362
|
-
seen = {}
|
363
|
-
load_target.find_all do |record|
|
364
|
-
seen[record.id] = true unless seen.key?(record.id)
|
365
|
-
end
|
366
|
-
end
|
367
|
-
alias uniq distinct
|
368
|
-
|
369
242
|
# Replace this collection with +other_array+. This will perform a diff
|
370
243
|
# and delete/add only records that have changed.
|
371
244
|
def replace(other_array)
|
@@ -378,6 +251,8 @@ module ActiveRecord
|
|
378
251
|
replace_common_records_in_memory(other_array, original_target)
|
379
252
|
if other_array != original_target
|
380
253
|
transaction { replace_records(other_array, original_target) }
|
254
|
+
else
|
255
|
+
other_array
|
381
256
|
end
|
382
257
|
end
|
383
258
|
end
|
@@ -410,25 +285,9 @@ module ActiveRecord
|
|
410
285
|
replace_on_target(record, index, skip_callbacks, &block)
|
411
286
|
end
|
412
287
|
|
413
|
-
def
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
if index
|
418
|
-
@target[index] = record
|
419
|
-
else
|
420
|
-
@target << record
|
421
|
-
end
|
422
|
-
|
423
|
-
callback(:after_add, record) unless skip_callbacks
|
424
|
-
set_inverse_instance(record)
|
425
|
-
|
426
|
-
record
|
427
|
-
end
|
428
|
-
|
429
|
-
def scope(opts = {})
|
430
|
-
scope = super()
|
431
|
-
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
288
|
+
def scope
|
289
|
+
scope = super
|
290
|
+
scope.none! if null_scope?
|
432
291
|
scope
|
433
292
|
end
|
434
293
|
|
@@ -436,28 +295,13 @@ module ActiveRecord
|
|
436
295
|
owner.new_record? && !foreign_key_present?
|
437
296
|
end
|
438
297
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
conn = klass.connection
|
444
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
445
|
-
StatementCache.create(conn) { |params|
|
446
|
-
as = AssociationScope.create { params.bind }
|
447
|
-
target_scope.merge as.scope(self, conn)
|
448
|
-
}
|
449
|
-
end
|
450
|
-
|
451
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
452
|
-
sc.execute binds, klass, klass.connection
|
298
|
+
def find_from_target?
|
299
|
+
loaded? ||
|
300
|
+
owner.new_record? ||
|
301
|
+
target.any? { |record| record.new_record? || record.changed? }
|
453
302
|
end
|
454
303
|
|
455
|
-
|
456
|
-
records = get_records
|
457
|
-
records.each { |record| set_inverse_instance(record) }
|
458
|
-
records
|
459
|
-
end
|
460
|
-
|
304
|
+
private
|
461
305
|
# We have some records loaded from the database (persisted) and some that are
|
462
306
|
# in-memory (memory). The same record may be represented in the persisted array
|
463
307
|
# and in the memory array.
|
@@ -475,7 +319,7 @@ module ActiveRecord
|
|
475
319
|
persisted.map! do |record|
|
476
320
|
if mem_record = memory.delete(record)
|
477
321
|
|
478
|
-
((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|
|
479
323
|
mem_record[name] = record[name]
|
480
324
|
end
|
481
325
|
|
@@ -496,28 +340,35 @@ module ActiveRecord
|
|
496
340
|
if attributes.is_a?(Array)
|
497
341
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
498
342
|
else
|
343
|
+
record = build_record(attributes, &block)
|
499
344
|
transaction do
|
500
|
-
|
501
|
-
|
502
|
-
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
|
+
}
|
503
350
|
end
|
351
|
+
raise ActiveRecord::Rollback unless result
|
504
352
|
end
|
353
|
+
record
|
505
354
|
end
|
506
355
|
end
|
507
356
|
|
508
357
|
# Do the relevant stuff to insert the given record into the association collection.
|
509
|
-
def insert_record(record, validate = true, raise = false)
|
510
|
-
raise
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
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
|
515
364
|
end
|
516
365
|
|
517
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) }
|
518
369
|
records = records.flatten
|
519
370
|
records.each { |record| raise_on_type_mismatch!(record) }
|
520
|
-
existing_records = records.reject
|
371
|
+
existing_records = records.reject(&:new_record?)
|
521
372
|
|
522
373
|
if existing_records.empty?
|
523
374
|
remove_records(existing_records, records, method)
|
@@ -530,21 +381,23 @@ module ActiveRecord
|
|
530
381
|
records.each { |record| callback(:before_remove, record) }
|
531
382
|
|
532
383
|
delete_records(existing_records, method) if existing_records.any?
|
533
|
-
|
384
|
+
@target -= records
|
385
|
+
@association_ids = nil
|
534
386
|
|
535
387
|
records.each { |record| callback(:after_remove, record) }
|
536
388
|
end
|
537
389
|
|
538
|
-
# Delete the given records from the association,
|
539
|
-
#
|
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).
|
540
393
|
def delete_records(records, method)
|
541
394
|
raise NotImplementedError
|
542
395
|
end
|
543
396
|
|
544
397
|
def replace_records(new_target, original_target)
|
545
|
-
delete(target
|
398
|
+
delete(difference(target, new_target))
|
546
399
|
|
547
|
-
unless concat(new_target
|
400
|
+
unless concat(difference(new_target, target))
|
548
401
|
@target = original_target
|
549
402
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
550
403
|
"new records could not be saved."
|
@@ -554,24 +407,53 @@ module ActiveRecord
|
|
554
407
|
end
|
555
408
|
|
556
409
|
def replace_common_records_in_memory(new_target, original_target)
|
557
|
-
common_records = new_target
|
410
|
+
common_records = intersection(new_target, original_target)
|
558
411
|
common_records.each do |record|
|
559
412
|
skip_callbacks = true
|
560
413
|
replace_on_target(record, @target.index(record), skip_callbacks)
|
561
414
|
end
|
562
415
|
end
|
563
416
|
|
564
|
-
def concat_records(records,
|
417
|
+
def concat_records(records, raise = false)
|
565
418
|
result = true
|
566
419
|
|
567
|
-
records.
|
420
|
+
records.each do |record|
|
568
421
|
raise_on_type_mismatch!(record)
|
569
|
-
add_to_target(record) do
|
570
|
-
|
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
|
571
428
|
end
|
572
429
|
end
|
573
430
|
|
574
|
-
|
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
|
575
457
|
end
|
576
458
|
|
577
459
|
def callback(method, record)
|
@@ -585,36 +467,12 @@ module ActiveRecord
|
|
585
467
|
owner.class.send(full_callback_name)
|
586
468
|
end
|
587
469
|
|
588
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
589
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
590
|
-
#
|
591
|
-
# If the args is just a non-empty options hash, go to the database.
|
592
|
-
#
|
593
|
-
# Otherwise, go to the database only if none of the following are true:
|
594
|
-
# * target already loaded
|
595
|
-
# * owner is new record
|
596
|
-
# * target contains new or changed record(s)
|
597
|
-
def fetch_first_nth_or_last_using_find?(args)
|
598
|
-
if args.first.is_a?(Hash)
|
599
|
-
true
|
600
|
-
else
|
601
|
-
!(loaded? ||
|
602
|
-
owner.new_record? ||
|
603
|
-
target.any? { |record| record.new_record? || record.changed? })
|
604
|
-
end
|
605
|
-
end
|
606
|
-
|
607
470
|
def include_in_memory?(record)
|
608
471
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
609
472
|
assoc = owner.association(reflection.through_reflection.name)
|
610
473
|
assoc.reader.any? { |source|
|
611
|
-
|
612
|
-
|
613
|
-
if target_association.respond_to?(:include?)
|
614
|
-
target_association.include?(record)
|
615
|
-
else
|
616
|
-
target_association == record
|
617
|
-
end
|
474
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
475
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
618
476
|
} || target.include?(record)
|
619
477
|
else
|
620
478
|
target.include?(record)
|
@@ -625,7 +483,7 @@ module ActiveRecord
|
|
625
483
|
# specified, then #find scans the entire collection.
|
626
484
|
def find_by_scan(*args)
|
627
485
|
expects_array = args.first.kind_of?(Array)
|
628
|
-
ids = args.flatten.compact.map
|
486
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
629
487
|
|
630
488
|
if ids.size == 1
|
631
489
|
id = ids.first
|
@@ -635,16 +493,6 @@ module ActiveRecord
|
|
635
493
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
636
494
|
end
|
637
495
|
end
|
638
|
-
|
639
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
640
|
-
def first_nth_or_last(type, *args)
|
641
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
642
|
-
|
643
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
644
|
-
collection.send(type, *args).tap do |record|
|
645
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
646
|
-
end
|
647
|
-
end
|
648
496
|
end
|
649
497
|
end
|
650
498
|
end
|