activerecord 4.2.11.2 → 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 +4 -4
- data/CHANGELOG.md +613 -1638
- 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 +131 -287
- 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 -86
- 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 -243
- 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 -635
- 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 -180
- 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 +6 -4
- 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 +621 -303
- 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 +312 -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 +143 -44
- 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 +328 -185
- 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 +307 -100
- 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 -59
- 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 -498
- 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 -64
- 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,103 +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
|
-
end
|
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) }
|
69
61
|
|
70
|
-
|
71
|
-
r.
|
62
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
63
|
+
r.public_send(primary_key)
|
72
64
|
end.values_at(*ids).compact
|
73
65
|
|
74
|
-
if
|
75
|
-
|
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)
|
76
70
|
else
|
77
|
-
|
71
|
+
replace(records)
|
78
72
|
end
|
79
73
|
end
|
80
74
|
|
81
75
|
def reset
|
82
76
|
super
|
83
77
|
@target = []
|
84
|
-
|
85
|
-
|
86
|
-
def select(*fields)
|
87
|
-
if block_given?
|
88
|
-
load_target.select.each { |e| yield e }
|
89
|
-
else
|
90
|
-
scope.select(*fields)
|
91
|
-
end
|
78
|
+
@association_ids = nil
|
92
79
|
end
|
93
80
|
|
94
81
|
def find(*args)
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
if options[:inverse_of] && loaded?
|
99
|
-
args_flatten = args.flatten
|
100
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
101
|
-
result = find_by_scan(*args)
|
102
|
-
|
103
|
-
result_size = Array(result).size
|
104
|
-
if !result || result_size != args_flatten.size
|
105
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
106
|
-
else
|
107
|
-
result
|
108
|
-
end
|
109
|
-
else
|
110
|
-
scope.find(*args)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def first(*args)
|
116
|
-
first_nth_or_last(:first, *args)
|
117
|
-
end
|
118
|
-
|
119
|
-
def second(*args)
|
120
|
-
first_nth_or_last(:second, *args)
|
121
|
-
end
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
122
85
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
def fourth(*args)
|
128
|
-
first_nth_or_last(:fourth, *args)
|
129
|
-
end
|
130
|
-
|
131
|
-
def fifth(*args)
|
132
|
-
first_nth_or_last(:fifth, *args)
|
133
|
-
end
|
134
|
-
|
135
|
-
def forty_two(*args)
|
136
|
-
first_nth_or_last(:forty_two, *args)
|
137
|
-
end
|
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
|
138
90
|
|
139
|
-
|
140
|
-
first_nth_or_last(:last, *args)
|
141
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
142
92
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
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
|
149
98
|
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
150
101
|
end
|
151
102
|
end
|
152
103
|
|
@@ -154,24 +105,14 @@ module ActiveRecord
|
|
154
105
|
if attributes.is_a?(Array)
|
155
106
|
attributes.collect { |attr| build(attr, &block) }
|
156
107
|
else
|
157
|
-
add_to_target(build_record(attributes))
|
158
|
-
yield(record) if block_given?
|
159
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
160
109
|
end
|
161
110
|
end
|
162
111
|
|
163
|
-
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
def create!(attributes = {}, &block)
|
168
|
-
_create_record(attributes, true, &block)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
172
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
173
|
-
# +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.
|
174
114
|
def concat(*records)
|
115
|
+
records = records.flatten
|
175
116
|
if owner.new_record?
|
176
117
|
load_target
|
177
118
|
concat_records(records)
|
@@ -214,12 +155,12 @@ module ActiveRecord
|
|
214
155
|
end
|
215
156
|
|
216
157
|
dependent = if dependent
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
223
164
|
|
224
165
|
delete_or_nullify_all_records(dependent).tap do
|
225
166
|
reset
|
@@ -237,32 +178,6 @@ module ActiveRecord
|
|
237
178
|
end
|
238
179
|
end
|
239
180
|
|
240
|
-
# Count all records using SQL. Construct options and pass them with
|
241
|
-
# scope to the target class's +count+.
|
242
|
-
def count(column_name = nil, count_options = {})
|
243
|
-
# TODO: Remove count_options argument as soon we remove support to
|
244
|
-
# activerecord-deprecated_finders.
|
245
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
246
|
-
|
247
|
-
relation = scope
|
248
|
-
if association_scope.distinct_value
|
249
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
250
|
-
column_name ||= reflection.klass.primary_key
|
251
|
-
relation = relation.distinct
|
252
|
-
end
|
253
|
-
|
254
|
-
value = relation.count(column_name)
|
255
|
-
|
256
|
-
limit = options[:limit]
|
257
|
-
offset = options[:offset]
|
258
|
-
|
259
|
-
if limit || offset
|
260
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
261
|
-
else
|
262
|
-
value
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
181
|
# Removes +records+ from this association calling +before_remove+ and
|
267
182
|
# +after_remove+ callbacks.
|
268
183
|
#
|
@@ -271,12 +186,7 @@ module ActiveRecord
|
|
271
186
|
# are actually removed from the database, that depends precisely on
|
272
187
|
# +delete_records+. They are in any case removed from the collection.
|
273
188
|
def delete(*records)
|
274
|
-
|
275
|
-
_options = records.extract_options!
|
276
|
-
dependent = _options[:dependent] || options[:dependent]
|
277
|
-
|
278
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
279
|
-
delete_or_destroy(records, dependent)
|
189
|
+
delete_or_destroy(records, options[:dependent])
|
280
190
|
end
|
281
191
|
|
282
192
|
# Deletes the +records+ and removes them from this association calling
|
@@ -285,8 +195,6 @@ module ActiveRecord
|
|
285
195
|
# Note that this method removes records from the database ignoring the
|
286
196
|
# +:dependent+ option.
|
287
197
|
def destroy(*records)
|
288
|
-
return if records.empty?
|
289
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
290
198
|
delete_or_destroy(records, :destroy)
|
291
199
|
end
|
292
200
|
|
@@ -302,30 +210,19 @@ module ActiveRecord
|
|
302
210
|
# +count_records+, which is a method descendants have to provide.
|
303
211
|
def size
|
304
212
|
if !find_target? || loaded?
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
end
|
310
|
-
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?
|
311
217
|
load_target.size
|
312
|
-
elsif !
|
313
|
-
unsaved_records = target.select
|
218
|
+
elsif !association_scope.distinct_value && !target.empty?
|
219
|
+
unsaved_records = target.select(&:new_record?)
|
314
220
|
unsaved_records.size + count_records
|
315
221
|
else
|
316
222
|
count_records
|
317
223
|
end
|
318
224
|
end
|
319
225
|
|
320
|
-
# Returns the size of the collection calling +size+ on the target.
|
321
|
-
#
|
322
|
-
# If the collection has been already loaded +length+ and +size+ are
|
323
|
-
# equivalent. If not and you are going to need the records anyway this
|
324
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
325
|
-
def length
|
326
|
-
load_target.size
|
327
|
-
end
|
328
|
-
|
329
226
|
# Returns true if the collection is empty.
|
330
227
|
#
|
331
228
|
# If the collection has been loaded
|
@@ -335,41 +232,13 @@ module ActiveRecord
|
|
335
232
|
# loaded and you are going to fetch the records anyway it is better to
|
336
233
|
# check <tt>collection.length.zero?</tt>.
|
337
234
|
def empty?
|
338
|
-
if loaded?
|
235
|
+
if loaded? || @association_ids || reflection.has_cached_counter?
|
339
236
|
size.zero?
|
340
237
|
else
|
341
|
-
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
# Returns true if the collections is not empty.
|
346
|
-
# Equivalent to +!collection.empty?+.
|
347
|
-
def any?
|
348
|
-
if block_given?
|
349
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
350
|
-
else
|
351
|
-
!empty?
|
238
|
+
target.empty? && !scope.exists?
|
352
239
|
end
|
353
240
|
end
|
354
241
|
|
355
|
-
# Returns true if the collection has more than 1 record.
|
356
|
-
# Equivalent to +collection.size > 1+.
|
357
|
-
def many?
|
358
|
-
if block_given?
|
359
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
360
|
-
else
|
361
|
-
size > 1
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
def distinct
|
366
|
-
seen = {}
|
367
|
-
load_target.find_all do |record|
|
368
|
-
seen[record.id] = true unless seen.key?(record.id)
|
369
|
-
end
|
370
|
-
end
|
371
|
-
alias uniq distinct
|
372
|
-
|
373
242
|
# Replace this collection with +other_array+. This will perform a diff
|
374
243
|
# and delete/add only records that have changed.
|
375
244
|
def replace(other_array)
|
@@ -382,6 +251,8 @@ module ActiveRecord
|
|
382
251
|
replace_common_records_in_memory(other_array, original_target)
|
383
252
|
if other_array != original_target
|
384
253
|
transaction { replace_records(other_array, original_target) }
|
254
|
+
else
|
255
|
+
other_array
|
385
256
|
end
|
386
257
|
end
|
387
258
|
end
|
@@ -414,25 +285,9 @@ module ActiveRecord
|
|
414
285
|
replace_on_target(record, index, skip_callbacks, &block)
|
415
286
|
end
|
416
287
|
|
417
|
-
def
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if index
|
422
|
-
@target[index] = record
|
423
|
-
else
|
424
|
-
@target << record
|
425
|
-
end
|
426
|
-
|
427
|
-
callback(:after_add, record) unless skip_callbacks
|
428
|
-
set_inverse_instance(record)
|
429
|
-
|
430
|
-
record
|
431
|
-
end
|
432
|
-
|
433
|
-
def scope(opts = {})
|
434
|
-
scope = super()
|
435
|
-
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
288
|
+
def scope
|
289
|
+
scope = super
|
290
|
+
scope.none! if null_scope?
|
436
291
|
scope
|
437
292
|
end
|
438
293
|
|
@@ -440,28 +295,13 @@ module ActiveRecord
|
|
440
295
|
owner.new_record? && !foreign_key_present?
|
441
296
|
end
|
442
297
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
conn = klass.connection
|
448
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
449
|
-
StatementCache.create(conn) { |params|
|
450
|
-
as = AssociationScope.create { params.bind }
|
451
|
-
target_scope.merge as.scope(self, conn)
|
452
|
-
}
|
453
|
-
end
|
454
|
-
|
455
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
456
|
-
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? }
|
457
302
|
end
|
458
303
|
|
459
|
-
|
460
|
-
records = get_records
|
461
|
-
records.each { |record| set_inverse_instance(record) }
|
462
|
-
records
|
463
|
-
end
|
464
|
-
|
304
|
+
private
|
465
305
|
# We have some records loaded from the database (persisted) and some that are
|
466
306
|
# in-memory (memory). The same record may be represented in the persisted array
|
467
307
|
# and in the memory array.
|
@@ -479,7 +319,7 @@ module ActiveRecord
|
|
479
319
|
persisted.map! do |record|
|
480
320
|
if mem_record = memory.delete(record)
|
481
321
|
|
482
|
-
((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|
|
483
323
|
mem_record[name] = record[name]
|
484
324
|
end
|
485
325
|
|
@@ -500,28 +340,35 @@ module ActiveRecord
|
|
500
340
|
if attributes.is_a?(Array)
|
501
341
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
502
342
|
else
|
343
|
+
record = build_record(attributes, &block)
|
503
344
|
transaction do
|
504
|
-
|
505
|
-
|
506
|
-
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
|
+
}
|
507
350
|
end
|
351
|
+
raise ActiveRecord::Rollback unless result
|
508
352
|
end
|
353
|
+
record
|
509
354
|
end
|
510
355
|
end
|
511
356
|
|
512
357
|
# Do the relevant stuff to insert the given record into the association collection.
|
513
|
-
def insert_record(record, validate = true, raise = false)
|
514
|
-
raise
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
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
|
519
364
|
end
|
520
365
|
|
521
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) }
|
522
369
|
records = records.flatten
|
523
370
|
records.each { |record| raise_on_type_mismatch!(record) }
|
524
|
-
existing_records = records.reject
|
371
|
+
existing_records = records.reject(&:new_record?)
|
525
372
|
|
526
373
|
if existing_records.empty?
|
527
374
|
remove_records(existing_records, records, method)
|
@@ -534,21 +381,23 @@ module ActiveRecord
|
|
534
381
|
records.each { |record| callback(:before_remove, record) }
|
535
382
|
|
536
383
|
delete_records(existing_records, method) if existing_records.any?
|
537
|
-
|
384
|
+
@target -= records
|
385
|
+
@association_ids = nil
|
538
386
|
|
539
387
|
records.each { |record| callback(:after_remove, record) }
|
540
388
|
end
|
541
389
|
|
542
|
-
# Delete the given records from the association,
|
543
|
-
#
|
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).
|
544
393
|
def delete_records(records, method)
|
545
394
|
raise NotImplementedError
|
546
395
|
end
|
547
396
|
|
548
397
|
def replace_records(new_target, original_target)
|
549
|
-
delete(target
|
398
|
+
delete(difference(target, new_target))
|
550
399
|
|
551
|
-
unless concat(new_target
|
400
|
+
unless concat(difference(new_target, target))
|
552
401
|
@target = original_target
|
553
402
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
554
403
|
"new records could not be saved."
|
@@ -558,24 +407,53 @@ module ActiveRecord
|
|
558
407
|
end
|
559
408
|
|
560
409
|
def replace_common_records_in_memory(new_target, original_target)
|
561
|
-
common_records = new_target
|
410
|
+
common_records = intersection(new_target, original_target)
|
562
411
|
common_records.each do |record|
|
563
412
|
skip_callbacks = true
|
564
413
|
replace_on_target(record, @target.index(record), skip_callbacks)
|
565
414
|
end
|
566
415
|
end
|
567
416
|
|
568
|
-
def concat_records(records,
|
417
|
+
def concat_records(records, raise = false)
|
569
418
|
result = true
|
570
419
|
|
571
|
-
records.
|
420
|
+
records.each do |record|
|
572
421
|
raise_on_type_mismatch!(record)
|
573
|
-
add_to_target(record) do
|
574
|
-
|
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
|
575
428
|
end
|
576
429
|
end
|
577
430
|
|
578
|
-
|
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
|
579
457
|
end
|
580
458
|
|
581
459
|
def callback(method, record)
|
@@ -589,36 +467,12 @@ module ActiveRecord
|
|
589
467
|
owner.class.send(full_callback_name)
|
590
468
|
end
|
591
469
|
|
592
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
593
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
594
|
-
#
|
595
|
-
# If the args is just a non-empty options hash, go to the database.
|
596
|
-
#
|
597
|
-
# Otherwise, go to the database only if none of the following are true:
|
598
|
-
# * target already loaded
|
599
|
-
# * owner is new record
|
600
|
-
# * target contains new or changed record(s)
|
601
|
-
def fetch_first_nth_or_last_using_find?(args)
|
602
|
-
if args.first.is_a?(Hash)
|
603
|
-
true
|
604
|
-
else
|
605
|
-
!(loaded? ||
|
606
|
-
owner.new_record? ||
|
607
|
-
target.any? { |record| record.new_record? || record.changed? })
|
608
|
-
end
|
609
|
-
end
|
610
|
-
|
611
470
|
def include_in_memory?(record)
|
612
471
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
613
472
|
assoc = owner.association(reflection.through_reflection.name)
|
614
473
|
assoc.reader.any? { |source|
|
615
|
-
|
616
|
-
|
617
|
-
if target_association.respond_to?(:include?)
|
618
|
-
target_association.include?(record)
|
619
|
-
else
|
620
|
-
target_association == record
|
621
|
-
end
|
474
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
475
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
622
476
|
} || target.include?(record)
|
623
477
|
else
|
624
478
|
target.include?(record)
|
@@ -629,7 +483,7 @@ module ActiveRecord
|
|
629
483
|
# specified, then #find scans the entire collection.
|
630
484
|
def find_by_scan(*args)
|
631
485
|
expects_array = args.first.kind_of?(Array)
|
632
|
-
ids = args.flatten.compact.map
|
486
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
633
487
|
|
634
488
|
if ids.size == 1
|
635
489
|
id = ids.first
|
@@ -639,16 +493,6 @@ module ActiveRecord
|
|
639
493
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
640
494
|
end
|
641
495
|
end
|
642
|
-
|
643
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
644
|
-
def first_nth_or_last(type, *args)
|
645
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
646
|
-
|
647
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
648
|
-
collection.send(type, *args).tap do |record|
|
649
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
650
|
-
end
|
651
|
-
end
|
652
496
|
end
|
653
497
|
end
|
654
498
|
end
|