activerecord 4.2.0 → 6.1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1221 -796
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +45 -7
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +172 -67
- data/lib/active_record/associations/association_scope.rb +105 -129
- data/lib/active_record/associations/belongs_to_association.rb +85 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +168 -279
- data/lib/active_record/associations/collection_proxy.rb +263 -155
- data/lib/active_record/associations/foreign_association.rb +33 -0
- data/lib/active_record/associations/has_many_association.rb +57 -84
- data/lib/active_record/associations/has_many_through_association.rb +70 -82
- data/lib/active_record/associations/has_one_association.rb +74 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
- 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/join_dependency.rb +175 -164
- data/lib/active_record/associations/preloader/association.rb +107 -112
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +99 -96
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1845 -1597
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
- data/lib/active_record/attribute_methods/dirty.rb +168 -138
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -36
- data/lib/active_record/attribute_methods/write.rb +25 -56
- data/lib/active_record/attribute_methods.rb +153 -162
- data/lib/active_record/attributes.rb +234 -70
- data/lib/active_record/autosave_association.rb +157 -69
- data/lib/active_record/base.rb +49 -50
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +46 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
- data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
- data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +271 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +78 -161
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- 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 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- 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 +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +499 -293
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +595 -382
- data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -389
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +488 -243
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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/database_configurations.rb +273 -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 -106
- data/lib/active_record/enum.rb +212 -94
- data/lib/active_record/errors.rb +225 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +33 -14
- 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 +273 -496
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +64 -0
- data/lib/active_record/legacy_yaml_adapter.rb +52 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +103 -95
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +298 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +685 -309
- data/lib/active_record/model_schema.rb +420 -113
- data/lib/active_record/nested_attributes.rb +265 -216
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +574 -135
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +175 -54
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +533 -216
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +485 -310
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +326 -244
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +318 -256
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +99 -84
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +139 -96
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -409
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +23 -21
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -342
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -26
- data/lib/active_record/scoping/default.rb +96 -82
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +133 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +366 -129
- data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +291 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +181 -152
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +33 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +65 -48
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -28
- 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/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 +76 -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/nodes.rb +70 -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/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/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -10
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- 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.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +175 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,5 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/insert_all"
|
4
|
+
|
1
5
|
module ActiveRecord
|
2
|
-
# = Active Record Persistence
|
6
|
+
# = Active Record \Persistence
|
3
7
|
module Persistence
|
4
8
|
extend ActiveSupport::Concern
|
5
9
|
|
@@ -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
|
#
|
@@ -61,15 +251,158 @@ module ActiveRecord
|
|
61
251
|
# +instantiate+ instead of +new+, finder methods ensure they get new
|
62
252
|
# instances of the appropriate class for each record.
|
63
253
|
#
|
64
|
-
# See
|
254
|
+
# See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
|
65
255
|
# how this "single-table" inheritance mapping is implemented.
|
66
|
-
def instantiate(attributes, column_types = {})
|
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,68 +411,100 @@ 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
|
-
!(new_record
|
445
|
+
!(@new_record || @destroyed)
|
100
446
|
end
|
101
447
|
|
448
|
+
##
|
449
|
+
# :call-seq:
|
450
|
+
# save(**options)
|
451
|
+
#
|
102
452
|
# Saves the model.
|
103
453
|
#
|
104
|
-
# If the model is new a record gets created in the database, otherwise
|
454
|
+
# If the model is new, a record gets created in the database, otherwise
|
105
455
|
# the existing record gets updated.
|
106
456
|
#
|
107
|
-
# By default, save always
|
108
|
-
# is cancelled and
|
109
|
-
# validate: false
|
457
|
+
# By default, save always runs validations. If any of them fail the action
|
458
|
+
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
|
459
|
+
# <tt>validate: false</tt>, validations are bypassed altogether. See
|
110
460
|
# ActiveRecord::Validations for more information.
|
111
461
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
462
|
+
# By default, #save also sets the +updated_at+/+updated_on+ attributes to
|
463
|
+
# the current time. However, if you supply <tt>touch: false</tt>, these
|
464
|
+
# timestamps will not be updated.
|
465
|
+
#
|
466
|
+
# There's a series of callbacks associated with #save. If any of the
|
467
|
+
# <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled and
|
468
|
+
# #save returns +false+. See ActiveRecord::Callbacks for further
|
115
469
|
# details.
|
116
470
|
#
|
117
471
|
# Attributes marked as readonly are silently ignored if the record is
|
118
472
|
# being updated.
|
119
|
-
def save(
|
120
|
-
create_or_update
|
473
|
+
def save(**options, &block)
|
474
|
+
create_or_update(**options, &block)
|
121
475
|
rescue ActiveRecord::RecordInvalid
|
122
476
|
false
|
123
477
|
end
|
124
478
|
|
479
|
+
##
|
480
|
+
# :call-seq:
|
481
|
+
# save!(**options)
|
482
|
+
#
|
125
483
|
# Saves the model.
|
126
484
|
#
|
127
|
-
# If the model is new a record gets created in the database, otherwise
|
485
|
+
# If the model is new, a record gets created in the database, otherwise
|
128
486
|
# the existing record gets updated.
|
129
487
|
#
|
130
|
-
#
|
131
|
-
# ActiveRecord::RecordInvalid gets raised.
|
132
|
-
#
|
488
|
+
# By default, #save! always runs validations. If any of them fail
|
489
|
+
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
|
490
|
+
# <tt>validate: false</tt>, validations are bypassed altogether. See
|
491
|
+
# ActiveRecord::Validations for more information.
|
133
492
|
#
|
134
|
-
#
|
135
|
-
# the
|
136
|
-
#
|
493
|
+
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
|
494
|
+
# the current time. However, if you supply <tt>touch: false</tt>, these
|
495
|
+
# timestamps will not be updated.
|
496
|
+
#
|
497
|
+
# There's a series of callbacks associated with #save!. If any of
|
498
|
+
# the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
499
|
+
# and #save! raises ActiveRecord::RecordNotSaved. See
|
137
500
|
# ActiveRecord::Callbacks for further details.
|
138
501
|
#
|
139
502
|
# Attributes marked as readonly are silently ignored if the record is
|
140
503
|
# being updated.
|
141
|
-
|
142
|
-
|
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))
|
143
508
|
end
|
144
509
|
|
145
510
|
# Deletes the record in the database and freezes this instance to
|
@@ -149,11 +514,13 @@ module ActiveRecord
|
|
149
514
|
# The row is simply removed with an SQL +DELETE+ statement on the
|
150
515
|
# record's primary key, and no callbacks are executed.
|
151
516
|
#
|
517
|
+
# Note that this will also delete records marked as {#readonly?}[rdoc-ref:Core#readonly?].
|
518
|
+
#
|
152
519
|
# To enforce the object's +before_destroy+ and +after_destroy+
|
153
520
|
# callbacks or any <tt>:dependent</tt> association
|
154
|
-
# options, use
|
521
|
+
# options, use #destroy.
|
155
522
|
def delete
|
156
|
-
|
523
|
+
_delete_row if persisted?
|
157
524
|
@destroyed = true
|
158
525
|
freeze
|
159
526
|
end
|
@@ -161,14 +528,18 @@ module ActiveRecord
|
|
161
528
|
# Deletes the record in the database and freezes this instance to reflect
|
162
529
|
# that no changes should be made (since they can't be persisted).
|
163
530
|
#
|
164
|
-
# There's a series of callbacks associated with
|
165
|
-
#
|
166
|
-
# and
|
167
|
-
# ActiveRecord::Callbacks for further details.
|
531
|
+
# There's a series of callbacks associated with #destroy. If the
|
532
|
+
# <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
|
533
|
+
# and #destroy returns +false+.
|
534
|
+
# See ActiveRecord::Callbacks for further details.
|
168
535
|
def destroy
|
169
|
-
|
536
|
+
_raise_readonly_record_error if readonly?
|
170
537
|
destroy_associations
|
171
|
-
|
538
|
+
@_trigger_destroy_callback = if persisted?
|
539
|
+
destroy_row > 0
|
540
|
+
else
|
541
|
+
true
|
542
|
+
end
|
172
543
|
@destroyed = true
|
173
544
|
freeze
|
174
545
|
end
|
@@ -176,12 +547,12 @@ module ActiveRecord
|
|
176
547
|
# Deletes the record in the database and freezes this instance to reflect
|
177
548
|
# that no changes should be made (since they can't be persisted).
|
178
549
|
#
|
179
|
-
# There's a series of callbacks associated with
|
180
|
-
#
|
181
|
-
# and
|
182
|
-
# ActiveRecord::Callbacks for further details.
|
550
|
+
# There's a series of callbacks associated with #destroy!. If the
|
551
|
+
# <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
|
552
|
+
# and #destroy! raises ActiveRecord::RecordNotDestroyed.
|
553
|
+
# See ActiveRecord::Callbacks for further details.
|
183
554
|
def destroy!
|
184
|
-
destroy ||
|
555
|
+
destroy || _raise_record_not_destroyed
|
185
556
|
end
|
186
557
|
|
187
558
|
# Returns an instance of the specified +klass+ with the attributes of the
|
@@ -193,18 +564,24 @@ module ActiveRecord
|
|
193
564
|
# instance using the companies/company partial instead of clients/client.
|
194
565
|
#
|
195
566
|
# Note: The new instance will share a link to the same attributes as the original class.
|
196
|
-
#
|
567
|
+
# Therefore the sti column value will still be the same.
|
568
|
+
# Any change to the attributes on either instance will affect both instances.
|
569
|
+
# If you want to change the sti column as well, use #becomes! instead.
|
197
570
|
def becomes(klass)
|
198
|
-
became = klass.
|
199
|
-
|
200
|
-
became.
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
+
|
204
581
|
became
|
205
582
|
end
|
206
583
|
|
207
|
-
# Wrapper around
|
584
|
+
# Wrapper around #becomes that also changes the instance's sti column value.
|
208
585
|
# This is especially useful if you want to persist the changed class in your
|
209
586
|
# database.
|
210
587
|
#
|
@@ -224,18 +601,19 @@ module ActiveRecord
|
|
224
601
|
# This is especially useful for boolean flags on existing records. Also note that
|
225
602
|
#
|
226
603
|
# * Validation is skipped.
|
227
|
-
# * Callbacks are invoked.
|
604
|
+
# * \Callbacks are invoked.
|
228
605
|
# * updated_at/updated_on column is updated if that column is available.
|
229
606
|
# * Updates all the attributes that are dirty in this object.
|
230
607
|
#
|
231
|
-
# This method raises an
|
608
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
232
609
|
# attribute is marked as readonly.
|
233
610
|
#
|
234
|
-
#
|
611
|
+
# Also see #update_column.
|
235
612
|
def update_attribute(name, value)
|
236
613
|
name = name.to_s
|
237
614
|
verify_readonly_attribute(name)
|
238
|
-
|
615
|
+
public_send("#{name}=", value)
|
616
|
+
|
239
617
|
save(validate: false)
|
240
618
|
end
|
241
619
|
|
@@ -251,10 +629,8 @@ module ActiveRecord
|
|
251
629
|
end
|
252
630
|
end
|
253
631
|
|
254
|
-
|
255
|
-
|
256
|
-
# Updates its receiver just like +update+ but calls <tt>save!</tt> instead
|
257
|
-
# of +save+, so an exception is raised if the record is invalid.
|
632
|
+
# Updates its receiver just like #update but calls #save! instead
|
633
|
+
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
258
634
|
def update!(attributes)
|
259
635
|
# The following transaction covers any possible database side-effects of the
|
260
636
|
# attributes assignment. For example, setting the IDs of a child collection.
|
@@ -264,8 +640,6 @@ module ActiveRecord
|
|
264
640
|
end
|
265
641
|
end
|
266
642
|
|
267
|
-
alias update_attributes! update!
|
268
|
-
|
269
643
|
# Equivalent to <code>update_columns(name => value)</code>.
|
270
644
|
def update_column(name, value)
|
271
645
|
update_columns(name => value)
|
@@ -280,27 +654,34 @@ module ActiveRecord
|
|
280
654
|
# the database, but take into account that in consequence the regular update
|
281
655
|
# procedures are totally bypassed. In particular:
|
282
656
|
#
|
283
|
-
# * Validations are skipped.
|
284
|
-
# * Callbacks are skipped.
|
657
|
+
# * \Validations are skipped.
|
658
|
+
# * \Callbacks are skipped.
|
285
659
|
# * +updated_at+/+updated_on+ are not updated.
|
660
|
+
# * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
|
286
661
|
#
|
287
|
-
# This method raises an
|
662
|
+
# This method raises an ActiveRecord::ActiveRecordError when called on new
|
288
663
|
# objects, or when at least one of the attributes is marked as readonly.
|
289
664
|
def update_columns(attributes)
|
290
665
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
291
666
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
292
667
|
|
293
|
-
attributes.
|
294
|
-
|
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
|
295
672
|
end
|
296
673
|
|
297
|
-
|
298
|
-
|
674
|
+
id_in_database = self.id_in_database
|
299
675
|
attributes.each do |k, v|
|
300
|
-
|
676
|
+
write_attribute_without_type_cast(k, v)
|
301
677
|
end
|
302
678
|
|
303
|
-
|
679
|
+
affected_rows = self.class._update_record(
|
680
|
+
attributes,
|
681
|
+
@primary_key => id_in_database
|
682
|
+
)
|
683
|
+
|
684
|
+
affected_rows == 1
|
304
685
|
end
|
305
686
|
|
306
687
|
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
|
@@ -312,42 +693,56 @@ module ActiveRecord
|
|
312
693
|
self
|
313
694
|
end
|
314
695
|
|
315
|
-
# Wrapper around
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
|
320
|
-
|
696
|
+
# Wrapper around #increment that writes the update to the database.
|
697
|
+
# Only +attribute+ is updated; the record itself is not saved.
|
698
|
+
# This means that any other modified attributes will still be dirty.
|
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)
|
703
|
+
increment(attribute, by)
|
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")
|
707
|
+
self
|
321
708
|
end
|
322
709
|
|
323
710
|
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
|
324
711
|
# The decrement is performed directly on the underlying attribute, no setter is invoked.
|
325
712
|
# Only makes sense for number-based attributes. Returns +self+.
|
326
713
|
def decrement(attribute, by = 1)
|
327
|
-
|
328
|
-
self[attribute] -= by
|
329
|
-
self
|
714
|
+
increment(attribute, -by)
|
330
715
|
end
|
331
716
|
|
332
|
-
# Wrapper around
|
333
|
-
#
|
334
|
-
#
|
335
|
-
#
|
336
|
-
|
337
|
-
|
717
|
+
# Wrapper around #decrement that writes the update to the database.
|
718
|
+
# Only +attribute+ is updated; the record itself is not saved.
|
719
|
+
# This means that any other modified attributes will still be dirty.
|
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)
|
338
725
|
end
|
339
726
|
|
340
727
|
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
|
341
728
|
# if the predicate returns +true+ the attribute will become +false+. This
|
342
729
|
# method toggles directly the underlying value without calling any setter.
|
343
730
|
# Returns +self+.
|
731
|
+
#
|
732
|
+
# Example:
|
733
|
+
#
|
734
|
+
# user = User.first
|
735
|
+
# user.banned? # => false
|
736
|
+
# user.toggle(:banned)
|
737
|
+
# user.banned? # => true
|
738
|
+
#
|
344
739
|
def toggle(attribute)
|
345
|
-
self[attribute] = !
|
740
|
+
self[attribute] = !public_send("#{attribute}?")
|
346
741
|
self
|
347
742
|
end
|
348
743
|
|
349
|
-
# Wrapper around
|
350
|
-
# its non-bang version in that it passes through the attribute setter.
|
744
|
+
# Wrapper around #toggle that saves the record. This method differs from
|
745
|
+
# its non-bang version in the sense that it passes through the attribute setter.
|
351
746
|
# Saving is not subjected to validation checks. Returns +true+ if the
|
352
747
|
# record could be saved.
|
353
748
|
def toggle!(attribute)
|
@@ -356,8 +751,8 @@ module ActiveRecord
|
|
356
751
|
|
357
752
|
# Reloads the record from the database.
|
358
753
|
#
|
359
|
-
# This method finds record by its primary key (which could be assigned
|
360
|
-
# 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:
|
361
756
|
#
|
362
757
|
# account = Account.new
|
363
758
|
# # => #<Account id: nil, email: nil>
|
@@ -367,9 +762,9 @@ module ActiveRecord
|
|
367
762
|
# # => #<Account id: 1, email: 'account@example.com'>
|
368
763
|
#
|
369
764
|
# Attributes are reloaded from the database, and caches busted, in
|
370
|
-
# particular the associations cache.
|
765
|
+
# particular the associations cache and the QueryCache.
|
371
766
|
#
|
372
|
-
# If the record no longer exists in the database
|
767
|
+
# If the record no longer exists in the database ActiveRecord::RecordNotFound
|
373
768
|
# is raised. Otherwise, in addition to the in-place modification the method
|
374
769
|
# returns +self+ for convenience.
|
375
770
|
#
|
@@ -403,8 +798,7 @@ module ActiveRecord
|
|
403
798
|
# end
|
404
799
|
#
|
405
800
|
def reload(options = nil)
|
406
|
-
|
407
|
-
clear_association_cache
|
801
|
+
self.class.connection.clear_query_cache
|
408
802
|
|
409
803
|
fresh_object =
|
410
804
|
if options && options[:lock]
|
@@ -413,24 +807,28 @@ module ActiveRecord
|
|
413
807
|
self.class.unscoped { self.class.find(id) }
|
414
808
|
end
|
415
809
|
|
416
|
-
@attributes = fresh_object.instance_variable_get(
|
810
|
+
@attributes = fresh_object.instance_variable_get(:@attributes)
|
417
811
|
@new_record = false
|
812
|
+
@previously_new_record = false
|
418
813
|
self
|
419
814
|
end
|
420
815
|
|
421
|
-
# Saves the record with the updated_at/on attributes set to the current time
|
816
|
+
# Saves the record with the updated_at/on attributes set to the current time
|
817
|
+
# or the time specified.
|
422
818
|
# Please note that no validation is performed and only the +after_touch+,
|
423
819
|
# +after_commit+ and +after_rollback+ callbacks are executed.
|
424
820
|
#
|
821
|
+
# This method can be passed attribute names and an optional time argument.
|
425
822
|
# If attribute names are passed, they are updated along with updated_at/on
|
426
|
-
# attributes.
|
823
|
+
# attributes. If no time argument is passed, the current time is used as default.
|
427
824
|
#
|
428
|
-
# product.touch # updates updated_at/on
|
825
|
+
# product.touch # updates updated_at/on with current time
|
826
|
+
# product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
|
429
827
|
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
|
430
828
|
# product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
|
431
829
|
#
|
432
|
-
# If used along with
|
433
|
-
# associated object.
|
830
|
+
# If used along with {belongs_to}[rdoc-ref:Associations::ClassMethods#belongs_to]
|
831
|
+
# then +touch+ will invoke +touch+ method on associated object.
|
434
832
|
#
|
435
833
|
# class Brake < ActiveRecord::Base
|
436
834
|
# belongs_to :car, touch: true
|
@@ -449,84 +847,125 @@ module ActiveRecord
|
|
449
847
|
# ball = Ball.new
|
450
848
|
# ball.touch(:updated_at) # => raises ActiveRecordError
|
451
849
|
#
|
452
|
-
def touch(*names)
|
453
|
-
|
454
|
-
|
455
|
-
attributes = timestamp_attributes_for_update_in_model
|
456
|
-
attributes.concat(names)
|
457
|
-
|
458
|
-
unless attributes.empty?
|
459
|
-
current_time = current_time_from_proper_timezone
|
460
|
-
changes = {}
|
461
|
-
|
462
|
-
attributes.each do |column|
|
463
|
-
column = column.to_s
|
464
|
-
changes[column] = write_attribute(column, current_time)
|
465
|
-
end
|
850
|
+
def touch(*names, time: nil)
|
851
|
+
_raise_record_not_touched_error unless persisted?
|
466
852
|
|
467
|
-
|
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?
|
468
858
|
|
469
|
-
|
470
|
-
|
471
|
-
|
859
|
+
unless attribute_names.empty?
|
860
|
+
affected_rows = _touch_row(attribute_names, time)
|
861
|
+
@_trigger_update_callback = affected_rows == 1
|
472
862
|
else
|
473
863
|
true
|
474
864
|
end
|
475
865
|
end
|
476
866
|
|
477
867
|
private
|
478
|
-
|
479
868
|
# A hook to be overridden by association modules.
|
480
869
|
def destroy_associations
|
481
870
|
end
|
482
871
|
|
483
872
|
def destroy_row
|
484
|
-
|
873
|
+
_delete_row
|
874
|
+
end
|
875
|
+
|
876
|
+
def _delete_row
|
877
|
+
self.class._delete_record(@primary_key => id_in_database)
|
485
878
|
end
|
486
879
|
|
487
|
-
def
|
488
|
-
|
489
|
-
column = self.class.columns_hash[pk]
|
490
|
-
substitute = self.class.connection.substitute_at(column)
|
880
|
+
def _touch_row(attribute_names, time)
|
881
|
+
time ||= current_time_from_proper_timezone
|
491
882
|
|
492
|
-
|
493
|
-
|
883
|
+
attribute_names.each do |attr_name|
|
884
|
+
_write_attribute(attr_name, time)
|
885
|
+
end
|
886
|
+
|
887
|
+
_update_row(attribute_names, "touch")
|
888
|
+
end
|
494
889
|
|
495
|
-
|
496
|
-
|
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
|
+
)
|
497
895
|
end
|
498
896
|
|
499
|
-
def create_or_update
|
500
|
-
|
501
|
-
|
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)
|
502
901
|
result != false
|
503
902
|
end
|
504
903
|
|
505
904
|
# Updates the associated record with values matching those of the instance attributes.
|
506
905
|
# Returns the number of affected rows.
|
507
906
|
def _update_record(attribute_names = self.attribute_names)
|
508
|
-
|
509
|
-
|
510
|
-
|
907
|
+
attribute_names = attributes_for_update(attribute_names)
|
908
|
+
|
909
|
+
if attribute_names.empty?
|
910
|
+
affected_rows = 0
|
911
|
+
@_trigger_update_callback = true
|
511
912
|
else
|
512
|
-
|
913
|
+
affected_rows = _update_row(attribute_names)
|
914
|
+
@_trigger_update_callback = affected_rows == 1
|
513
915
|
end
|
916
|
+
|
917
|
+
@previously_new_record = false
|
918
|
+
|
919
|
+
yield(self) if block_given?
|
920
|
+
|
921
|
+
affected_rows
|
514
922
|
end
|
515
923
|
|
516
924
|
# Creates a record with values matching those of the instance attributes
|
517
925
|
# and returns its id.
|
518
926
|
def _create_record(attribute_names = self.attribute_names)
|
519
|
-
|
927
|
+
attribute_names = attributes_for_create(attribute_names)
|
928
|
+
|
929
|
+
new_id = self.class._insert_record(
|
930
|
+
attributes_with_values(attribute_names)
|
931
|
+
)
|
520
932
|
|
521
|
-
new_id
|
522
|
-
self.id ||= new_id if self.class.primary_key
|
933
|
+
self.id ||= new_id if @primary_key
|
523
934
|
|
524
935
|
@new_record = false
|
936
|
+
@previously_new_record = true
|
937
|
+
|
938
|
+
yield(self) if block_given?
|
939
|
+
|
525
940
|
id
|
526
941
|
end
|
527
942
|
|
528
943
|
def verify_readonly_attribute(name)
|
529
|
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.
|
944
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
|
945
|
+
end
|
946
|
+
|
947
|
+
def _raise_record_not_destroyed
|
948
|
+
@_association_destroy_exception ||= nil
|
949
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy the record", self)
|
950
|
+
ensure
|
951
|
+
@_association_destroy_exception = nil
|
952
|
+
end
|
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.
|
967
|
+
def belongs_to_touch_method
|
968
|
+
:touch
|
530
969
|
end
|
531
970
|
end
|
532
971
|
end
|