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,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record No Touching
|
3
5
|
module NoTouching
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
6
8
|
module ClassMethods
|
7
|
-
# Lets you selectively disable calls to
|
9
|
+
# Lets you selectively disable calls to +touch+ for the
|
8
10
|
# duration of a block.
|
9
11
|
#
|
10
12
|
# ==== Examples
|
@@ -41,6 +43,13 @@ module ActiveRecord
|
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
46
|
+
# Returns +true+ if the class has +no_touching+ set, +false+ otherwise.
|
47
|
+
#
|
48
|
+
# Project.no_touching do
|
49
|
+
# Project.first.no_touching? # true
|
50
|
+
# Message.first.no_touching? # false
|
51
|
+
# end
|
52
|
+
#
|
44
53
|
def no_touching?
|
45
54
|
NoTouching.applied_to?(self.class)
|
46
55
|
end
|
@@ -49,7 +58,7 @@ module ActiveRecord
|
|
49
58
|
super unless no_touching?
|
50
59
|
end
|
51
60
|
|
52
|
-
def touch(
|
61
|
+
def touch(*, **) # :nodoc:
|
53
62
|
super unless no_touching?
|
54
63
|
end
|
55
64
|
end
|
@@ -1,14 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module NullRelation # :nodoc:
|
3
|
-
def exec_queries
|
4
|
-
@records = [].freeze
|
5
|
-
end
|
6
|
-
|
7
5
|
def pluck(*column_names)
|
8
6
|
[]
|
9
7
|
end
|
10
8
|
|
11
|
-
def delete_all
|
9
|
+
def delete_all
|
12
10
|
0
|
13
11
|
end
|
14
12
|
|
@@ -20,10 +18,6 @@ module ActiveRecord
|
|
20
18
|
0
|
21
19
|
end
|
22
20
|
|
23
|
-
def size
|
24
|
-
calculate :size, nil
|
25
|
-
end
|
26
|
-
|
27
21
|
def empty?
|
28
22
|
true
|
29
23
|
end
|
@@ -48,33 +42,12 @@ module ActiveRecord
|
|
48
42
|
""
|
49
43
|
end
|
50
44
|
|
51
|
-
def count(*)
|
52
|
-
calculate :count, nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def sum(*)
|
56
|
-
calculate :sum, nil
|
57
|
-
end
|
58
|
-
|
59
|
-
def average(*)
|
60
|
-
calculate :average, nil
|
61
|
-
end
|
62
|
-
|
63
|
-
def minimum(*)
|
64
|
-
calculate :minimum, nil
|
65
|
-
end
|
66
|
-
|
67
|
-
def maximum(*)
|
68
|
-
calculate :maximum, nil
|
69
|
-
end
|
70
|
-
|
71
45
|
def calculate(operation, _column_name)
|
72
|
-
|
46
|
+
case operation
|
47
|
+
when :count, :sum
|
73
48
|
group_values.any? ? Hash.new : 0
|
74
|
-
|
75
|
-
Hash.new
|
76
|
-
else
|
77
|
-
nil
|
49
|
+
when :average, :minimum, :maximum
|
50
|
+
group_values.any? ? Hash.new : nil
|
78
51
|
end
|
79
52
|
end
|
80
53
|
|
@@ -85,5 +58,10 @@ module ActiveRecord
|
|
85
58
|
def or(other)
|
86
59
|
other.spawn
|
87
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def exec_queries
|
64
|
+
@records = [].freeze
|
65
|
+
end
|
88
66
|
end
|
89
67
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/insert_all"
|
4
|
+
|
1
5
|
module ActiveRecord
|
2
6
|
# = Active Record \Persistence
|
3
7
|
module Persistence
|
@@ -53,6 +57,192 @@ module ActiveRecord
|
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
60
|
+
# Inserts a single record into the database in a single SQL INSERT
|
61
|
+
# statement. It does not instantiate any models nor does it trigger
|
62
|
+
# Active Record callbacks or validations. Though passed values
|
63
|
+
# go through Active Record's type casting and serialization.
|
64
|
+
#
|
65
|
+
# See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
|
66
|
+
def insert(attributes, returning: nil, unique_by: nil)
|
67
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
71
|
+
# statement. It does not instantiate any models nor does it trigger
|
72
|
+
# Active Record callbacks or validations. Though passed values
|
73
|
+
# go through Active Record's type casting and serialization.
|
74
|
+
#
|
75
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
76
|
+
# the attributes for a single row and must have the same keys.
|
77
|
+
#
|
78
|
+
# Rows are considered to be unique by every unique index on the table. Any
|
79
|
+
# duplicate rows are skipped.
|
80
|
+
# Override with <tt>:unique_by</tt> (see below).
|
81
|
+
#
|
82
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
83
|
+
# <tt>:returning</tt> (see below).
|
84
|
+
#
|
85
|
+
# ==== Options
|
86
|
+
#
|
87
|
+
# [:returning]
|
88
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
89
|
+
# inserted records, which by default is the primary key.
|
90
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
91
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
|
+
# clause entirely.
|
93
|
+
#
|
94
|
+
# [:unique_by]
|
95
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
97
|
+
#
|
98
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
99
|
+
#
|
100
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
101
|
+
# row has an existing id, or is not unique by another unique index,
|
102
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
103
|
+
#
|
104
|
+
# Unique indexes can be identified by columns or name:
|
105
|
+
#
|
106
|
+
# unique_by: :isbn
|
107
|
+
# unique_by: %i[ author_id name ]
|
108
|
+
# unique_by: :index_books_on_isbn
|
109
|
+
#
|
110
|
+
# Because it relies on the index information from the database
|
111
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
112
|
+
# Active Record's schema_cache.
|
113
|
+
#
|
114
|
+
# ==== Example
|
115
|
+
#
|
116
|
+
# # Insert records and skip inserting any duplicates.
|
117
|
+
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
118
|
+
#
|
119
|
+
# Book.insert_all([
|
120
|
+
# { id: 1, title: "Rework", author: "David" },
|
121
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
|
+
# ])
|
123
|
+
def insert_all(attributes, returning: nil, unique_by: nil)
|
124
|
+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
|
125
|
+
end
|
126
|
+
|
127
|
+
# Inserts a single record into the database in a single SQL INSERT
|
128
|
+
# statement. It does not instantiate any models nor does it trigger
|
129
|
+
# Active Record callbacks or validations. Though passed values
|
130
|
+
# go through Active Record's type casting and serialization.
|
131
|
+
#
|
132
|
+
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
133
|
+
def insert!(attributes, returning: nil)
|
134
|
+
insert_all!([ attributes ], returning: returning)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Inserts multiple records into the database in a single SQL INSERT
|
138
|
+
# statement. It does not instantiate any models nor does it trigger
|
139
|
+
# Active Record callbacks or validations. Though passed values
|
140
|
+
# go through Active Record's type casting and serialization.
|
141
|
+
#
|
142
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
143
|
+
# the attributes for a single row and must have the same keys.
|
144
|
+
#
|
145
|
+
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
146
|
+
# unique index on the table. In that case, no rows are inserted.
|
147
|
+
#
|
148
|
+
# To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
|
149
|
+
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
150
|
+
#
|
151
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
152
|
+
# <tt>:returning</tt> (see below).
|
153
|
+
#
|
154
|
+
# ==== Options
|
155
|
+
#
|
156
|
+
# [:returning]
|
157
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
158
|
+
# inserted records, which by default is the primary key.
|
159
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
160
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
|
+
# clause entirely.
|
162
|
+
#
|
163
|
+
# ==== Examples
|
164
|
+
#
|
165
|
+
# # Insert multiple records
|
166
|
+
# Book.insert_all!([
|
167
|
+
# { title: "Rework", author: "David" },
|
168
|
+
# { title: "Eloquent Ruby", author: "Russ" }
|
169
|
+
# ])
|
170
|
+
#
|
171
|
+
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
172
|
+
# # does not have a unique id.
|
173
|
+
# Book.insert_all!([
|
174
|
+
# { id: 1, title: "Rework", author: "David" },
|
175
|
+
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
|
+
# ])
|
177
|
+
def insert_all!(attributes, returning: nil)
|
178
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
179
|
+
end
|
180
|
+
|
181
|
+
# Updates or inserts (upserts) a single record into the database in a
|
182
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
183
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
184
|
+
# go through Active Record's type casting and serialization.
|
185
|
+
#
|
186
|
+
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
187
|
+
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
+
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Updates or inserts (upserts) multiple records into the database in a
|
192
|
+
# single SQL INSERT statement. It does not instantiate any models nor does
|
193
|
+
# it trigger Active Record callbacks or validations. Though passed values
|
194
|
+
# go through Active Record's type casting and serialization.
|
195
|
+
#
|
196
|
+
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
197
|
+
# the attributes for a single row and must have the same keys.
|
198
|
+
#
|
199
|
+
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
200
|
+
# <tt>:returning</tt> (see below).
|
201
|
+
#
|
202
|
+
# ==== Options
|
203
|
+
#
|
204
|
+
# [:returning]
|
205
|
+
# (PostgreSQL only) An array of attributes to return for all successfully
|
206
|
+
# inserted records, which by default is the primary key.
|
207
|
+
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
208
|
+
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
|
+
# clause entirely.
|
210
|
+
#
|
211
|
+
# [:unique_by]
|
212
|
+
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
|
+
# by every unique index on the table. Any duplicate rows are skipped.
|
214
|
+
#
|
215
|
+
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
216
|
+
#
|
217
|
+
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
218
|
+
# row has an existing id, or is not unique by another unique index,
|
219
|
+
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
220
|
+
#
|
221
|
+
# Unique indexes can be identified by columns or name:
|
222
|
+
#
|
223
|
+
# unique_by: :isbn
|
224
|
+
# unique_by: %i[ author_id name ]
|
225
|
+
# unique_by: :index_books_on_isbn
|
226
|
+
#
|
227
|
+
# Because it relies on the index information from the database
|
228
|
+
# <tt>:unique_by</tt> is recommended to be paired with
|
229
|
+
# Active Record's schema_cache.
|
230
|
+
#
|
231
|
+
# ==== Examples
|
232
|
+
#
|
233
|
+
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
234
|
+
# # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
|
235
|
+
#
|
236
|
+
# Book.upsert_all([
|
237
|
+
# { title: "Rework", author: "David", isbn: "1" },
|
238
|
+
# { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
|
239
|
+
# ], unique_by: :isbn)
|
240
|
+
#
|
241
|
+
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
242
|
+
def upsert_all(attributes, returning: nil, unique_by: nil)
|
243
|
+
InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
|
244
|
+
end
|
245
|
+
|
56
246
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
57
247
|
# the appropriate class. Accepts only keys as strings.
|
58
248
|
#
|
@@ -65,11 +255,154 @@ module ActiveRecord
|
|
65
255
|
# how this "single-table" inheritance mapping is implemented.
|
66
256
|
def instantiate(attributes, column_types = {}, &block)
|
67
257
|
klass = discriminate_class_for_record(attributes)
|
68
|
-
|
69
|
-
|
258
|
+
instantiate_instance_of(klass, attributes, column_types, &block)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
262
|
+
# The resulting object is returned whether the object was saved successfully to the database or not.
|
263
|
+
#
|
264
|
+
# ==== Parameters
|
265
|
+
#
|
266
|
+
# * +id+ - This should be the id or an array of ids to be updated.
|
267
|
+
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
|
+
#
|
269
|
+
# ==== Examples
|
270
|
+
#
|
271
|
+
# # Updates one record
|
272
|
+
# Person.update(15, user_name: "Samuel", group: "expert")
|
273
|
+
#
|
274
|
+
# # Updates multiple records
|
275
|
+
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
|
276
|
+
# Person.update(people.keys, people.values)
|
277
|
+
#
|
278
|
+
# # Updates multiple records from the result of a relation
|
279
|
+
# people = Person.where(group: "expert")
|
280
|
+
# people.update(group: "masters")
|
281
|
+
#
|
282
|
+
# Note: Updating a large number of records will run an UPDATE
|
283
|
+
# query for each record, which may cause a performance issue.
|
284
|
+
# When running callbacks is not needed for each record update,
|
285
|
+
# it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
|
286
|
+
# for updating all records in a single query.
|
287
|
+
def update(id = :all, attributes)
|
288
|
+
if id.is_a?(Array)
|
289
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
|
+
object.update(attributes[idx])
|
291
|
+
}
|
292
|
+
elsif id == :all
|
293
|
+
all.each { |record| record.update(attributes) }
|
294
|
+
else
|
295
|
+
if ActiveRecord::Base === id
|
296
|
+
raise ArgumentError,
|
297
|
+
"You are passing an instance of ActiveRecord::Base to `update`. " \
|
298
|
+
"Please pass the id of the object by calling `.id`."
|
299
|
+
end
|
300
|
+
object = find(id)
|
301
|
+
object.update(attributes)
|
302
|
+
object
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
307
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
308
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
309
|
+
#
|
310
|
+
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
311
|
+
# from the attributes, and then calls destroy on it.
|
312
|
+
#
|
313
|
+
# ==== Parameters
|
314
|
+
#
|
315
|
+
# * +id+ - This should be the id or an array of ids to be destroyed.
|
316
|
+
#
|
317
|
+
# ==== Examples
|
318
|
+
#
|
319
|
+
# # Destroy a single object
|
320
|
+
# Todo.destroy(1)
|
321
|
+
#
|
322
|
+
# # Destroy multiple objects
|
323
|
+
# todos = [1,2,3]
|
324
|
+
# Todo.destroy(todos)
|
325
|
+
def destroy(id)
|
326
|
+
if id.is_a?(Array)
|
327
|
+
find(id).each(&:destroy)
|
328
|
+
else
|
329
|
+
find(id).destroy
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
334
|
+
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
335
|
+
# Record objects are not instantiated, so the object's callbacks are not
|
336
|
+
# executed, including any <tt>:dependent</tt> association options.
|
337
|
+
#
|
338
|
+
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
339
|
+
#
|
340
|
+
# Note: Although it is often much faster than the alternative, #destroy,
|
341
|
+
# skipping callbacks might bypass business logic in your application
|
342
|
+
# that ensures referential integrity or performs other essential jobs.
|
343
|
+
#
|
344
|
+
# ==== Examples
|
345
|
+
#
|
346
|
+
# # Delete a single row
|
347
|
+
# Todo.delete(1)
|
348
|
+
#
|
349
|
+
# # Delete multiple rows
|
350
|
+
# Todo.delete([2,3,4])
|
351
|
+
def delete(id_or_array)
|
352
|
+
delete_by(primary_key => id_or_array)
|
353
|
+
end
|
354
|
+
|
355
|
+
def _insert_record(values) # :nodoc:
|
356
|
+
primary_key = self.primary_key
|
357
|
+
primary_key_value = nil
|
358
|
+
|
359
|
+
if primary_key && Hash === values
|
360
|
+
primary_key_value = values[primary_key]
|
361
|
+
|
362
|
+
if !primary_key_value && prefetch_primary_key?
|
363
|
+
primary_key_value = next_sequence_value
|
364
|
+
values[primary_key] = primary_key_value
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
if values.empty?
|
369
|
+
im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
|
370
|
+
im.into arel_table
|
371
|
+
else
|
372
|
+
im = arel_table.compile_insert(_substitute_values(values))
|
373
|
+
end
|
374
|
+
|
375
|
+
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
376
|
+
end
|
377
|
+
|
378
|
+
def _update_record(values, constraints) # :nodoc:
|
379
|
+
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
|
380
|
+
|
381
|
+
um = arel_table.where(
|
382
|
+
constraints.reduce(&:and)
|
383
|
+
).compile_update(_substitute_values(values), primary_key)
|
384
|
+
|
385
|
+
connection.update(um, "#{self} Update")
|
386
|
+
end
|
387
|
+
|
388
|
+
def _delete_record(constraints) # :nodoc:
|
389
|
+
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
|
390
|
+
|
391
|
+
dm = Arel::DeleteManager.new
|
392
|
+
dm.from(arel_table)
|
393
|
+
dm.wheres = constraints
|
394
|
+
|
395
|
+
connection.delete(dm, "#{self} Destroy")
|
70
396
|
end
|
71
397
|
|
72
398
|
private
|
399
|
+
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
400
|
+
# new instance of the class. Accepts only keys as strings.
|
401
|
+
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
402
|
+
attributes = klass.attributes_builder.build_from_database(attributes, column_types)
|
403
|
+
klass.allocate.init_with_attributes(attributes, &block)
|
404
|
+
end
|
405
|
+
|
73
406
|
# Called by +instantiate+ to decide which class to use for a new
|
74
407
|
# record instance.
|
75
408
|
#
|
@@ -78,31 +411,43 @@ module ActiveRecord
|
|
78
411
|
def discriminate_class_for_record(record)
|
79
412
|
self
|
80
413
|
end
|
414
|
+
|
415
|
+
def _substitute_values(values)
|
416
|
+
values.map do |name, value|
|
417
|
+
attr = arel_table[name]
|
418
|
+
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
419
|
+
[attr, bind]
|
420
|
+
end
|
421
|
+
end
|
81
422
|
end
|
82
423
|
|
83
424
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
84
425
|
# for the object doesn't exist in the database yet; otherwise, returns false.
|
85
426
|
def new_record?
|
86
|
-
sync_with_transaction_state
|
87
427
|
@new_record
|
88
428
|
end
|
89
429
|
|
430
|
+
# Returns true if this object was just created -- that is, prior to the last
|
431
|
+
# save, the object didn't exist in the database and new_record? would have
|
432
|
+
# returned true.
|
433
|
+
def previously_new_record?
|
434
|
+
@previously_new_record
|
435
|
+
end
|
436
|
+
|
90
437
|
# Returns true if this object has been destroyed, otherwise returns false.
|
91
438
|
def destroyed?
|
92
|
-
sync_with_transaction_state
|
93
439
|
@destroyed
|
94
440
|
end
|
95
441
|
|
96
442
|
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
97
443
|
# not destroyed, otherwise returns false.
|
98
444
|
def persisted?
|
99
|
-
sync_with_transaction_state
|
100
445
|
!(@new_record || @destroyed)
|
101
446
|
end
|
102
447
|
|
103
448
|
##
|
104
449
|
# :call-seq:
|
105
|
-
# save(
|
450
|
+
# save(**options)
|
106
451
|
#
|
107
452
|
# Saves the model.
|
108
453
|
#
|
@@ -111,7 +456,7 @@ module ActiveRecord
|
|
111
456
|
#
|
112
457
|
# By default, save always runs validations. If any of them fail the action
|
113
458
|
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
|
114
|
-
# validate: false
|
459
|
+
# <tt>validate: false</tt>, validations are bypassed altogether. See
|
115
460
|
# ActiveRecord::Validations for more information.
|
116
461
|
#
|
117
462
|
# By default, #save also sets the +updated_at+/+updated_on+ attributes to
|
@@ -125,15 +470,15 @@ module ActiveRecord
|
|
125
470
|
#
|
126
471
|
# Attributes marked as readonly are silently ignored if the record is
|
127
472
|
# being updated.
|
128
|
-
def save(
|
129
|
-
create_or_update(
|
473
|
+
def save(**options, &block)
|
474
|
+
create_or_update(**options, &block)
|
130
475
|
rescue ActiveRecord::RecordInvalid
|
131
476
|
false
|
132
477
|
end
|
133
478
|
|
134
479
|
##
|
135
480
|
# :call-seq:
|
136
|
-
# save!(
|
481
|
+
# save!(**options)
|
137
482
|
#
|
138
483
|
# Saves the model.
|
139
484
|
#
|
@@ -142,7 +487,7 @@ module ActiveRecord
|
|
142
487
|
#
|
143
488
|
# By default, #save! always runs validations. If any of them fail
|
144
489
|
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
|
145
|
-
# validate: false
|
490
|
+
# <tt>validate: false</tt>, validations are bypassed altogether. See
|
146
491
|
# ActiveRecord::Validations for more information.
|
147
492
|
#
|
148
493
|
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
|
@@ -156,8 +501,10 @@ module ActiveRecord
|
|
156
501
|
#
|
157
502
|
# Attributes marked as readonly are silently ignored if the record is
|
158
503
|
# being updated.
|
159
|
-
|
160
|
-
|
504
|
+
#
|
505
|
+
# Unless an error is raised, returns true.
|
506
|
+
def save!(**options, &block)
|
507
|
+
create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
|
161
508
|
end
|
162
509
|
|
163
510
|
# Deletes the record in the database and freezes this instance to
|
@@ -171,9 +518,9 @@ module ActiveRecord
|
|
171
518
|
#
|
172
519
|
# To enforce the object's +before_destroy+ and +after_destroy+
|
173
520
|
# callbacks or any <tt>:dependent</tt> association
|
174
|
-
# options, use
|
521
|
+
# options, use #destroy.
|
175
522
|
def delete
|
176
|
-
|
523
|
+
_delete_row if persisted?
|
177
524
|
@destroyed = true
|
178
525
|
freeze
|
179
526
|
end
|
@@ -186,10 +533,13 @@ module ActiveRecord
|
|
186
533
|
# and #destroy returns +false+.
|
187
534
|
# See ActiveRecord::Callbacks for further details.
|
188
535
|
def destroy
|
189
|
-
|
536
|
+
_raise_readonly_record_error if readonly?
|
190
537
|
destroy_associations
|
191
|
-
|
192
|
-
|
538
|
+
@_trigger_destroy_callback = if persisted?
|
539
|
+
destroy_row > 0
|
540
|
+
else
|
541
|
+
true
|
542
|
+
end
|
193
543
|
@destroyed = true
|
194
544
|
freeze
|
195
545
|
end
|
@@ -218,13 +568,16 @@ module ActiveRecord
|
|
218
568
|
# Any change to the attributes on either instance will affect both instances.
|
219
569
|
# If you want to change the sti column as well, use #becomes! instead.
|
220
570
|
def becomes(klass)
|
221
|
-
became = klass.
|
222
|
-
|
223
|
-
became.
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
571
|
+
became = klass.allocate
|
572
|
+
|
573
|
+
became.send(:initialize) do |becoming|
|
574
|
+
becoming.instance_variable_set(:@attributes, @attributes)
|
575
|
+
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
576
|
+
becoming.instance_variable_set(:@new_record, new_record?)
|
577
|
+
becoming.instance_variable_set(:@destroyed, destroyed?)
|
578
|
+
becoming.errors.copy!(errors)
|
579
|
+
end
|
580
|
+
|
228
581
|
became
|
229
582
|
end
|
230
583
|
|
@@ -261,7 +614,7 @@ module ActiveRecord
|
|
261
614
|
verify_readonly_attribute(name)
|
262
615
|
public_send("#{name}=", value)
|
263
616
|
|
264
|
-
|
617
|
+
save(validate: false)
|
265
618
|
end
|
266
619
|
|
267
620
|
# Updates the attributes of the model from the passed-in hash and saves the
|
@@ -276,8 +629,6 @@ module ActiveRecord
|
|
276
629
|
end
|
277
630
|
end
|
278
631
|
|
279
|
-
alias update_attributes update
|
280
|
-
|
281
632
|
# Updates its receiver just like #update but calls #save! instead
|
282
633
|
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
283
634
|
def update!(attributes)
|
@@ -289,8 +640,6 @@ module ActiveRecord
|
|
289
640
|
end
|
290
641
|
end
|
291
642
|
|
292
|
-
alias update_attributes! update!
|
293
|
-
|
294
643
|
# Equivalent to <code>update_columns(name => value)</code>.
|
295
644
|
def update_column(name, value)
|
296
645
|
update_columns(name => value)
|
@@ -316,17 +665,23 @@ module ActiveRecord
|
|
316
665
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
317
666
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
318
667
|
|
319
|
-
attributes.
|
320
|
-
|
668
|
+
attributes = attributes.transform_keys do |key|
|
669
|
+
name = key.to_s
|
670
|
+
name = self.class.attribute_aliases[name] || name
|
671
|
+
verify_readonly_attribute(name) || name
|
321
672
|
end
|
322
673
|
|
323
|
-
|
324
|
-
|
674
|
+
id_in_database = self.id_in_database
|
325
675
|
attributes.each do |k, v|
|
326
|
-
|
676
|
+
write_attribute_without_type_cast(k, v)
|
327
677
|
end
|
328
678
|
|
329
|
-
|
679
|
+
affected_rows = self.class._update_record(
|
680
|
+
attributes,
|
681
|
+
@primary_key => id_in_database
|
682
|
+
)
|
683
|
+
|
684
|
+
affected_rows == 1
|
330
685
|
end
|
331
686
|
|
332
687
|
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
|
@@ -341,12 +696,14 @@ module ActiveRecord
|
|
341
696
|
# Wrapper around #increment that writes the update to the database.
|
342
697
|
# Only +attribute+ is updated; the record itself is not saved.
|
343
698
|
# This means that any other modified attributes will still be dirty.
|
344
|
-
# Validations and callbacks are skipped.
|
345
|
-
|
699
|
+
# Validations and callbacks are skipped. Supports the +touch+ option from
|
700
|
+
# +update_counters+, see that for more.
|
701
|
+
# Returns +self+.
|
702
|
+
def increment!(attribute, by = 1, touch: nil)
|
346
703
|
increment(attribute, by)
|
347
|
-
change = public_send(attribute) - (
|
348
|
-
self.class.update_counters(id, attribute => change)
|
349
|
-
|
704
|
+
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
|
705
|
+
self.class.update_counters(id, attribute => change, touch: touch)
|
706
|
+
public_send(:"clear_#{attribute}_change")
|
350
707
|
self
|
351
708
|
end
|
352
709
|
|
@@ -360,9 +717,11 @@ module ActiveRecord
|
|
360
717
|
# Wrapper around #decrement that writes the update to the database.
|
361
718
|
# Only +attribute+ is updated; the record itself is not saved.
|
362
719
|
# This means that any other modified attributes will still be dirty.
|
363
|
-
# Validations and callbacks are skipped.
|
364
|
-
|
365
|
-
|
720
|
+
# Validations and callbacks are skipped. Supports the +touch+ option from
|
721
|
+
# +update_counters+, see that for more.
|
722
|
+
# Returns +self+.
|
723
|
+
def decrement!(attribute, by = 1, touch: nil)
|
724
|
+
increment!(attribute, -by, touch: touch)
|
366
725
|
end
|
367
726
|
|
368
727
|
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
|
@@ -392,8 +751,8 @@ module ActiveRecord
|
|
392
751
|
|
393
752
|
# Reloads the record from the database.
|
394
753
|
#
|
395
|
-
# This method finds record by its primary key (which could be assigned
|
396
|
-
# modifies the receiver in-place:
|
754
|
+
# This method finds the record by its primary key (which could be assigned
|
755
|
+
# manually) and modifies the receiver in-place:
|
397
756
|
#
|
398
757
|
# account = Account.new
|
399
758
|
# # => #<Account id: nil, email: nil>
|
@@ -448,8 +807,9 @@ module ActiveRecord
|
|
448
807
|
self.class.unscoped { self.class.find(id) }
|
449
808
|
end
|
450
809
|
|
451
|
-
@attributes = fresh_object.instance_variable_get(
|
810
|
+
@attributes = fresh_object.instance_variable_get(:@attributes)
|
452
811
|
@new_record = false
|
812
|
+
@previously_new_record = false
|
453
813
|
self
|
454
814
|
end
|
455
815
|
|
@@ -488,91 +848,92 @@ module ActiveRecord
|
|
488
848
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
489
849
|
#
|
490
850
|
def touch(*names, time: nil)
|
491
|
-
unless persisted?
|
492
|
-
raise ActiveRecordError, <<-MSG.squish
|
493
|
-
cannot touch on a new or destroyed record object. Consider using
|
494
|
-
persisted?, new_record?, or destroyed? before touching
|
495
|
-
MSG
|
496
|
-
end
|
497
|
-
|
498
|
-
time ||= current_time_from_proper_timezone
|
499
|
-
attributes = timestamp_attributes_for_update_in_model
|
500
|
-
attributes.concat(names)
|
501
|
-
|
502
|
-
unless attributes.empty?
|
503
|
-
changes = {}
|
504
|
-
|
505
|
-
attributes.each do |column|
|
506
|
-
column = column.to_s
|
507
|
-
changes[column] = write_attribute(column, time)
|
508
|
-
end
|
509
|
-
|
510
|
-
clear_attribute_changes(changes.keys)
|
511
|
-
primary_key = self.class.primary_key
|
512
|
-
scope = self.class.unscoped.where(primary_key => _read_attribute(primary_key))
|
513
|
-
|
514
|
-
if locking_enabled?
|
515
|
-
locking_column = self.class.locking_column
|
516
|
-
scope = scope.where(locking_column => _read_attribute(locking_column))
|
517
|
-
changes[locking_column] = increment_lock
|
518
|
-
end
|
851
|
+
_raise_record_not_touched_error unless persisted?
|
519
852
|
|
520
|
-
|
853
|
+
attribute_names = timestamp_attributes_for_update_in_model
|
854
|
+
attribute_names |= names.map! do |name|
|
855
|
+
name = name.to_s
|
856
|
+
self.class.attribute_aliases[name] || name
|
857
|
+
end unless names.empty?
|
521
858
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
result
|
859
|
+
unless attribute_names.empty?
|
860
|
+
affected_rows = _touch_row(attribute_names, time)
|
861
|
+
@_trigger_update_callback = affected_rows == 1
|
527
862
|
else
|
528
863
|
true
|
529
864
|
end
|
530
865
|
end
|
531
866
|
|
532
867
|
private
|
533
|
-
|
534
868
|
# A hook to be overridden by association modules.
|
535
869
|
def destroy_associations
|
536
870
|
end
|
537
871
|
|
538
872
|
def destroy_row
|
539
|
-
|
873
|
+
_delete_row
|
874
|
+
end
|
875
|
+
|
876
|
+
def _delete_row
|
877
|
+
self.class._delete_record(@primary_key => id_in_database)
|
878
|
+
end
|
879
|
+
|
880
|
+
def _touch_row(attribute_names, time)
|
881
|
+
time ||= current_time_from_proper_timezone
|
882
|
+
|
883
|
+
attribute_names.each do |attr_name|
|
884
|
+
_write_attribute(attr_name, time)
|
885
|
+
end
|
886
|
+
|
887
|
+
_update_row(attribute_names, "touch")
|
540
888
|
end
|
541
889
|
|
542
|
-
def
|
543
|
-
self.class.
|
890
|
+
def _update_row(attribute_names, attempted_action = "update")
|
891
|
+
self.class._update_record(
|
892
|
+
attributes_with_values(attribute_names),
|
893
|
+
@primary_key => id_in_database
|
894
|
+
)
|
544
895
|
end
|
545
896
|
|
546
|
-
def create_or_update(
|
547
|
-
|
548
|
-
|
897
|
+
def create_or_update(**, &block)
|
898
|
+
_raise_readonly_record_error if readonly?
|
899
|
+
return false if destroyed?
|
900
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
549
901
|
result != false
|
550
902
|
end
|
551
903
|
|
552
904
|
# Updates the associated record with values matching those of the instance attributes.
|
553
905
|
# Returns the number of affected rows.
|
554
906
|
def _update_record(attribute_names = self.attribute_names)
|
555
|
-
|
556
|
-
|
557
|
-
|
907
|
+
attribute_names = attributes_for_update(attribute_names)
|
908
|
+
|
909
|
+
if attribute_names.empty?
|
910
|
+
affected_rows = 0
|
911
|
+
@_trigger_update_callback = true
|
558
912
|
else
|
559
|
-
|
913
|
+
affected_rows = _update_row(attribute_names)
|
914
|
+
@_trigger_update_callback = affected_rows == 1
|
560
915
|
end
|
561
916
|
|
917
|
+
@previously_new_record = false
|
918
|
+
|
562
919
|
yield(self) if block_given?
|
563
920
|
|
564
|
-
|
921
|
+
affected_rows
|
565
922
|
end
|
566
923
|
|
567
924
|
# Creates a record with values matching those of the instance attributes
|
568
925
|
# and returns its id.
|
569
926
|
def _create_record(attribute_names = self.attribute_names)
|
570
|
-
|
927
|
+
attribute_names = attributes_for_create(attribute_names)
|
928
|
+
|
929
|
+
new_id = self.class._insert_record(
|
930
|
+
attributes_with_values(attribute_names)
|
931
|
+
)
|
571
932
|
|
572
|
-
new_id
|
573
|
-
self.id ||= new_id if self.class.primary_key
|
933
|
+
self.id ||= new_id if @primary_key
|
574
934
|
|
575
935
|
@new_record = false
|
936
|
+
@previously_new_record = true
|
576
937
|
|
577
938
|
yield(self) if block_given?
|
578
939
|
|
@@ -580,7 +941,7 @@ module ActiveRecord
|
|
580
941
|
end
|
581
942
|
|
582
943
|
def verify_readonly_attribute(name)
|
583
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.
|
944
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
584
945
|
end
|
585
946
|
|
586
947
|
def _raise_record_not_destroyed
|
@@ -590,6 +951,19 @@ module ActiveRecord
|
|
590
951
|
@_association_destroy_exception = nil
|
591
952
|
end
|
592
953
|
|
954
|
+
def _raise_readonly_record_error
|
955
|
+
raise ReadOnlyRecord, "#{self.class} is marked as readonly"
|
956
|
+
end
|
957
|
+
|
958
|
+
def _raise_record_not_touched_error
|
959
|
+
raise ActiveRecordError, <<~MSG.squish
|
960
|
+
Cannot touch on a new or destroyed record object. Consider using
|
961
|
+
persisted?, new_record?, or destroyed? before touching.
|
962
|
+
MSG
|
963
|
+
end
|
964
|
+
|
965
|
+
# The name of the method used to touch a +belongs_to+ association when the
|
966
|
+
# +:touch+ option is used.
|
593
967
|
def belongs_to_touch_method
|
594
968
|
:touch
|
595
969
|
end
|