activerecord 6.1.7 → 7.1.0
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 +4 -4
- data/CHANGELOG.md +1516 -1019
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +423 -289
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +61 -14
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +194 -224
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +156 -62
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +133 -20
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +117 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +108 -13
- data/lib/active_record/migration/compatibility.rb +221 -48
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +355 -171
- data/lib/active_record/model_schema.rb +116 -97
- data/lib/active_record/nested_attributes.rb +36 -15
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +185 -249
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +229 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +211 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +116 -96
- data/lib/active_record/timestamp.rb +28 -17
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +92 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module AttributeMethods
|
5
|
+
# = Active Record Attribute Methods \Serialization
|
5
6
|
module Serialization
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
@@ -15,16 +16,18 @@ module ActiveRecord
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
included do
|
20
|
+
class_attribute :default_column_serializer, instance_accessor: false, default: Coders::YAMLColumn
|
21
|
+
end
|
22
|
+
|
18
23
|
module ClassMethods
|
19
|
-
# If you have an attribute that needs to be saved to the database as
|
20
|
-
# object, and retrieved
|
21
|
-
# attribute using this method and
|
22
|
-
#
|
23
|
-
# serialized object must be of that class on assignment and retrieval.
|
24
|
-
# Otherwise SerializationTypeMismatch will be raised.
|
24
|
+
# If you have an attribute that needs to be saved to the database as a
|
25
|
+
# serialized object, and retrieved by deserializing into the same object,
|
26
|
+
# then specify the name of that attribute using this method and serialization
|
27
|
+
# will be handled automatically.
|
25
28
|
#
|
26
|
-
#
|
27
|
-
#
|
29
|
+
# The serialization format may be YAML, JSON, or any custom format using a
|
30
|
+
# custom coder class.
|
28
31
|
#
|
29
32
|
# Keep in mind that database adapters handle certain serialization tasks
|
30
33
|
# for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
|
@@ -37,57 +40,211 @@ module ActiveRecord
|
|
37
40
|
#
|
38
41
|
# ==== Parameters
|
39
42
|
#
|
40
|
-
# * +attr_name+ - The
|
41
|
-
# * +
|
42
|
-
#
|
43
|
+
# * +attr_name+ - The name of the attribute to serialize.
|
44
|
+
# * +coder+ The serializer implementation to use, e.g. +JSON+.
|
45
|
+
# * The attribute value will be serialized
|
46
|
+
# using the coder's <tt>dump(value)</tt> method, and will be
|
47
|
+
# deserialized using the coder's <tt>load(string)</tt> method. The
|
48
|
+
# +dump+ method may return +nil+ to serialize the value as +NULL+.
|
49
|
+
# * +type+ - Optional. What the type of the serialized object should be.
|
50
|
+
# * Attempting to serialize another type will raise an
|
51
|
+
# ActiveRecord::SerializationTypeMismatch error.
|
52
|
+
# * If the column is +NULL+ or starting from a new record, the default value
|
53
|
+
# will set to +type.new+
|
54
|
+
# * +yaml+ - Optional. Yaml specific options. The allowed config is:
|
55
|
+
# * +:permitted_classes+ - +Array+ with the permitted classes.
|
56
|
+
# * +:unsafe_load+ - Unsafely load YAML blobs, allow YAML to load any class.
|
43
57
|
#
|
44
58
|
# ==== Options
|
45
59
|
#
|
46
|
-
#
|
47
|
-
# is not passed, the previous default value (if any) will
|
48
|
-
# Otherwise, the default will be +nil+.
|
60
|
+
# * +:default+ - The default value to use when no value is provided. If
|
61
|
+
# this option is not passed, the previous default value (if any) will
|
62
|
+
# be used. Otherwise, the default will be +nil+.
|
63
|
+
#
|
64
|
+
# ==== Choosing a serializer
|
65
|
+
#
|
66
|
+
# While any serialization format can be used, it is recommended to carefully
|
67
|
+
# evaluate the properties of a serializer before using it, as migrating to
|
68
|
+
# another format later on can be difficult.
|
69
|
+
#
|
70
|
+
# ===== Avoid accepting arbitrary types
|
71
|
+
#
|
72
|
+
# When serializing data in a column, it is heavily recommended to make sure
|
73
|
+
# only expected types will be serialized. For instance some serializer like
|
74
|
+
# +Marshal+ or +YAML+ are capable of serializing almost any Ruby object.
|
75
|
+
#
|
76
|
+
# This can lead to unexpected types being serialized, and it is important
|
77
|
+
# that type serialization remains backward and forward compatible as long
|
78
|
+
# as some database records still contain these serialized types.
|
49
79
|
#
|
50
|
-
#
|
80
|
+
# class Address
|
81
|
+
# def initialize(line, city, country)
|
82
|
+
# @line, @city, @country = line, city, country
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# In the above example, if any of the +Address+ attributes is renamed,
|
87
|
+
# instances that were persisted before the change will be loaded with the
|
88
|
+
# old attributes. This problem is even worse when the serialized type comes
|
89
|
+
# from a dependency which doesn't expect to be serialized this way and may
|
90
|
+
# change its internal representation without notice.
|
91
|
+
#
|
92
|
+
# As such, it is heavily recommended to instead convert these objects into
|
93
|
+
# primitives of the serialization format, for example:
|
94
|
+
#
|
95
|
+
# class Address
|
96
|
+
# attr_reader :line, :city, :country
|
97
|
+
#
|
98
|
+
# def self.load(payload)
|
99
|
+
# data = YAML.safe_load(payload)
|
100
|
+
# new(data["line"], data["city"], data["country"])
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# def self.dump(address)
|
104
|
+
# YAML.safe_dump(
|
105
|
+
# "line" => address.line,
|
106
|
+
# "city" => address.city,
|
107
|
+
# "country" => address.country,
|
108
|
+
# )
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# def initialize(line, city, country)
|
112
|
+
# @line, @city, @country = line, city, country
|
113
|
+
# end
|
114
|
+
# end
|
51
115
|
#
|
52
|
-
# # Serialize a preferences attribute.
|
53
116
|
# class User < ActiveRecord::Base
|
54
|
-
# serialize :
|
117
|
+
# serialize :address, coder: Address
|
55
118
|
# end
|
56
119
|
#
|
57
|
-
#
|
120
|
+
# This pattern allows to be more deliberate about what is serialized, and
|
121
|
+
# to evolve the format in a backward compatible way.
|
122
|
+
#
|
123
|
+
# ===== Ensure serialization stability
|
124
|
+
#
|
125
|
+
# Some serialization methods may accept some types they don't support by
|
126
|
+
# silently casting them to other types. This can cause bugs when the
|
127
|
+
# data is deserialized.
|
128
|
+
#
|
129
|
+
# For instance the +JSON+ serializer provided in the standard library will
|
130
|
+
# silently cast unsupported types to +String+:
|
131
|
+
#
|
132
|
+
# >> JSON.parse(JSON.dump(Struct.new(:foo)))
|
133
|
+
# => "#<Class:0x000000013090b4c0>"
|
134
|
+
#
|
135
|
+
# ==== Examples
|
136
|
+
#
|
137
|
+
# ===== Serialize the +preferences+ attribute using YAML
|
138
|
+
#
|
139
|
+
# class User < ActiveRecord::Base
|
140
|
+
# serialize :preferences, coder: YAML
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# ===== Serialize the +preferences+ attribute using JSON
|
144
|
+
#
|
58
145
|
# class User < ActiveRecord::Base
|
59
|
-
# serialize :preferences, JSON
|
146
|
+
# serialize :preferences, coder: JSON
|
60
147
|
# end
|
61
148
|
#
|
62
|
-
#
|
149
|
+
# ===== Serialize the +preferences+ +Hash+ using YAML
|
150
|
+
#
|
63
151
|
# class User < ActiveRecord::Base
|
64
|
-
# serialize :preferences, Hash
|
152
|
+
# serialize :preferences, type: Hash, coder: YAML
|
65
153
|
# end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
154
|
+
#
|
155
|
+
# ===== Serializes +preferences+ to YAML, permitting select classes
|
156
|
+
#
|
157
|
+
# class User < ActiveRecord::Base
|
158
|
+
# serialize :preferences, coder: YAML, yaml: { permitted_classes: [Symbol, Time] }
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# ===== Serialize the +preferences+ attribute using a custom coder
|
162
|
+
#
|
163
|
+
# class Rot13JSON
|
164
|
+
# def self.rot13(string)
|
165
|
+
# string.tr("a-zA-Z", "n-za-mN-ZA-M")
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# # Serializes an attribute value to a string that will be stored in the database.
|
169
|
+
# def self.dump(value)
|
170
|
+
# rot13(ActiveSupport::JSON.dump(value))
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# # Deserializes a string from the database to an attribute value.
|
174
|
+
# def self.load(string)
|
175
|
+
# ActiveSupport::JSON.load(rot13(string))
|
176
|
+
# end
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# class User < ActiveRecord::Base
|
180
|
+
# serialize :preferences, coder: Rot13JSON
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
def serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options)
|
184
|
+
unless class_name_or_coder.nil?
|
185
|
+
if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
186
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
187
|
+
Passing the coder as positional argument is deprecated and will be removed in Rails 7.2.
|
188
|
+
|
189
|
+
Please pass the coder as a keyword argument:
|
190
|
+
|
191
|
+
serialize #{attr_name.inspect}, coder: #{class_name_or_coder}
|
192
|
+
MSG
|
193
|
+
coder = class_name_or_coder
|
194
|
+
else
|
195
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
196
|
+
Passing the class as positional argument is deprecated and will be removed in Rails 7.2.
|
197
|
+
|
198
|
+
Please pass the class as a keyword argument:
|
199
|
+
|
200
|
+
serialize #{attr_name.inspect}, type: #{class_name_or_coder.name}
|
201
|
+
MSG
|
202
|
+
type = class_name_or_coder
|
203
|
+
end
|
76
204
|
end
|
77
205
|
|
78
|
-
|
79
|
-
|
206
|
+
coder ||= default_column_serializer
|
207
|
+
unless coder
|
208
|
+
raise ArgumentError, <<~MSG.squish
|
209
|
+
missing keyword: :coder
|
210
|
+
|
211
|
+
If no default coder is configured, a coder must be provided to `serialize`.
|
212
|
+
MSG
|
213
|
+
end
|
214
|
+
|
215
|
+
column_serializer = build_column_serializer(attr_name, coder, type, yaml)
|
216
|
+
|
217
|
+
attribute(attr_name, **options) do |cast_type|
|
218
|
+
if type_incompatible_with_serialize?(cast_type, coder, type)
|
80
219
|
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
81
220
|
end
|
82
221
|
|
83
|
-
Type::Serialized
|
222
|
+
cast_type = cast_type.subtype if Type::Serialized === cast_type
|
223
|
+
Type::Serialized.new(cast_type, column_serializer)
|
84
224
|
end
|
85
225
|
end
|
86
226
|
|
87
227
|
private
|
88
|
-
def
|
89
|
-
|
90
|
-
|
228
|
+
def build_column_serializer(attr_name, coder, type, yaml = nil)
|
229
|
+
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
230
|
+
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
231
|
+
# using the #as_json hook.
|
232
|
+
coder = Coders::JSON if coder == ::JSON
|
233
|
+
|
234
|
+
if coder == ::YAML || coder == Coders::YAMLColumn
|
235
|
+
Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
|
236
|
+
elsif coder.respond_to?(:new) && !coder.respond_to?(:load)
|
237
|
+
coder.new(attr_name, type)
|
238
|
+
elsif type && type != Object
|
239
|
+
Coders::ColumnSerializer.new(attr_name, coder, type)
|
240
|
+
else
|
241
|
+
coder
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def type_incompatible_with_serialize?(cast_type, coder, type)
|
246
|
+
cast_type.is_a?(ActiveRecord::Type::Json) && coder == ::JSON ||
|
247
|
+
cast_type.respond_to?(:type_cast_array, true) && type == ::Array
|
91
248
|
end
|
92
249
|
end
|
93
250
|
end
|
@@ -25,6 +25,8 @@ module ActiveRecord
|
|
25
25
|
rescue ArgumentError
|
26
26
|
nil
|
27
27
|
end
|
28
|
+
elsif value.respond_to?(:infinite?) && value.infinite?
|
29
|
+
value
|
28
30
|
else
|
29
31
|
map_avoiding_infinite_recursion(super) { |v| cast(v) }
|
30
32
|
end
|
@@ -36,7 +38,7 @@ module ActiveRecord
|
|
36
38
|
|
37
39
|
if value.acts_like?(:time)
|
38
40
|
value.in_time_zone
|
39
|
-
elsif value.
|
41
|
+
elsif value.respond_to?(:infinite?) && value.infinite?
|
40
42
|
value
|
41
43
|
else
|
42
44
|
map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
|
@@ -61,8 +63,7 @@ module ActiveRecord
|
|
61
63
|
extend ActiveSupport::Concern
|
62
64
|
|
63
65
|
included do
|
64
|
-
|
65
|
-
|
66
|
+
class_attribute :time_zone_aware_attributes, instance_writer: false, default: false
|
66
67
|
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
|
67
68
|
class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
|
68
69
|
end
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module AttributeMethods
|
5
|
+
# = Active Record Attribute Methods \Write
|
5
6
|
module Write
|
6
7
|
extend ActiveSupport::Concern
|
7
8
|
|
8
9
|
included do
|
9
|
-
attribute_method_suffix "="
|
10
|
+
attribute_method_suffix "=", parameters: "value"
|
10
11
|
end
|
11
12
|
|
12
13
|
module ClassMethods # :nodoc:
|
@@ -15,17 +16,18 @@ module ActiveRecord
|
|
15
16
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
16
17
|
owner, name, writer: true,
|
17
18
|
) do |temp_method_name, attr_name_expr|
|
18
|
-
owner
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_record) do |batch|
|
20
|
+
batch <<
|
21
|
+
"def #{temp_method_name}(value)" <<
|
22
|
+
" _write_attribute(#{attr_name_expr}, value)" <<
|
23
|
+
"end"
|
24
|
+
end
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
|
-
# Updates the attribute identified by
|
27
|
-
#
|
28
|
-
# turned into +nil+.
|
29
|
+
# Updates the attribute identified by +attr_name+ using the specified
|
30
|
+
# +value+. The attribute value will be type cast upon being read.
|
29
31
|
def write_attribute(attr_name, value)
|
30
32
|
name = attr_name.to_s
|
31
33
|
name = self.class.attribute_aliases[name] || name
|
@@ -42,11 +44,6 @@ module ActiveRecord
|
|
42
44
|
|
43
45
|
alias :attribute= :_write_attribute
|
44
46
|
private :attribute=
|
45
|
-
|
46
|
-
private
|
47
|
-
def write_attribute_without_type_cast(attr_name, value)
|
48
|
-
@attributes.write_cast_value(attr_name, value)
|
49
|
-
end
|
50
47
|
end
|
51
48
|
end
|
52
49
|
end
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
25
25
|
|
26
|
-
class GeneratedAttributeMethods < Module
|
26
|
+
class GeneratedAttributeMethods < Module # :nodoc:
|
27
27
|
include Mutex_m
|
28
28
|
end
|
29
29
|
|
@@ -33,26 +33,94 @@ module ActiveRecord
|
|
33
33
|
Base.instance_methods +
|
34
34
|
Base.private_instance_methods -
|
35
35
|
Base.superclass.instance_methods -
|
36
|
-
Base.superclass.private_instance_methods
|
36
|
+
Base.superclass.private_instance_methods +
|
37
|
+
%i[__id__ dup freeze frozen? hash object_id class clone]
|
37
38
|
).map { |m| -m.to_s }.to_set.freeze
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
module ClassMethods
|
42
|
-
def inherited(child_class) #:nodoc:
|
43
|
-
child_class.initialize_generated_modules
|
44
|
-
super
|
45
|
-
end
|
46
|
-
|
47
43
|
def initialize_generated_modules # :nodoc:
|
48
44
|
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
49
45
|
private_constant :GeneratedAttributeMethods
|
50
46
|
@attribute_methods_generated = false
|
47
|
+
@alias_attributes_mass_generated = false
|
51
48
|
include @generated_attribute_methods
|
52
49
|
|
53
50
|
super
|
54
51
|
end
|
55
52
|
|
53
|
+
def alias_attribute(new_name, old_name)
|
54
|
+
super
|
55
|
+
|
56
|
+
if @alias_attributes_mass_generated
|
57
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
58
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
|
64
|
+
# alias attributes in Active Record are lazily generated
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_alias_attributes # :nodoc:
|
68
|
+
superclass.generate_alias_attributes unless superclass == Base
|
69
|
+
return if @alias_attributes_mass_generated
|
70
|
+
|
71
|
+
generated_attribute_methods.synchronize do
|
72
|
+
return if @alias_attributes_mass_generated
|
73
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
74
|
+
aliases_by_attribute_name.each do |old_name, new_names|
|
75
|
+
new_names.each do |new_name|
|
76
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@alias_attributes_mass_generated = true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
86
|
+
method_name = pattern.method_name(new_name).to_s
|
87
|
+
target_name = pattern.method_name(old_name).to_s
|
88
|
+
parameters = pattern.parameters
|
89
|
+
old_name = old_name.to_s
|
90
|
+
|
91
|
+
method_defined = method_defined?(target_name) || private_method_defined?(target_name)
|
92
|
+
manually_defined = method_defined &&
|
93
|
+
!self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
|
94
|
+
reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
|
95
|
+
|
96
|
+
if !abstract_class? && !has_attribute?(old_name)
|
97
|
+
# We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
|
98
|
+
should_warn = target_name == old_name
|
99
|
+
if should_warn
|
100
|
+
ActiveRecord.deprecator.warn(
|
101
|
+
"#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
102
|
+
"Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
|
103
|
+
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
104
|
+
)
|
105
|
+
end
|
106
|
+
super
|
107
|
+
elsif manually_defined && !reserved_method_name
|
108
|
+
aliased_method_redefined_as_well = method_defined_within?(method_name, self)
|
109
|
+
return if aliased_method_redefined_as_well
|
110
|
+
|
111
|
+
ActiveRecord.deprecator.warn(
|
112
|
+
"#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
|
113
|
+
"Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
|
114
|
+
"You may want to additionally define `#{method_name}` to preserve the current behavior."
|
115
|
+
)
|
116
|
+
super
|
117
|
+
else
|
118
|
+
define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
|
119
|
+
namespace: :proxy_alias_attribute
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
56
124
|
# Generates all the attribute related methods for columns in the database
|
57
125
|
# accessors, mutators and query methods.
|
58
126
|
def define_attribute_methods # :nodoc:
|
@@ -71,6 +139,7 @@ module ActiveRecord
|
|
71
139
|
generated_attribute_methods.synchronize do
|
72
140
|
super if defined?(@attribute_methods_generated) && @attribute_methods_generated
|
73
141
|
@attribute_methods_generated = false
|
142
|
+
@alias_attributes_mass_generated = false
|
74
143
|
end
|
75
144
|
end
|
76
145
|
|
@@ -97,7 +166,7 @@ module ActiveRecord
|
|
97
166
|
super
|
98
167
|
else
|
99
168
|
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
|
100
|
-
# defines its own attribute method, then we don't want to
|
169
|
+
# defines its own attribute method, then we don't want to override that.
|
101
170
|
defined = method_defined_within?(method_name, superclass, Base) &&
|
102
171
|
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
|
103
172
|
defined || super
|
@@ -186,6 +255,16 @@ module ActiveRecord
|
|
186
255
|
def _has_attribute?(attr_name) # :nodoc:
|
187
256
|
attribute_types.key?(attr_name)
|
188
257
|
end
|
258
|
+
|
259
|
+
private
|
260
|
+
def inherited(child_class)
|
261
|
+
super
|
262
|
+
child_class.initialize_generated_modules
|
263
|
+
child_class.class_eval do
|
264
|
+
@alias_attributes_mass_generated = false
|
265
|
+
@attribute_names = nil
|
266
|
+
end
|
267
|
+
end
|
189
268
|
end
|
190
269
|
|
191
270
|
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
@@ -267,9 +346,8 @@ module ActiveRecord
|
|
267
346
|
|
268
347
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
269
348
|
# attribute +attr_name+. String attributes are truncated up to 50
|
270
|
-
# characters
|
271
|
-
#
|
272
|
-
# <tt>#inspect</tt> without modification.
|
349
|
+
# characters. Other attributes return the value of <tt>#inspect</tt>
|
350
|
+
# without modification.
|
273
351
|
#
|
274
352
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
275
353
|
#
|
@@ -277,7 +355,7 @@ module ActiveRecord
|
|
277
355
|
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
|
278
356
|
#
|
279
357
|
# person.attribute_for_inspect(:created_at)
|
280
|
-
# # => "\"2012-10-22 00:15:07\""
|
358
|
+
# # => "\"2012-10-22 00:15:07.000000000 +0000\""
|
281
359
|
#
|
282
360
|
# person.attribute_for_inspect(:tag_ids)
|
283
361
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
@@ -310,37 +388,40 @@ module ActiveRecord
|
|
310
388
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
311
389
|
end
|
312
390
|
|
313
|
-
# Returns the value of the attribute identified by
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
# Note: +:id+ is always present.
|
391
|
+
# Returns the value of the attribute identified by +attr_name+ after it has
|
392
|
+
# been type cast. (For information about specific type casting behavior, see
|
393
|
+
# the types under ActiveModel::Type.)
|
318
394
|
#
|
319
395
|
# class Person < ActiveRecord::Base
|
320
396
|
# belongs_to :organization
|
321
397
|
# end
|
322
398
|
#
|
323
|
-
# person = Person.new(name:
|
324
|
-
# person[:name]
|
325
|
-
# person[:
|
399
|
+
# person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
|
400
|
+
# person[:name] # => "Francesco"
|
401
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
402
|
+
# person[:organization_id] # => nil
|
326
403
|
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
404
|
+
# Raises ActiveModel::MissingAttributeError if the attribute is missing.
|
405
|
+
# Note, however, that the +id+ attribute will never be considered missing.
|
406
|
+
#
|
407
|
+
# person = Person.select(:name).first
|
408
|
+
# person[:name] # => "Francesco"
|
409
|
+
# person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
|
410
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
|
411
|
+
# person[:id] # => nil
|
330
412
|
def [](attr_name)
|
331
413
|
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
332
414
|
end
|
333
415
|
|
334
|
-
# Updates the attribute identified by
|
335
|
-
#
|
416
|
+
# Updates the attribute identified by +attr_name+ using the specified
|
417
|
+
# +value+. The attribute value will be type cast upon being read.
|
336
418
|
#
|
337
419
|
# class Person < ActiveRecord::Base
|
338
420
|
# end
|
339
421
|
#
|
340
422
|
# person = Person.new
|
341
|
-
# person[:
|
342
|
-
# person[:
|
343
|
-
# person[:age].class # => Integer
|
423
|
+
# person[:date_of_birth] = "2004-12-12"
|
424
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
344
425
|
def []=(attr_name, value)
|
345
426
|
write_attribute(attr_name, value)
|
346
427
|
end
|
@@ -361,10 +442,9 @@ module ActiveRecord
|
|
361
442
|
# end
|
362
443
|
#
|
363
444
|
# private
|
364
|
-
#
|
365
|
-
#
|
366
|
-
#
|
367
|
-
# end
|
445
|
+
# def print_accessed_fields
|
446
|
+
# p @posts.first.accessed_fields
|
447
|
+
# end
|
368
448
|
# end
|
369
449
|
#
|
370
450
|
# Which allows you to quickly change your code to:
|
@@ -385,25 +465,26 @@ module ActiveRecord
|
|
385
465
|
end
|
386
466
|
|
387
467
|
def attributes_with_values(attribute_names)
|
388
|
-
attribute_names.index_with
|
389
|
-
_read_attribute(name)
|
390
|
-
end
|
468
|
+
attribute_names.index_with { |name| @attributes[name] }
|
391
469
|
end
|
392
470
|
|
393
|
-
# Filters the primary keys
|
471
|
+
# Filters the primary keys, readonly attributes and virtual columns from the attribute names.
|
394
472
|
def attributes_for_update(attribute_names)
|
395
473
|
attribute_names &= self.class.column_names
|
396
474
|
attribute_names.delete_if do |name|
|
397
|
-
self.class.readonly_attribute?(name)
|
475
|
+
self.class.readonly_attribute?(name) ||
|
476
|
+
self.class.counter_cache_column?(name) ||
|
477
|
+
column_for_attribute(name).virtual?
|
398
478
|
end
|
399
479
|
end
|
400
480
|
|
401
|
-
# Filters out the primary keys, from the attribute names, when the primary
|
481
|
+
# Filters out the virtual columns and also primary keys, from the attribute names, when the primary
|
402
482
|
# key is to be generated (e.g. the id attribute has no value).
|
403
483
|
def attributes_for_create(attribute_names)
|
404
484
|
attribute_names &= self.class.column_names
|
405
485
|
attribute_names.delete_if do |name|
|
406
|
-
pk_attribute?(name) && id.nil?
|
486
|
+
(pk_attribute?(name) && id.nil?) ||
|
487
|
+
column_for_attribute(name).virtual?
|
407
488
|
end
|
408
489
|
end
|
409
490
|
|
@@ -414,7 +495,7 @@ module ActiveRecord
|
|
414
495
|
inspected_value = if value.is_a?(String) && value.length > 50
|
415
496
|
"#{value[0, 50]}...".inspect
|
416
497
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
417
|
-
%("#{value.
|
498
|
+
%("#{value.to_fs(:inspect)}")
|
418
499
|
else
|
419
500
|
value.inspect
|
420
501
|
end
|