activerecord 6.1.6 → 7.0.4
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 +1314 -975
- 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 +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- 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 +10 -3
- 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 +19 -21
- data/lib/active_record/associations/collection_proxy.rb +10 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- 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/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -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 +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +124 -95
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +14 -15
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -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 +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
- data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +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 +51 -51
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +124 -134
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- 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 +67 -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 +206 -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 +28 -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 +90 -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 +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- 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 +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/migration/compatibility.rb +84 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +114 -83
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +228 -60
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +73 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- 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 +276 -67
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +25 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +7 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +16 -9
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +3 -3
- 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 +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +217 -27
- 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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -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 +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -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 +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -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
- 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 +55 -11
@@ -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,32 @@ 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
|
+
if partial_inserts?
|
233
|
+
changed_attribute_names_to_save
|
234
|
+
else
|
235
|
+
attribute_names.reject do |attr_name|
|
236
|
+
if column_for_attribute(attr_name).default_function
|
237
|
+
!attribute_changed?(attr_name)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
208
241
|
end
|
209
242
|
end
|
210
243
|
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,13 @@ 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.
|
28
26
|
#
|
29
27
|
# Keep in mind that database adapters handle certain serialization tasks
|
30
28
|
# for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
|
@@ -37,32 +35,71 @@ module ActiveRecord
|
|
37
35
|
#
|
38
36
|
# ==== Parameters
|
39
37
|
#
|
40
|
-
# * +attr_name+ - The
|
41
|
-
# * +class_name_or_coder+ - Optional
|
42
|
-
#
|
38
|
+
# * +attr_name+ - The name of the attribute to serialize.
|
39
|
+
# * +class_name_or_coder+ - Optional. May be one of the following:
|
40
|
+
# * <em>default</em> - The attribute value will be serialized as YAML.
|
41
|
+
# The attribute value must respond to +to_yaml+.
|
42
|
+
# * +Array+ - The attribute value will be serialized as YAML, but an
|
43
|
+
# empty +Array+ will be serialized as +NULL+. The attribute value
|
44
|
+
# must be an +Array+.
|
45
|
+
# * +Hash+ - The attribute value will be serialized as YAML, but an
|
46
|
+
# empty +Hash+ will be serialized as +NULL+. The attribute value
|
47
|
+
# must be a +Hash+.
|
48
|
+
# * +JSON+ - The attribute value will be serialized as JSON. The
|
49
|
+
# attribute value must respond to +to_json+.
|
50
|
+
# * <em>custom coder</em> - The attribute value will be serialized
|
51
|
+
# using the coder's <tt>dump(value)</tt> method, and will be
|
52
|
+
# deserialized using the coder's <tt>load(string)</tt> method. The
|
53
|
+
# +dump+ method may return +nil+ to serialize the value as +NULL+.
|
43
54
|
#
|
44
55
|
# ==== Options
|
45
56
|
#
|
46
|
-
#
|
47
|
-
# is not passed, the previous default value (if any) will
|
48
|
-
# Otherwise, the default will be +nil+.
|
57
|
+
# * +:default+ - The default value to use when no value is provided. If
|
58
|
+
# this option is not passed, the previous default value (if any) will
|
59
|
+
# be used. Otherwise, the default will be +nil+.
|
49
60
|
#
|
50
|
-
# ====
|
61
|
+
# ==== Examples
|
62
|
+
#
|
63
|
+
# ===== Serialize the +preferences+ attribute using YAML
|
51
64
|
#
|
52
|
-
# # Serialize a preferences attribute.
|
53
65
|
# class User < ActiveRecord::Base
|
54
66
|
# serialize :preferences
|
55
67
|
# end
|
56
68
|
#
|
57
|
-
#
|
69
|
+
# ===== Serialize the +preferences+ attribute using JSON
|
70
|
+
#
|
58
71
|
# class User < ActiveRecord::Base
|
59
72
|
# serialize :preferences, JSON
|
60
73
|
# end
|
61
74
|
#
|
62
|
-
#
|
75
|
+
# ===== Serialize the +preferences+ +Hash+ using YAML
|
76
|
+
#
|
63
77
|
# class User < ActiveRecord::Base
|
64
78
|
# serialize :preferences, Hash
|
65
79
|
# end
|
80
|
+
#
|
81
|
+
# ===== Serialize the +preferences+ attribute using a custom coder
|
82
|
+
#
|
83
|
+
# class Rot13JSON
|
84
|
+
# def self.rot13(string)
|
85
|
+
# string.tr("a-zA-Z", "n-za-mN-ZA-M")
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# # Serializes an attribute value to a string that will be stored in the database.
|
89
|
+
# def self.dump(value)
|
90
|
+
# rot13(ActiveSupport::JSON.dump(value))
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# # Deserializes a string from the database to an attribute value.
|
94
|
+
# def self.load(string)
|
95
|
+
# ActiveSupport::JSON.load(rot13(string))
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# class User < ActiveRecord::Base
|
100
|
+
# serialize :preferences, Rot13JSON
|
101
|
+
# end
|
102
|
+
#
|
66
103
|
def serialize(attr_name, class_name_or_coder = Object, **options)
|
67
104
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
68
105
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
@@ -75,11 +112,12 @@ module ActiveRecord
|
|
75
112
|
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
76
113
|
end
|
77
114
|
|
78
|
-
|
115
|
+
attribute(attr_name, **options) do |cast_type|
|
79
116
|
if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
|
80
117
|
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
81
118
|
end
|
82
119
|
|
120
|
+
cast_type = cast_type.subtype if Type::Serialized === cast_type
|
83
121
|
Type::Serialized.new(cast_type, coder)
|
84
122
|
end
|
85
123
|
end
|
@@ -19,12 +19,16 @@ module ActiveRecord
|
|
19
19
|
|
20
20
|
if value.is_a?(Hash)
|
21
21
|
set_time_zone_without_conversion(super)
|
22
|
+
elsif value.is_a?(Range)
|
23
|
+
Range.new(user_input_in_time_zone(value.begin), user_input_in_time_zone(value.end), value.exclude_end?)
|
22
24
|
elsif value.respond_to?(:in_time_zone)
|
23
25
|
begin
|
24
26
|
super(user_input_in_time_zone(value)) || super
|
25
27
|
rescue ArgumentError
|
26
28
|
nil
|
27
29
|
end
|
30
|
+
elsif value.respond_to?(:infinite?) && value.infinite?
|
31
|
+
value
|
28
32
|
else
|
29
33
|
map_avoiding_infinite_recursion(super) { |v| cast(v) }
|
30
34
|
end
|
@@ -36,8 +40,10 @@ module ActiveRecord
|
|
36
40
|
|
37
41
|
if value.acts_like?(:time)
|
38
42
|
value.in_time_zone
|
39
|
-
elsif value.
|
43
|
+
elsif value.respond_to?(:infinite?) && value.infinite?
|
40
44
|
value
|
45
|
+
elsif value.is_a?(Range)
|
46
|
+
Range.new(convert_time_to_time_zone(value.begin), convert_time_to_time_zone(value.end), value.exclude_end?)
|
41
47
|
else
|
42
48
|
map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
|
43
49
|
end
|
@@ -61,8 +67,7 @@ module ActiveRecord
|
|
61
67
|
extend ActiveSupport::Concern
|
62
68
|
|
63
69
|
included do
|
64
|
-
|
65
|
-
|
70
|
+
class_attribute :time_zone_aware_attributes, instance_writer: false, default: false
|
66
71
|
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
|
67
72
|
class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
|
68
73
|
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
|
@@ -97,7 +97,7 @@ module ActiveRecord
|
|
97
97
|
super
|
98
98
|
else
|
99
99
|
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
|
100
|
-
# defines its own attribute method, then we don't want to
|
100
|
+
# defines its own attribute method, then we don't want to override that.
|
101
101
|
defined = method_defined_within?(method_name, superclass, Base) &&
|
102
102
|
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
|
103
103
|
defined || super
|
@@ -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,25 +384,25 @@ 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
|
-
# Filters the primary keys
|
390
|
+
# Filters the primary keys, readonly attributes and virtual columns from the attribute names.
|
394
391
|
def attributes_for_update(attribute_names)
|
395
392
|
attribute_names &= self.class.column_names
|
396
393
|
attribute_names.delete_if do |name|
|
397
|
-
self.class.readonly_attribute?(name)
|
394
|
+
self.class.readonly_attribute?(name) ||
|
395
|
+
column_for_attribute(name).virtual?
|
398
396
|
end
|
399
397
|
end
|
400
398
|
|
401
|
-
# Filters out the primary keys, from the attribute names, when the primary
|
399
|
+
# Filters out the virtual columns and also primary keys, from the attribute names, when the primary
|
402
400
|
# key is to be generated (e.g. the id attribute has no value).
|
403
401
|
def attributes_for_create(attribute_names)
|
404
402
|
attribute_names &= self.class.column_names
|
405
403
|
attribute_names.delete_if do |name|
|
406
|
-
pk_attribute?(name) && id.nil?
|
404
|
+
(pk_attribute?(name) && id.nil?) ||
|
405
|
+
column_for_attribute(name).virtual?
|
407
406
|
end
|
408
407
|
end
|
409
408
|
|
@@ -414,7 +413,7 @@ module ActiveRecord
|
|
414
413
|
inspected_value = if value.is_a?(String) && value.length > 50
|
415
414
|
"#{value[0, 50]}...".inspect
|
416
415
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
417
|
-
%("#{value.
|
416
|
+
%("#{value.to_fs(:inspect)}")
|
418
417
|
else
|
419
418
|
value.inspect
|
420
419
|
end
|
@@ -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
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
module AutosaveAssociation
|
139
139
|
extend ActiveSupport::Concern
|
140
140
|
|
141
|
-
module AssociationBuilderExtension
|
141
|
+
module AssociationBuilderExtension # :nodoc:
|
142
142
|
def self.build(model, reflection)
|
143
143
|
model.send(:add_autosave_association_callbacks, reflection)
|
144
144
|
end
|
@@ -150,25 +150,10 @@ module ActiveRecord
|
|
150
150
|
|
151
151
|
included do
|
152
152
|
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
153
|
-
mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
|
154
153
|
end
|
155
154
|
|
156
155
|
module ClassMethods # :nodoc:
|
157
156
|
private
|
158
|
-
if Module.method(:method_defined?).arity == 1 # MRI 2.5 and older
|
159
|
-
using Module.new {
|
160
|
-
refine Module do
|
161
|
-
def method_defined?(method, inherit = true)
|
162
|
-
if inherit
|
163
|
-
super(method)
|
164
|
-
else
|
165
|
-
instance_methods(false).include?(method.to_sym)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
}
|
170
|
-
end
|
171
|
-
|
172
157
|
def define_non_cyclic_method(name, &block)
|
173
158
|
return if method_defined?(name, false)
|
174
159
|
|
@@ -210,7 +195,7 @@ module ActiveRecord
|
|
210
195
|
after_create save_method
|
211
196
|
after_update save_method
|
212
197
|
elsif reflection.has_one?
|
213
|
-
|
198
|
+
define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
|
214
199
|
# Configures two callbacks instead of a single after_save so that
|
215
200
|
# the model may rely on their execution order relative to its
|
216
201
|
# own callbacks.
|
@@ -349,7 +334,7 @@ module ActiveRecord
|
|
349
334
|
|
350
335
|
unless valid = record.valid?(context)
|
351
336
|
if reflection.options[:autosave]
|
352
|
-
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord
|
337
|
+
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord.index_nested_attribute_errors)
|
353
338
|
|
354
339
|
record.errors.group_by_attribute.each { |attribute, errors|
|
355
340
|
attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
@@ -419,6 +404,8 @@ module ActiveRecord
|
|
419
404
|
saved = true
|
420
405
|
|
421
406
|
if autosave != false && (new_record_before_save || record.new_record?)
|
407
|
+
association.set_inverse_instance(record)
|
408
|
+
|
422
409
|
if autosave
|
423
410
|
saved = association.insert_record(record, false)
|
424
411
|
elsif !reflection.nested?
|
@@ -459,12 +446,10 @@ module ActiveRecord
|
|
459
446
|
elsif autosave != false
|
460
447
|
key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
|
461
448
|
|
462
|
-
if (autosave && record.changed_for_autosave?) ||
|
449
|
+
if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, key)
|
463
450
|
unless reflection.through_reflection
|
464
451
|
record[reflection.foreign_key] = key
|
465
|
-
|
466
|
-
record.association(inverse_reflection.name).inversed_from(self)
|
467
|
-
end
|
452
|
+
association.set_inverse_instance(record)
|
468
453
|
end
|
469
454
|
|
470
455
|
saved = record.save(validate: !autosave)
|
@@ -476,7 +461,7 @@ module ActiveRecord
|
|
476
461
|
end
|
477
462
|
|
478
463
|
# If the record is new or it has changed, returns true.
|
479
|
-
def
|
464
|
+
def _record_changed?(reflection, record, key)
|
480
465
|
record.new_record? ||
|
481
466
|
association_foreign_key_changed?(reflection, record, key) ||
|
482
467
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
data/lib/active_record/base.rb
CHANGED
@@ -12,7 +12,7 @@ require "active_record/attributes"
|
|
12
12
|
require "active_record/type_caster"
|
13
13
|
require "active_record/database_configurations"
|
14
14
|
|
15
|
-
module ActiveRecord
|
15
|
+
module ActiveRecord # :nodoc:
|
16
16
|
# = Active Record
|
17
17
|
#
|
18
18
|
# Active Record objects don't specify their attributes directly, but rather infer them from
|
@@ -137,6 +137,23 @@ module ActiveRecord #:nodoc:
|
|
137
137
|
# anonymous = User.new(name: "")
|
138
138
|
# anonymous.name? # => false
|
139
139
|
#
|
140
|
+
# Query methods will also respect any overrides of default accessors:
|
141
|
+
#
|
142
|
+
# class User
|
143
|
+
# # Has admin boolean column
|
144
|
+
# def admin
|
145
|
+
# false
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
# user.update(admin: true)
|
150
|
+
#
|
151
|
+
# user.read_attribute(:admin) # => true, gets the column value
|
152
|
+
# user[:admin] # => true, also gets the column value
|
153
|
+
#
|
154
|
+
# user.admin # => false, due to the getter override
|
155
|
+
# user.admin? # => false, due to the getter override
|
156
|
+
#
|
140
157
|
# == Accessing attributes before they have been typecasted
|
141
158
|
#
|
142
159
|
# Sometimes you want to be able to read the raw attribute data without having the column-determined
|
@@ -310,6 +327,7 @@ module ActiveRecord #:nodoc:
|
|
310
327
|
include SecureToken
|
311
328
|
include SignedId
|
312
329
|
include Suppressor
|
330
|
+
include Encryption::EncryptableRecord
|
313
331
|
end
|
314
332
|
|
315
333
|
ActiveSupport.run_load_hooks(:active_record, Base)
|