activerecord 5.0.7.2 → 6.1.1
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 +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- 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 +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +20 -22
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- 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 +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_decorators.rb +0 -67
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/except"
|
4
|
+
require "active_support/core_ext/module/redefine_method"
|
5
|
+
require "active_support/core_ext/hash/indifferent_access"
|
4
6
|
|
5
7
|
module ActiveRecord
|
6
8
|
module NestedAttributes #:nodoc:
|
@@ -10,8 +12,7 @@ module ActiveRecord
|
|
10
12
|
extend ActiveSupport::Concern
|
11
13
|
|
12
14
|
included do
|
13
|
-
class_attribute :nested_attributes_options, instance_writer: false
|
14
|
-
self.nested_attributes_options = {}
|
15
|
+
class_attribute :nested_attributes_options, instance_writer: false, default: {}
|
15
16
|
end
|
16
17
|
|
17
18
|
# = Active Record Nested Attributes
|
@@ -61,6 +62,18 @@ module ActiveRecord
|
|
61
62
|
# member.update params[:member]
|
62
63
|
# member.avatar.icon # => 'sad'
|
63
64
|
#
|
65
|
+
# If you want to update the current avatar without providing the id, you must add <tt>:update_only</tt> option.
|
66
|
+
#
|
67
|
+
# class Member < ActiveRecord::Base
|
68
|
+
# has_one :avatar
|
69
|
+
# accepts_nested_attributes_for :avatar, update_only: true
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# params = { member: { avatar_attributes: { icon: 'sad' } } }
|
73
|
+
# member.update params[:member]
|
74
|
+
# member.avatar.id # => 2
|
75
|
+
# member.avatar.icon # => 'sad'
|
76
|
+
#
|
64
77
|
# By default you will only be able to set and update attributes on the
|
65
78
|
# associated model. If you want to destroy the associated model through the
|
66
79
|
# attributes hash, you have to enable it first using the
|
@@ -267,7 +280,7 @@ module ActiveRecord
|
|
267
280
|
# member.avatar_attributes = {icon: 'sad'}
|
268
281
|
# member.avatar.width # => 200
|
269
282
|
module ClassMethods
|
270
|
-
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key ==
|
283
|
+
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
|
271
284
|
|
272
285
|
# Defines an attributes writer for the specified association(s).
|
273
286
|
#
|
@@ -275,7 +288,7 @@ module ActiveRecord
|
|
275
288
|
# [:allow_destroy]
|
276
289
|
# If true, destroys any members from the attributes hash with a
|
277
290
|
# <tt>_destroy</tt> key and a value that evaluates to +true+
|
278
|
-
# (
|
291
|
+
# (e.g. 1, '1', true, or 'true'). This option is off by default.
|
279
292
|
# [:reject_if]
|
280
293
|
# Allows you to specify a Proc or a Symbol pointing to a method
|
281
294
|
# that checks whether a record should be built for a certain attribute
|
@@ -317,7 +330,7 @@ module ActiveRecord
|
|
317
330
|
# # creates avatar_attributes= and posts_attributes=
|
318
331
|
# accepts_nested_attributes_for :avatar, :posts, allow_destroy: true
|
319
332
|
def accepts_nested_attributes_for(*attr_names)
|
320
|
-
options = { :
|
333
|
+
options = { allow_destroy: false, update_only: false }
|
321
334
|
options.update(attr_names.extract_options!)
|
322
335
|
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
|
323
336
|
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
|
@@ -340,28 +353,25 @@ module ActiveRecord
|
|
340
353
|
end
|
341
354
|
|
342
355
|
private
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
end
|
363
|
-
eoruby
|
364
|
-
end
|
356
|
+
# Generates a writer method for this association. Serves as a point for
|
357
|
+
# accessing the objects in the association. For example, this method
|
358
|
+
# could generate the following:
|
359
|
+
#
|
360
|
+
# def pirate_attributes=(attributes)
|
361
|
+
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
362
|
+
# end
|
363
|
+
#
|
364
|
+
# This redirects the attempts to write objects in an association through
|
365
|
+
# the helper methods defined below. Makes it seem like the nested
|
366
|
+
# associations are just regular associations.
|
367
|
+
def generate_association_writer(association_name, type)
|
368
|
+
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
|
369
|
+
silence_redefinition_of_method :#{association_name}_attributes=
|
370
|
+
def #{association_name}_attributes=(attributes)
|
371
|
+
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
372
|
+
end
|
373
|
+
eoruby
|
374
|
+
end
|
365
375
|
end
|
366
376
|
|
367
377
|
# Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
|
@@ -374,214 +384,214 @@ module ActiveRecord
|
|
374
384
|
end
|
375
385
|
|
376
386
|
private
|
387
|
+
# Attribute hash keys that should not be assigned as normal attributes.
|
388
|
+
# These hash keys are nested attributes implementation details.
|
389
|
+
UNASSIGNABLE_KEYS = %w( id _destroy )
|
377
390
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
attributes = attributes.to_h
|
399
|
-
end
|
400
|
-
attributes = attributes.with_indifferent_access
|
401
|
-
existing_record = send(association_name)
|
391
|
+
# Assigns the given attributes to the association.
|
392
|
+
#
|
393
|
+
# If an associated record does not yet exist, one will be instantiated. If
|
394
|
+
# an associated record already exists, the method's behavior depends on
|
395
|
+
# the value of the update_only option. If update_only is +false+ and the
|
396
|
+
# given attributes include an <tt>:id</tt> that matches the existing record's
|
397
|
+
# id, then the existing record will be modified. If no <tt>:id</tt> is provided
|
398
|
+
# it will be replaced with a new record. If update_only is +true+ the existing
|
399
|
+
# record will be modified regardless of whether an <tt>:id</tt> is provided.
|
400
|
+
#
|
401
|
+
# If the given attributes include a matching <tt>:id</tt> attribute, or
|
402
|
+
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
|
403
|
+
# then the existing record will be marked for destruction.
|
404
|
+
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
|
405
|
+
options = nested_attributes_options[association_name]
|
406
|
+
if attributes.respond_to?(:permitted?)
|
407
|
+
attributes = attributes.to_h
|
408
|
+
end
|
409
|
+
attributes = attributes.with_indifferent_access
|
410
|
+
existing_record = send(association_name)
|
402
411
|
|
403
|
-
|
404
|
-
|
405
|
-
|
412
|
+
if (options[:update_only] || !attributes["id"].blank?) && existing_record &&
|
413
|
+
(options[:update_only] || existing_record.id.to_s == attributes["id"].to_s)
|
414
|
+
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
|
406
415
|
|
407
|
-
|
408
|
-
|
416
|
+
elsif attributes["id"].present?
|
417
|
+
raise_nested_attributes_record_not_found!(association_name, attributes["id"])
|
409
418
|
|
410
|
-
|
411
|
-
|
419
|
+
elsif !reject_new_record?(association_name, attributes)
|
420
|
+
assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
412
421
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
else
|
417
|
-
method = "build_#{association_name}"
|
418
|
-
if respond_to?(method)
|
419
|
-
send(method, assignable_attributes)
|
422
|
+
if existing_record && existing_record.new_record?
|
423
|
+
existing_record.assign_attributes(assignable_attributes)
|
424
|
+
association(association_name).initialize_attributes(existing_record)
|
420
425
|
else
|
421
|
-
|
426
|
+
method = :"build_#{association_name}"
|
427
|
+
if respond_to?(method)
|
428
|
+
send(method, assignable_attributes)
|
429
|
+
else
|
430
|
+
raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
|
431
|
+
end
|
422
432
|
end
|
423
433
|
end
|
424
434
|
end
|
425
|
-
end
|
426
435
|
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
436
|
+
# Assigns the given attributes to the collection association.
|
437
|
+
#
|
438
|
+
# Hashes with an <tt>:id</tt> value matching an existing associated record
|
439
|
+
# will update that record. Hashes without an <tt>:id</tt> value will build
|
440
|
+
# a new record for the association. Hashes with a matching <tt>:id</tt>
|
441
|
+
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
|
442
|
+
# matched record for destruction.
|
443
|
+
#
|
444
|
+
# For example:
|
445
|
+
#
|
446
|
+
# assign_nested_attributes_for_collection_association(:people, {
|
447
|
+
# '1' => { id: '1', name: 'Peter' },
|
448
|
+
# '2' => { name: 'John' },
|
449
|
+
# '3' => { id: '2', _destroy: true }
|
450
|
+
# })
|
451
|
+
#
|
452
|
+
# Will update the name of the Person with ID 1, build a new associated
|
453
|
+
# person with the name 'John', and mark the associated Person with ID 2
|
454
|
+
# for destruction.
|
455
|
+
#
|
456
|
+
# Also accepts an Array of attribute hashes:
|
457
|
+
#
|
458
|
+
# assign_nested_attributes_for_collection_association(:people, [
|
459
|
+
# { id: '1', name: 'Peter' },
|
460
|
+
# { name: 'John' },
|
461
|
+
# { id: '2', _destroy: true }
|
462
|
+
# ])
|
463
|
+
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
|
464
|
+
options = nested_attributes_options[association_name]
|
465
|
+
if attributes_collection.respond_to?(:permitted?)
|
466
|
+
attributes_collection = attributes_collection.to_h
|
467
|
+
end
|
459
468
|
|
460
|
-
|
461
|
-
|
462
|
-
|
469
|
+
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
|
470
|
+
raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
|
471
|
+
end
|
463
472
|
|
464
|
-
|
473
|
+
check_record_limit!(options[:limit], attributes_collection)
|
465
474
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
475
|
+
if attributes_collection.is_a? Hash
|
476
|
+
keys = attributes_collection.keys
|
477
|
+
attributes_collection = if keys.include?("id") || keys.include?(:id)
|
478
|
+
[attributes_collection]
|
479
|
+
else
|
480
|
+
attributes_collection.values
|
481
|
+
end
|
472
482
|
end
|
473
|
-
end
|
474
|
-
|
475
|
-
association = association(association_name)
|
476
483
|
|
477
|
-
|
478
|
-
association.target
|
479
|
-
else
|
480
|
-
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
|
481
|
-
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
|
482
|
-
end
|
484
|
+
association = association(association_name)
|
483
485
|
|
484
|
-
|
485
|
-
|
486
|
-
|
486
|
+
existing_records = if association.loaded?
|
487
|
+
association.target
|
488
|
+
else
|
489
|
+
attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
|
490
|
+
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
|
487
491
|
end
|
488
|
-
attributes = attributes.with_indifferent_access
|
489
492
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
+
attributes_collection.each do |attributes|
|
494
|
+
if attributes.respond_to?(:permitted?)
|
495
|
+
attributes = attributes.to_h
|
493
496
|
end
|
494
|
-
|
495
|
-
unless call_reject_if(association_name, attributes)
|
496
|
-
# Make sure we are operating on the actual object which is in the association's
|
497
|
-
# proxy_target array (either by finding it, or adding it if not found)
|
498
|
-
# Take into account that the proxy_target may have changed due to callbacks
|
499
|
-
target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
|
500
|
-
if target_record
|
501
|
-
existing_record = target_record
|
502
|
-
else
|
503
|
-
association.add_to_target(existing_record, :skip_callbacks)
|
504
|
-
end
|
497
|
+
attributes = attributes.with_indifferent_access
|
505
498
|
|
506
|
-
|
499
|
+
if attributes["id"].blank?
|
500
|
+
unless reject_new_record?(association_name, attributes)
|
501
|
+
association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
|
502
|
+
end
|
503
|
+
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
|
504
|
+
unless call_reject_if(association_name, attributes)
|
505
|
+
# Make sure we are operating on the actual object which is in the association's
|
506
|
+
# proxy_target array (either by finding it, or adding it if not found)
|
507
|
+
# Take into account that the proxy_target may have changed due to callbacks
|
508
|
+
target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
|
509
|
+
if target_record
|
510
|
+
existing_record = target_record
|
511
|
+
else
|
512
|
+
association.add_to_target(existing_record, skip_callbacks: true)
|
513
|
+
end
|
514
|
+
|
515
|
+
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
516
|
+
end
|
517
|
+
else
|
518
|
+
raise_nested_attributes_record_not_found!(association_name, attributes["id"])
|
507
519
|
end
|
508
|
-
else
|
509
|
-
raise_nested_attributes_record_not_found!(association_name, attributes['id'])
|
510
520
|
end
|
511
521
|
end
|
512
|
-
end
|
513
522
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
523
|
+
# Takes in a limit and checks if the attributes_collection has too many
|
524
|
+
# records. It accepts limit in the form of symbol, proc, or
|
525
|
+
# number-like object (anything that can be compared with an integer).
|
526
|
+
#
|
527
|
+
# Raises TooManyRecords error if the attributes_collection is
|
528
|
+
# larger than the limit.
|
529
|
+
def check_record_limit!(limit, attributes_collection)
|
530
|
+
if limit
|
531
|
+
limit = \
|
532
|
+
case limit
|
533
|
+
when Symbol
|
534
|
+
send(limit)
|
535
|
+
when Proc
|
536
|
+
limit.call
|
537
|
+
else
|
538
|
+
limit
|
539
|
+
end
|
530
540
|
|
531
|
-
|
532
|
-
|
541
|
+
if limit && attributes_collection.size > limit
|
542
|
+
raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
|
543
|
+
end
|
533
544
|
end
|
534
545
|
end
|
535
|
-
end
|
536
546
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
547
|
+
# Updates a record with the +attributes+ or marks it for destruction if
|
548
|
+
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
|
549
|
+
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
|
550
|
+
record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
|
551
|
+
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
|
552
|
+
end
|
543
553
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
554
|
+
# Determines if a hash contains a truthy _destroy key.
|
555
|
+
def has_destroy_flag?(hash)
|
556
|
+
Type::Boolean.new.cast(hash["_destroy"])
|
557
|
+
end
|
548
558
|
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
559
|
+
# Determines if a new record should be rejected by checking
|
560
|
+
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
561
|
+
# association and evaluates to +true+.
|
562
|
+
def reject_new_record?(association_name, attributes)
|
563
|
+
will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
|
564
|
+
end
|
555
565
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
566
|
+
# Determines if a record with the particular +attributes+ should be
|
567
|
+
# rejected by calling the reject_if Symbol or Proc (if defined).
|
568
|
+
# The reject_if option is defined by +accepts_nested_attributes_for+.
|
569
|
+
#
|
570
|
+
# Returns false if there is a +destroy_flag+ on the attributes.
|
571
|
+
def call_reject_if(association_name, attributes)
|
572
|
+
return false if will_be_destroyed?(association_name, attributes)
|
563
573
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
574
|
+
case callback = nested_attributes_options[association_name][:reject_if]
|
575
|
+
when Symbol
|
576
|
+
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
|
577
|
+
when Proc
|
578
|
+
callback.call(attributes)
|
579
|
+
end
|
569
580
|
end
|
570
|
-
end
|
571
581
|
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
582
|
+
# Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
|
583
|
+
def will_be_destroyed?(association_name, attributes)
|
584
|
+
allow_destroy?(association_name) && has_destroy_flag?(attributes)
|
585
|
+
end
|
576
586
|
|
577
|
-
|
578
|
-
|
579
|
-
|
587
|
+
def allow_destroy?(association_name)
|
588
|
+
nested_attributes_options[association_name][:allow_destroy]
|
589
|
+
end
|
580
590
|
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
591
|
+
def raise_nested_attributes_record_not_found!(association_name, record_id)
|
592
|
+
model = self.class._reflect_on_association(association_name).klass.name
|
593
|
+
raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
|
594
|
+
model, "id", record_id)
|
595
|
+
end
|
586
596
|
end
|
587
597
|
end
|