activerecord 6.1.3.2 → 7.0.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +734 -1058
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +35 -7
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +16 -6
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- 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 +2 -2
- data/lib/active_record/associations/collection_association.rb +24 -25
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -49
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +11 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- 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 +1 -1
- 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 +28 -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 +6 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
- data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +8 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +129 -117
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +18 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -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/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -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 +80 -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 +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +44 -46
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +39 -6
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +46 -32
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +83 -58
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +42 -25
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +32 -23
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +233 -50
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +22 -15
- data/lib/active_record/relation.rb +170 -87
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +62 -15
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +107 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +45 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/composite.rb +3 -3
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/homogeneous_in.rb +4 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +3 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +44 -3
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- 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
- metadata +55 -16
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
def execute_callstack_for_multiparameter_attributes(callstack)
|
47
47
|
errors = []
|
48
48
|
callstack.each do |name, values_with_empty_parameters|
|
49
|
-
if values_with_empty_parameters.each_value.all?(
|
49
|
+
if values_with_empty_parameters.each_value.all?(NilClass)
|
50
50
|
values = nil
|
51
51
|
else
|
52
52
|
values = values_with_empty_parameters
|
@@ -29,8 +29,8 @@ module ActiveRecord
|
|
29
29
|
extend ActiveSupport::Concern
|
30
30
|
|
31
31
|
included do
|
32
|
-
attribute_method_suffix "_before_type_cast", "_for_database"
|
33
|
-
attribute_method_suffix "_came_from_user?"
|
32
|
+
attribute_method_suffix "_before_type_cast", "_for_database", parameters: false
|
33
|
+
attribute_method_suffix "_came_from_user?", parameters: false
|
34
34
|
end
|
35
35
|
|
36
36
|
# Returns the value of the attribute identified by +attr_name+ before
|
@@ -66,6 +66,11 @@ module ActiveRecord
|
|
66
66
|
@attributes.values_before_type_cast
|
67
67
|
end
|
68
68
|
|
69
|
+
# Returns a hash of attributes for assignment to the database.
|
70
|
+
def attributes_for_database
|
71
|
+
@attributes.values_for_database
|
72
|
+
end
|
73
|
+
|
69
74
|
private
|
70
75
|
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
|
71
76
|
def attribute_before_type_cast(attr_name)
|
@@ -14,16 +14,43 @@ module ActiveRecord
|
|
14
14
|
raise "You cannot include Dirty after Timestamp"
|
15
15
|
end
|
16
16
|
|
17
|
-
class_attribute :
|
17
|
+
class_attribute :partial_updates, instance_writer: false, default: true
|
18
|
+
class_attribute :partial_inserts, instance_writer: false, default: true
|
18
19
|
|
19
20
|
# Attribute methods for "changed in last call to save?"
|
20
|
-
attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
|
21
|
-
attribute_method_prefix("saved_change_to_")
|
22
|
-
attribute_method_suffix("_before_last_save")
|
21
|
+
attribute_method_affix(prefix: "saved_change_to_", suffix: "?", parameters: "**options")
|
22
|
+
attribute_method_prefix("saved_change_to_", parameters: false)
|
23
|
+
attribute_method_suffix("_before_last_save", parameters: false)
|
23
24
|
|
24
25
|
# Attribute methods for "will change if I call save?"
|
25
|
-
attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
|
26
|
-
attribute_method_suffix("_change_to_be_saved", "_in_database")
|
26
|
+
attribute_method_affix(prefix: "will_save_change_to_", suffix: "?", parameters: "**options")
|
27
|
+
attribute_method_suffix("_change_to_be_saved", "_in_database", parameters: false)
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
def partial_writes
|
32
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
33
|
+
ActiveRecord::Base.partial_writes is deprecated and will be removed in Rails 7.1.
|
34
|
+
Use `partial_updates` and `partial_inserts` instead.
|
35
|
+
MSG
|
36
|
+
partial_updates && partial_inserts
|
37
|
+
end
|
38
|
+
|
39
|
+
def partial_writes?
|
40
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
41
|
+
`ActiveRecord::Base.partial_writes?` is deprecated and will be removed in Rails 7.1.
|
42
|
+
Use `partial_updates?` and `partial_inserts?` instead.
|
43
|
+
MSG
|
44
|
+
partial_updates? && partial_inserts?
|
45
|
+
end
|
46
|
+
|
47
|
+
def partial_writes=(value)
|
48
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
49
|
+
`ActiveRecord::Base.partial_writes=` is deprecated and will be removed in Rails 7.1.
|
50
|
+
Use `partial_updates=` and `partial_inserts=` instead.
|
51
|
+
MSG
|
52
|
+
self.partial_updates = self.partial_inserts = value
|
53
|
+
end
|
27
54
|
end
|
28
55
|
|
29
56
|
# <tt>reload</tt> the record and clears changed attributes.
|
@@ -156,12 +183,6 @@ module ActiveRecord
|
|
156
183
|
end
|
157
184
|
|
158
185
|
private
|
159
|
-
def write_attribute_without_type_cast(attr_name, value)
|
160
|
-
result = super
|
161
|
-
clear_attribute_change(attr_name)
|
162
|
-
result
|
163
|
-
end
|
164
|
-
|
165
186
|
def _touch_row(attribute_names, time)
|
166
187
|
@_touch_attr_names = Set.new(attribute_names)
|
167
188
|
|
@@ -191,20 +212,24 @@ module ActiveRecord
|
|
191
212
|
@_touch_attr_names, @_skip_dirty_tracking = nil, nil
|
192
213
|
end
|
193
214
|
|
194
|
-
def _update_record(attribute_names =
|
215
|
+
def _update_record(attribute_names = attribute_names_for_partial_updates)
|
195
216
|
affected_rows = super
|
196
217
|
changes_applied
|
197
218
|
affected_rows
|
198
219
|
end
|
199
220
|
|
200
|
-
def _create_record(attribute_names =
|
221
|
+
def _create_record(attribute_names = attribute_names_for_partial_inserts)
|
201
222
|
id = super
|
202
223
|
changes_applied
|
203
224
|
id
|
204
225
|
end
|
205
226
|
|
206
|
-
def
|
207
|
-
|
227
|
+
def attribute_names_for_partial_updates
|
228
|
+
partial_updates? ? changed_attribute_names_to_save : attribute_names
|
229
|
+
end
|
230
|
+
|
231
|
+
def attribute_names_for_partial_inserts
|
232
|
+
partial_inserts? ? changed_attribute_names_to_save : attribute_names
|
208
233
|
end
|
209
234
|
end
|
210
235
|
end
|
@@ -78,7 +78,7 @@ module ActiveRecord
|
|
78
78
|
@quoted_primary_key ||= connection.quote_column_name(primary_key)
|
79
79
|
end
|
80
80
|
|
81
|
-
def reset_primary_key
|
81
|
+
def reset_primary_key # :nodoc:
|
82
82
|
if base_class?
|
83
83
|
self.primary_key = get_primary_key(base_class.name)
|
84
84
|
else
|
@@ -86,7 +86,7 @@ module ActiveRecord
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def get_primary_key(base_name)
|
89
|
+
def get_primary_key(base_name) # :nodoc:
|
90
90
|
if base_name && primary_key_prefix_type == :table_name
|
91
91
|
base_name.foreign_key(false)
|
92
92
|
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
@@ -6,11 +6,11 @@ module ActiveRecord
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
attribute_method_suffix "?"
|
9
|
+
attribute_method_suffix "?", parameters: false
|
10
10
|
end
|
11
11
|
|
12
12
|
def query_attribute(attr_name)
|
13
|
-
value = self
|
13
|
+
value = self.public_send(attr_name)
|
14
14
|
|
15
15
|
case value
|
16
16
|
when true then true
|
@@ -11,10 +11,12 @@ module ActiveRecord
|
|
11
11
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
12
12
|
owner, name
|
13
13
|
) do |temp_method_name, attr_name_expr|
|
14
|
-
owner
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
owner.define_cached_method(name, as: temp_method_name, namespace: :active_record) do |batch|
|
15
|
+
batch <<
|
16
|
+
"def #{temp_method_name}" <<
|
17
|
+
" _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
|
18
|
+
"end"
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -32,7 +34,7 @@ module ActiveRecord
|
|
32
34
|
|
33
35
|
# This method exists to avoid the expensive primary_key check internally, without
|
34
36
|
# breaking compatibility with the read_attribute API
|
35
|
-
def _read_attribute(attr_name, &block) # :nodoc
|
37
|
+
def _read_attribute(attr_name, &block) # :nodoc:
|
36
38
|
@attributes.fetch_value(attr_name, &block)
|
37
39
|
end
|
38
40
|
|
@@ -16,15 +16,45 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
|
18
18
|
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.
|
19
|
+
# If you have an attribute that needs to be saved to the database as a
|
20
|
+
# serialized object, and retrieved by deserializing into the same object,
|
21
|
+
# then specify the name of that attribute using this method and serialization
|
22
|
+
# will be handled automatically.
|
25
23
|
#
|
26
|
-
#
|
27
|
-
#
|
24
|
+
# The serialization format may be YAML, JSON, or any custom format using a
|
25
|
+
# custom coder class.
|
26
|
+
#
|
27
|
+
# === Serialization formats
|
28
|
+
#
|
29
|
+
# serialize attr_name [, class_name_or_coder]
|
30
|
+
#
|
31
|
+
# | | database storage |
|
32
|
+
# class_name_or_coder | attribute read/write type | serialized | NULL |
|
33
|
+
# ---------------------+---------------------------+------------+--------+
|
34
|
+
# <not given> | any value that supports | YAML | |
|
35
|
+
# | .to_yaml | | |
|
36
|
+
# | | | |
|
37
|
+
# Array | Array ** | YAML | [] |
|
38
|
+
# | | | |
|
39
|
+
# Hash | Hash ** | YAML | {} |
|
40
|
+
# | | | |
|
41
|
+
# JSON | any value that supports | JSON | |
|
42
|
+
# | .to_json | | |
|
43
|
+
# | | | |
|
44
|
+
# <custom coder class> | any value supported by | custom | custom |
|
45
|
+
# | the custom coder class | | |
|
46
|
+
#
|
47
|
+
# ** If +class_name_or_coder+ is +Array+ or +Hash+, values retrieved will
|
48
|
+
# always be of that type, and any value assigned must be of that type or
|
49
|
+
# +SerializationTypeMismatch+ will be raised.
|
50
|
+
#
|
51
|
+
# ==== Custom coders
|
52
|
+
# A custom coder class or module may be given. This must have +self.load+
|
53
|
+
# and +self.dump+ class/module methods. <tt>self.dump(object)</tt> will be called
|
54
|
+
# to serialize an object and should return the serialized value to be
|
55
|
+
# stored in the database (+nil+ to store as +NULL+). <tt>self.load(string)</tt>
|
56
|
+
# will be called to reverse the process and load (unserialize) from the
|
57
|
+
# database.
|
28
58
|
#
|
29
59
|
# Keep in mind that database adapters handle certain serialization tasks
|
30
60
|
# for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
|
@@ -38,8 +68,9 @@ module ActiveRecord
|
|
38
68
|
# ==== Parameters
|
39
69
|
#
|
40
70
|
# * +attr_name+ - The field name that should be serialized.
|
41
|
-
# * +class_name_or_coder+ - Optional,
|
42
|
-
# or a
|
71
|
+
# * +class_name_or_coder+ - Optional, may be be +Array+ or +Hash+ or
|
72
|
+
# +JSON+ or a custom coder class or module which responds to +.load+
|
73
|
+
# and +.dump+. See table above.
|
43
74
|
#
|
44
75
|
# ==== Options
|
45
76
|
#
|
@@ -49,7 +80,7 @@ module ActiveRecord
|
|
49
80
|
#
|
50
81
|
# ==== Example
|
51
82
|
#
|
52
|
-
# # Serialize a preferences attribute.
|
83
|
+
# # Serialize a preferences attribute using YAML coder.
|
53
84
|
# class User < ActiveRecord::Base
|
54
85
|
# serialize :preferences
|
55
86
|
# end
|
@@ -63,6 +94,28 @@ module ActiveRecord
|
|
63
94
|
# class User < ActiveRecord::Base
|
64
95
|
# serialize :preferences, Hash
|
65
96
|
# end
|
97
|
+
#
|
98
|
+
# # Serialize preferences using a custom coder.
|
99
|
+
# class Rot13JSON
|
100
|
+
# def self.rot13(string)
|
101
|
+
# string.tr("a-zA-Z", "n-za-mN-ZA-M")
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# # returns serialized string that will be stored in the database
|
105
|
+
# def self.dump(object)
|
106
|
+
# ActiveSupport::JSON.encode(object).rot13
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# # reverses the above, turning the serialized string from the database
|
110
|
+
# # back into its original value
|
111
|
+
# def self.load(string)
|
112
|
+
# ActiveSupport::JSON.decode(string.rot13)
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# class User < ActiveRecord::Base
|
117
|
+
# serialize :preferences, Rot13JSON
|
118
|
+
# end
|
66
119
|
def serialize(attr_name, class_name_or_coder = Object, **options)
|
67
120
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
68
121
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
@@ -75,11 +128,12 @@ module ActiveRecord
|
|
75
128
|
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
76
129
|
end
|
77
130
|
|
78
|
-
|
131
|
+
attribute(attr_name, **options) do |cast_type|
|
79
132
|
if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
|
80
133
|
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
81
134
|
end
|
82
135
|
|
136
|
+
cast_type = cast_type.subtype if Type::Serialized === cast_type
|
83
137
|
Type::Serialized.new(cast_type, coder)
|
84
138
|
end
|
85
139
|
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
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
attribute_method_suffix "="
|
9
|
+
attribute_method_suffix "=", parameters: "value"
|
10
10
|
end
|
11
11
|
|
12
12
|
module ClassMethods # :nodoc:
|
@@ -15,10 +15,12 @@ module ActiveRecord
|
|
15
15
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
16
16
|
owner, name, writer: true,
|
17
17
|
) do |temp_method_name, attr_name_expr|
|
18
|
-
owner
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_record) do |batch|
|
19
|
+
batch <<
|
20
|
+
"def #{temp_method_name}(value)" <<
|
21
|
+
" _write_attribute(#{attr_name_expr}, value)" <<
|
22
|
+
"end"
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -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
|
|
@@ -39,7 +39,7 @@ module ActiveRecord
|
|
39
39
|
end
|
40
40
|
|
41
41
|
module ClassMethods
|
42
|
-
def inherited(child_class)
|
42
|
+
def inherited(child_class) # :nodoc:
|
43
43
|
child_class.initialize_generated_modules
|
44
44
|
super
|
45
45
|
end
|
@@ -267,9 +267,8 @@ module ActiveRecord
|
|
267
267
|
|
268
268
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
269
269
|
# attribute +attr_name+. String attributes are truncated up to 50
|
270
|
-
# characters
|
271
|
-
#
|
272
|
-
# <tt>#inspect</tt> without modification.
|
270
|
+
# characters. Other attributes return the value of <tt>#inspect</tt>
|
271
|
+
# without modification.
|
273
272
|
#
|
274
273
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
275
274
|
#
|
@@ -277,7 +276,7 @@ module ActiveRecord
|
|
277
276
|
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
|
278
277
|
#
|
279
278
|
# person.attribute_for_inspect(:created_at)
|
280
|
-
# # => "\"2012-10-22 00:15:07\""
|
279
|
+
# # => "\"2012-10-22 00:15:07.000000000 +0000\""
|
281
280
|
#
|
282
281
|
# person.attribute_for_inspect(:tag_ids)
|
283
282
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
@@ -385,9 +384,7 @@ module ActiveRecord
|
|
385
384
|
end
|
386
385
|
|
387
386
|
def attributes_with_values(attribute_names)
|
388
|
-
attribute_names.index_with
|
389
|
-
_read_attribute(name)
|
390
|
-
end
|
387
|
+
attribute_names.index_with { |name| @attributes[name] }
|
391
388
|
end
|
392
389
|
|
393
390
|
# Filters the primary keys and readonly attributes from the attribute names.
|
@@ -12,9 +12,6 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
##
|
16
|
-
# :call-seq: attribute(name, cast_type = nil, **options)
|
17
|
-
#
|
18
15
|
# Defines an attribute with a type on this model. It will override the
|
19
16
|
# type of existing attributes if needed. This allows control over how
|
20
17
|
# values are converted to and from SQL when assigned to a model. It also
|
@@ -208,14 +205,31 @@ module ActiveRecord
|
|
208
205
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
209
206
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
210
207
|
# methods in ActiveModel::Type::Value for more details.
|
211
|
-
def attribute(name, cast_type = nil, **options
|
208
|
+
def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
|
212
209
|
name = name.to_s
|
210
|
+
name = attribute_aliases[name] || name
|
211
|
+
|
213
212
|
reload_schema_from_cache
|
214
213
|
|
214
|
+
case cast_type
|
215
|
+
when Symbol
|
216
|
+
cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
|
217
|
+
when nil
|
218
|
+
if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
|
219
|
+
default = prev_default if default == NO_DEFAULT_PROVIDED
|
220
|
+
else
|
221
|
+
prev_cast_type = -> subtype { subtype }
|
222
|
+
end
|
223
|
+
|
224
|
+
cast_type = if block_given?
|
225
|
+
-> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
|
226
|
+
else
|
227
|
+
prev_cast_type
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
215
231
|
self.attributes_to_define_after_schema_loads =
|
216
|
-
attributes_to_define_after_schema_loads.merge(
|
217
|
-
name => [cast_type || block, options]
|
218
|
-
)
|
232
|
+
attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
|
219
233
|
end
|
220
234
|
|
221
235
|
# This is the low level API which sits beneath +attribute+. It only
|
@@ -248,8 +262,9 @@ module ActiveRecord
|
|
248
262
|
|
249
263
|
def load_schema! # :nodoc:
|
250
264
|
super
|
251
|
-
attributes_to_define_after_schema_loads.each do |name, (
|
252
|
-
|
265
|
+
attributes_to_define_after_schema_loads.each do |name, (cast_type, default)|
|
266
|
+
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
267
|
+
define_attribute(name, cast_type, default: default)
|
253
268
|
end
|
254
269
|
end
|
255
270
|
|
@@ -272,32 +287,6 @@ module ActiveRecord
|
|
272
287
|
end
|
273
288
|
_default_attributes[name] = default_attribute
|
274
289
|
end
|
275
|
-
|
276
|
-
def decorate_attribute_type(attr_name, **default)
|
277
|
-
type, options = attributes_to_define_after_schema_loads[attr_name]
|
278
|
-
|
279
|
-
default.with_defaults!(default: options[:default]) if options&.key?(:default)
|
280
|
-
|
281
|
-
attribute(attr_name, **default) do |cast_type|
|
282
|
-
if type && !type.is_a?(Proc)
|
283
|
-
cast_type = _lookup_cast_type(attr_name, type, options)
|
284
|
-
end
|
285
|
-
|
286
|
-
yield cast_type
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
def _lookup_cast_type(name, type, options)
|
291
|
-
case type
|
292
|
-
when Symbol
|
293
|
-
adapter_name = ActiveRecord::Type.adapter_name_from(self)
|
294
|
-
ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
|
295
|
-
when Proc
|
296
|
-
type[type_for_attribute(name)]
|
297
|
-
else
|
298
|
-
type || type_for_attribute(name)
|
299
|
-
end
|
300
|
-
end
|
301
290
|
end
|
302
291
|
end
|
303
292
|
end
|