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
@@ -6,6 +6,13 @@ module ActiveRecord
|
|
6
6
|
module ModelSchema
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
##
|
10
|
+
# :method: id_value
|
11
|
+
# :call-seq: id_value
|
12
|
+
#
|
13
|
+
# Returns the underlying column value for a column named "id". Useful when defining
|
14
|
+
# a composite primary key including an "id" column so that the value is readable.
|
15
|
+
|
9
16
|
##
|
10
17
|
# :singleton-method: primary_key_prefix_type
|
11
18
|
# :call-seq: primary_key_prefix_type
|
@@ -126,9 +133,29 @@ module ActiveRecord
|
|
126
133
|
# +:immutable_string+. This setting does not affect the behavior of
|
127
134
|
# <tt>attribute :foo, :string</tt>. Defaults to false.
|
128
135
|
|
129
|
-
|
130
|
-
|
136
|
+
##
|
137
|
+
# :singleton-method: inheritance_column
|
138
|
+
# :call-seq: inheritance_column
|
139
|
+
#
|
140
|
+
# The name of the table column which stores the class name on single-table
|
141
|
+
# inheritance situations.
|
142
|
+
#
|
143
|
+
# The default inheritance column name is +type+, which means it's a
|
144
|
+
# reserved word inside Active Record. To be able to use single-table
|
145
|
+
# inheritance with another column name, or to use the column +type+ in
|
146
|
+
# your own model for something else, you can set +inheritance_column+:
|
147
|
+
#
|
148
|
+
# self.inheritance_column = 'zoink'
|
149
|
+
|
150
|
+
##
|
151
|
+
# :singleton-method: inheritance_column=
|
152
|
+
# :call-seq: inheritance_column=(column)
|
153
|
+
#
|
154
|
+
# Defines the name of the table column which will store the class name on single-table
|
155
|
+
# inheritance situations.
|
131
156
|
|
157
|
+
included do
|
158
|
+
class_attribute :primary_key_prefix_type, instance_writer: false
|
132
159
|
class_attribute :table_name_prefix, instance_writer: false, default: ""
|
133
160
|
class_attribute :table_name_suffix, instance_writer: false, default: ""
|
134
161
|
class_attribute :schema_migrations_table_name, instance_accessor: false, default: "schema_migrations"
|
@@ -137,8 +164,15 @@ module ActiveRecord
|
|
137
164
|
class_attribute :implicit_order_column, instance_accessor: false
|
138
165
|
class_attribute :immutable_strings_by_default, instance_accessor: false
|
139
166
|
|
167
|
+
class_attribute :inheritance_column, instance_accessor: false, default: "type"
|
168
|
+
singleton_class.class_eval do
|
169
|
+
alias_method :_inheritance_column=, :inheritance_column=
|
170
|
+
private :_inheritance_column=
|
171
|
+
alias_method :inheritance_column=, :real_inheritance_column=
|
172
|
+
end
|
173
|
+
|
140
174
|
self.protected_environments = ["production"]
|
141
|
-
|
175
|
+
|
142
176
|
self.ignored_columns = [].freeze
|
143
177
|
|
144
178
|
delegate :type_for_attribute, :column_for_attribute, to: :class
|
@@ -153,8 +187,9 @@ module ActiveRecord
|
|
153
187
|
# artists, records => artists_records
|
154
188
|
# records, artists => artists_records
|
155
189
|
# music_artists, music_records => music_artists_records
|
190
|
+
# music.artists, music.records => music.artists_records
|
156
191
|
def self.derive_join_table_name(first_table, second_table) # :nodoc:
|
157
|
-
[first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
192
|
+
[first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*[_.])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
158
193
|
end
|
159
194
|
|
160
195
|
module ClassMethods
|
@@ -197,6 +232,21 @@ module ActiveRecord
|
|
197
232
|
# the table name guess for an Invoice class becomes "myapp_invoices".
|
198
233
|
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
|
199
234
|
#
|
235
|
+
# Active Model Naming's +model_name+ is the base name used to guess the
|
236
|
+
# table name. In case a custom Active Model Name is defined, it will be
|
237
|
+
# used for the table name as well:
|
238
|
+
#
|
239
|
+
# class PostRecord < ActiveRecord::Base
|
240
|
+
# class << self
|
241
|
+
# def model_name
|
242
|
+
# ActiveModel::Name.new(self, nil, "Post")
|
243
|
+
# end
|
244
|
+
# end
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# PostRecord.table_name
|
248
|
+
# # => "posts"
|
249
|
+
#
|
200
250
|
# You can also set your own table name explicitly:
|
201
251
|
#
|
202
252
|
# class Mouse < ActiveRecord::Base
|
@@ -233,7 +283,7 @@ module ActiveRecord
|
|
233
283
|
end
|
234
284
|
|
235
285
|
# Computes the table name, (re)sets it internally, and returns it.
|
236
|
-
def reset_table_name
|
286
|
+
def reset_table_name # :nodoc:
|
237
287
|
self.table_name = if abstract_class?
|
238
288
|
superclass == Base ? nil : superclass.table_name
|
239
289
|
elsif superclass.abstract_class?
|
@@ -243,11 +293,11 @@ module ActiveRecord
|
|
243
293
|
end
|
244
294
|
end
|
245
295
|
|
246
|
-
def full_table_name_prefix
|
296
|
+
def full_table_name_prefix # :nodoc:
|
247
297
|
(module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
248
298
|
end
|
249
299
|
|
250
|
-
def full_table_name_suffix
|
300
|
+
def full_table_name_suffix # :nodoc:
|
251
301
|
(module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
|
252
302
|
end
|
253
303
|
|
@@ -266,33 +316,14 @@ module ActiveRecord
|
|
266
316
|
@protected_environments = environments.map(&:to_s)
|
267
317
|
end
|
268
318
|
|
269
|
-
|
270
|
-
|
271
|
-
#
|
272
|
-
# The default inheritance column name is +type+, which means it's a
|
273
|
-
# reserved word inside Active Record. To be able to use single-table
|
274
|
-
# inheritance with another column name, or to use the column +type+ in
|
275
|
-
# your own model for something else, you can set +inheritance_column+:
|
276
|
-
#
|
277
|
-
# self.inheritance_column = 'zoink'
|
278
|
-
def inheritance_column
|
279
|
-
(@inheritance_column ||= nil) || superclass.inheritance_column
|
280
|
-
end
|
281
|
-
|
282
|
-
# Sets the value of inheritance_column
|
283
|
-
def inheritance_column=(value)
|
284
|
-
@inheritance_column = value.to_s
|
285
|
-
@explicit_inheritance_column = true
|
319
|
+
def real_inheritance_column=(value) # :nodoc:
|
320
|
+
self._inheritance_column = value.to_s
|
286
321
|
end
|
287
322
|
|
288
323
|
# The list of columns names the model should ignore. Ignored columns won't have attribute
|
289
324
|
# accessors defined, and won't be referenced in SQL queries.
|
290
325
|
def ignored_columns
|
291
|
-
|
292
|
-
@ignored_columns
|
293
|
-
else
|
294
|
-
superclass.ignored_columns
|
295
|
-
end
|
326
|
+
@ignored_columns || superclass.ignored_columns
|
296
327
|
end
|
297
328
|
|
298
329
|
# Sets the columns names the model should ignore. Ignored columns won't have attribute
|
@@ -313,7 +344,7 @@ module ActiveRecord
|
|
313
344
|
# # name :string, limit: 255
|
314
345
|
# # category :string, limit: 255
|
315
346
|
#
|
316
|
-
# self.ignored_columns
|
347
|
+
# self.ignored_columns += [:category]
|
317
348
|
# end
|
318
349
|
#
|
319
350
|
# The schema still contains "category", but now the model omits it, so any meta-driven code or
|
@@ -339,7 +370,7 @@ module ActiveRecord
|
|
339
370
|
end
|
340
371
|
end
|
341
372
|
|
342
|
-
def reset_sequence_name
|
373
|
+
def reset_sequence_name # :nodoc:
|
343
374
|
@explicit_sequence_name = false
|
344
375
|
@sequence_name = connection.default_sequence_name(table_name, primary_key)
|
345
376
|
end
|
@@ -398,6 +429,12 @@ module ActiveRecord
|
|
398
429
|
@columns ||= columns_hash.values.freeze
|
399
430
|
end
|
400
431
|
|
432
|
+
def _returning_columns_for_insert # :nodoc:
|
433
|
+
@_returning_columns_for_insert ||= columns.filter_map do |c|
|
434
|
+
c.name if connection.return_value_after_insert?(c)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
401
438
|
def attribute_types # :nodoc:
|
402
439
|
load_schema
|
403
440
|
@attribute_types ||= Hash.new(Type.default_value)
|
@@ -486,9 +523,9 @@ module ActiveRecord
|
|
486
523
|
#
|
487
524
|
# The most common usage pattern for this method is probably in a migration,
|
488
525
|
# when just after creating a table you want to populate it with some default
|
489
|
-
# values,
|
526
|
+
# values, e.g.:
|
490
527
|
#
|
491
|
-
# class CreateJobLevels < ActiveRecord::Migration[
|
528
|
+
# class CreateJobLevels < ActiveRecord::Migration[7.1]
|
492
529
|
# def up
|
493
530
|
# create_table :job_levels do |t|
|
494
531
|
# t.integer :id
|
@@ -516,35 +553,61 @@ module ActiveRecord
|
|
516
553
|
initialize_find_by_cache
|
517
554
|
end
|
518
555
|
|
556
|
+
def load_schema # :nodoc:
|
557
|
+
return if schema_loaded?
|
558
|
+
@load_schema_monitor.synchronize do
|
559
|
+
return if @columns_hash
|
560
|
+
|
561
|
+
load_schema!
|
562
|
+
|
563
|
+
@schema_loaded = true
|
564
|
+
rescue
|
565
|
+
reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
|
566
|
+
raise
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
519
570
|
protected
|
520
571
|
def initialize_load_schema_monitor
|
521
572
|
@load_schema_monitor = Monitor.new
|
522
573
|
end
|
523
574
|
|
575
|
+
def reload_schema_from_cache(recursive = true)
|
576
|
+
@_returning_columns_for_insert = nil
|
577
|
+
@arel_table = nil
|
578
|
+
@column_names = nil
|
579
|
+
@symbol_column_to_string_name_hash = nil
|
580
|
+
@attribute_types = nil
|
581
|
+
@content_columns = nil
|
582
|
+
@default_attributes = nil
|
583
|
+
@column_defaults = nil
|
584
|
+
@attributes_builder = nil
|
585
|
+
@columns = nil
|
586
|
+
@columns_hash = nil
|
587
|
+
@schema_loaded = false
|
588
|
+
@attribute_names = nil
|
589
|
+
@yaml_encoder = nil
|
590
|
+
if recursive
|
591
|
+
subclasses.each do |descendant|
|
592
|
+
descendant.send(:reload_schema_from_cache)
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
524
597
|
private
|
525
598
|
def inherited(child_class)
|
526
599
|
super
|
527
600
|
child_class.initialize_load_schema_monitor
|
601
|
+
child_class.reload_schema_from_cache(false)
|
602
|
+
child_class.class_eval do
|
603
|
+
@ignored_columns = nil
|
604
|
+
end
|
528
605
|
end
|
529
606
|
|
530
607
|
def schema_loaded?
|
531
608
|
defined?(@schema_loaded) && @schema_loaded
|
532
609
|
end
|
533
610
|
|
534
|
-
def load_schema
|
535
|
-
return if schema_loaded?
|
536
|
-
@load_schema_monitor.synchronize do
|
537
|
-
return if defined?(@columns_hash) && @columns_hash
|
538
|
-
|
539
|
-
load_schema!
|
540
|
-
|
541
|
-
@schema_loaded = true
|
542
|
-
rescue
|
543
|
-
reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
|
544
|
-
raise
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
611
|
def load_schema!
|
549
612
|
unless table_name
|
550
613
|
raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
|
@@ -556,39 +619,21 @@ module ActiveRecord
|
|
556
619
|
@columns_hash.each do |name, column|
|
557
620
|
type = connection.lookup_cast_type_from_column(column)
|
558
621
|
type = _convert_type_from_options(type)
|
559
|
-
warn_if_deprecated_type(column)
|
560
622
|
define_attribute(
|
561
623
|
name,
|
562
624
|
type,
|
563
625
|
default: column.default,
|
564
626
|
user_provided_default: false
|
565
627
|
)
|
628
|
+
alias_attribute :id_value, :id if name == "id"
|
566
629
|
end
|
567
|
-
end
|
568
630
|
|
569
|
-
|
570
|
-
@arel_table = nil
|
571
|
-
@column_names = nil
|
572
|
-
@symbol_column_to_string_name_hash = nil
|
573
|
-
@attribute_types = nil
|
574
|
-
@content_columns = nil
|
575
|
-
@default_attributes = nil
|
576
|
-
@column_defaults = nil
|
577
|
-
@inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
|
578
|
-
@attributes_builder = nil
|
579
|
-
@columns = nil
|
580
|
-
@columns_hash = nil
|
581
|
-
@schema_loaded = false
|
582
|
-
@attribute_names = nil
|
583
|
-
@yaml_encoder = nil
|
584
|
-
direct_descendants.each do |descendant|
|
585
|
-
descendant.send(:reload_schema_from_cache)
|
586
|
-
end
|
631
|
+
super
|
587
632
|
end
|
588
633
|
|
589
634
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
590
|
-
def undecorated_table_name(
|
591
|
-
table_name =
|
635
|
+
def undecorated_table_name(model_name)
|
636
|
+
table_name = model_name.to_s.demodulize.underscore
|
592
637
|
pluralize_table_names ? table_name.pluralize : table_name
|
593
638
|
end
|
594
639
|
|
@@ -602,9 +647,9 @@ module ActiveRecord
|
|
602
647
|
contained += "_"
|
603
648
|
end
|
604
649
|
|
605
|
-
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(
|
650
|
+
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}"
|
606
651
|
else
|
607
|
-
# STI subclasses always use their superclass' table.
|
652
|
+
# STI subclasses always use their superclass's table.
|
608
653
|
base_class.table_name
|
609
654
|
end
|
610
655
|
end
|
@@ -616,32 +661,6 @@ module ActiveRecord
|
|
616
661
|
type
|
617
662
|
end
|
618
663
|
end
|
619
|
-
|
620
|
-
def warn_if_deprecated_type(column)
|
621
|
-
return if attributes_to_define_after_schema_loads.key?(column.name)
|
622
|
-
return unless column.respond_to?(:oid)
|
623
|
-
|
624
|
-
if column.array?
|
625
|
-
array_arguments = ", array: true"
|
626
|
-
else
|
627
|
-
array_arguments = ""
|
628
|
-
end
|
629
|
-
|
630
|
-
if column.sql_type.start_with?("interval")
|
631
|
-
precision_arguments = column.precision.presence && ", precision: #{column.precision}"
|
632
|
-
ActiveSupport::Deprecation.warn(<<~WARNING)
|
633
|
-
The behavior of the `:interval` type will be changing in Rails 7.0
|
634
|
-
to return an `ActiveSupport::Duration` object. If you'd like to keep
|
635
|
-
the old behavior, you can add this line to #{self.name} model:
|
636
|
-
|
637
|
-
attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
|
638
|
-
|
639
|
-
If you'd like the new behavior today, you can add this line:
|
640
|
-
|
641
|
-
attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
|
642
|
-
WARNING
|
643
|
-
end
|
644
|
-
end
|
645
664
|
end
|
646
665
|
end
|
647
666
|
end
|
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/redefine_method"
|
|
5
5
|
require "active_support/core_ext/hash/indifferent_access"
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
|
-
module NestedAttributes
|
8
|
+
module NestedAttributes # :nodoc:
|
9
9
|
class TooManyRecords < ActiveRecordError
|
10
10
|
end
|
11
11
|
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
class_attribute :nested_attributes_options, instance_writer: false, default: {}
|
16
16
|
end
|
17
17
|
|
18
|
-
# = Active Record Nested Attributes
|
18
|
+
# = Active Record Nested \Attributes
|
19
19
|
#
|
20
20
|
# Nested attributes allow you to save attributes on associated records
|
21
21
|
# through the parent. By default nested attribute updating is turned off
|
@@ -180,7 +180,7 @@ module ActiveRecord
|
|
180
180
|
# member.posts.second.title # => '[UPDATED] other post'
|
181
181
|
#
|
182
182
|
# However, the above applies if the parent model is being updated as well.
|
183
|
-
# For example,
|
183
|
+
# For example, if you wanted to create a +member+ named _joe_ and wanted to
|
184
184
|
# update the +posts+ at the same time, that would give an
|
185
185
|
# ActiveRecord::RecordNotFound error.
|
186
186
|
#
|
@@ -245,18 +245,19 @@ module ActiveRecord
|
|
245
245
|
#
|
246
246
|
# === Validating the presence of a parent model
|
247
247
|
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
248
|
+
# The +belongs_to+ association validates the presence of the parent model
|
249
|
+
# by default. You can disable this behavior by specifying <code>optional: true</code>.
|
250
|
+
# This can be used, for example, when conditionally validating the presence
|
251
|
+
# of the parent model:
|
251
252
|
#
|
252
|
-
# class
|
253
|
-
# has_many :
|
254
|
-
# accepts_nested_attributes_for :
|
253
|
+
# class Veterinarian < ActiveRecord::Base
|
254
|
+
# has_many :patients, inverse_of: :veterinarian
|
255
|
+
# accepts_nested_attributes_for :patients
|
255
256
|
# end
|
256
257
|
#
|
257
|
-
# class
|
258
|
-
# belongs_to :
|
259
|
-
#
|
258
|
+
# class Patient < ActiveRecord::Base
|
259
|
+
# belongs_to :veterinarian, inverse_of: :patients, optional: true
|
260
|
+
# validates :veterinarian, presence: true, unless: -> { awaiting_intake }
|
260
261
|
# end
|
261
262
|
#
|
262
263
|
# Note that if you do not specify the +:inverse_of+ option, then
|
@@ -279,6 +280,26 @@ module ActiveRecord
|
|
279
280
|
# member = Member.new
|
280
281
|
# member.avatar_attributes = {icon: 'sad'}
|
281
282
|
# member.avatar.width # => 200
|
283
|
+
#
|
284
|
+
# === Creating forms with nested attributes
|
285
|
+
#
|
286
|
+
# Use ActionView::Helpers::FormHelper#fields_for to create form elements
|
287
|
+
# for updating or destroying nested attributes.
|
288
|
+
#
|
289
|
+
# === Testing
|
290
|
+
#
|
291
|
+
# If you are using ActionView::Helpers::FormHelper#fields_for, your integration
|
292
|
+
# tests should replicate the HTML structure it provides. For example;
|
293
|
+
#
|
294
|
+
# post members_path, params: {
|
295
|
+
# member: {
|
296
|
+
# name: 'joe',
|
297
|
+
# posts_attributes: {
|
298
|
+
# '0' => { title: 'Foo' },
|
299
|
+
# '1' => { title: 'Bar' }
|
300
|
+
# }
|
301
|
+
# }
|
302
|
+
# }
|
282
303
|
module ClassMethods
|
283
304
|
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
|
284
305
|
|
@@ -374,11 +395,11 @@ module ActiveRecord
|
|
374
395
|
end
|
375
396
|
end
|
376
397
|
|
377
|
-
# Returns ActiveRecord::AutosaveAssociation
|
398
|
+
# Returns ActiveRecord::AutosaveAssociation#marked_for_destruction? It's
|
378
399
|
# used in conjunction with fields_for to build a form element for the
|
379
400
|
# destruction of this association.
|
380
401
|
#
|
381
|
-
# See ActionView::Helpers::FormHelper
|
402
|
+
# See ActionView::Helpers::FormHelper#fields_for for more info.
|
382
403
|
def _destroy
|
383
404
|
marked_for_destruction?
|
384
405
|
end
|
@@ -486,7 +507,7 @@ module ActiveRecord
|
|
486
507
|
existing_records = if association.loaded?
|
487
508
|
association.target
|
488
509
|
else
|
489
|
-
attribute_ids = attributes_collection.
|
510
|
+
attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] }
|
490
511
|
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
|
491
512
|
end
|
492
513
|
|
@@ -26,20 +26,20 @@ module ActiveRecord
|
|
26
26
|
end
|
27
27
|
|
28
28
|
class << self
|
29
|
-
def apply_to(klass)
|
29
|
+
def apply_to(klass) # :nodoc:
|
30
30
|
klasses.push(klass)
|
31
31
|
yield
|
32
32
|
ensure
|
33
33
|
klasses.pop
|
34
34
|
end
|
35
35
|
|
36
|
-
def applied_to?(klass)
|
36
|
+
def applied_to?(klass) # :nodoc:
|
37
37
|
klasses.any? { |k| k >= klass }
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
41
41
|
def klasses
|
42
|
-
|
42
|
+
ActiveSupport::IsolatedExecutionState[:active_record_no_touching_classes] ||= []
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module Normalization
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :normalized_attributes, default: Set.new
|
9
|
+
|
10
|
+
before_validation :normalize_changed_in_place_attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
# Normalizes a specified attribute using its declared normalizations.
|
14
|
+
#
|
15
|
+
# ==== Examples
|
16
|
+
#
|
17
|
+
# class User < ActiveRecord::Base
|
18
|
+
# normalizes :email, with: -> email { email.strip.downcase }
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# legacy_user = User.find(1)
|
22
|
+
# legacy_user.email # => " CRUISE-CONTROL@EXAMPLE.COM\n"
|
23
|
+
# legacy_user.normalize_attribute(:email)
|
24
|
+
# legacy_user.email # => "cruise-control@example.com"
|
25
|
+
# legacy_user.save
|
26
|
+
def normalize_attribute(name)
|
27
|
+
# Treat the value as a new, unnormalized value.
|
28
|
+
self[name] = self[name]
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
# Declares a normalization for one or more attributes. The normalization
|
33
|
+
# is applied when the attribute is assigned or updated, and the normalized
|
34
|
+
# value will be persisted to the database. The normalization is also
|
35
|
+
# applied to the corresponding keyword argument of query methods. This
|
36
|
+
# allows a record to be created and later queried using unnormalized
|
37
|
+
# values.
|
38
|
+
#
|
39
|
+
# However, to prevent confusion, the normalization will not be applied
|
40
|
+
# when the attribute is fetched from the database. This means that if a
|
41
|
+
# record was persisted before the normalization was declared, the record's
|
42
|
+
# attribute will not be normalized until either it is assigned a new
|
43
|
+
# value, or it is explicitly migrated via Normalization#normalize_attribute.
|
44
|
+
#
|
45
|
+
# Because the normalization may be applied multiple times, it should be
|
46
|
+
# _idempotent_. In other words, applying the normalization more than once
|
47
|
+
# should have the same result as applying it only once.
|
48
|
+
#
|
49
|
+
# By default, the normalization will not be applied to +nil+ values. This
|
50
|
+
# behavior can be changed with the +:apply_to_nil+ option.
|
51
|
+
#
|
52
|
+
# ==== Options
|
53
|
+
#
|
54
|
+
# * +:with+ - Any callable object that accepts the attribute's value as
|
55
|
+
# its sole argument, and returns it normalized.
|
56
|
+
# * +:apply_to_nil+ - Whether to apply the normalization to +nil+ values.
|
57
|
+
# Defaults to +false+.
|
58
|
+
#
|
59
|
+
# ==== Examples
|
60
|
+
#
|
61
|
+
# class User < ActiveRecord::Base
|
62
|
+
# normalizes :email, with: -> email { email.strip.downcase }
|
63
|
+
# normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
|
67
|
+
# user.email # => "cruise-control@example.com"
|
68
|
+
#
|
69
|
+
# user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
|
70
|
+
# user.email # => "cruise-control@example.com"
|
71
|
+
# user.email_before_type_cast # => "cruise-control@example.com"
|
72
|
+
#
|
73
|
+
# User.where(email: "\tCRUISE-CONTROL@EXAMPLE.COM ").count # => 1
|
74
|
+
# User.where(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]).count # => 0
|
75
|
+
#
|
76
|
+
# User.exists?(email: "\tCRUISE-CONTROL@EXAMPLE.COM ") # => true
|
77
|
+
# User.exists?(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]) # => false
|
78
|
+
#
|
79
|
+
# User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
|
80
|
+
def normalizes(*names, with:, apply_to_nil: false)
|
81
|
+
names.each do |name|
|
82
|
+
attribute(name) do |cast_type|
|
83
|
+
NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
self.normalized_attributes += names.map(&:to_sym)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Normalizes a given +value+ using normalizations declared for +name+.
|
91
|
+
#
|
92
|
+
# ==== Examples
|
93
|
+
#
|
94
|
+
# class User < ActiveRecord::Base
|
95
|
+
# normalizes :email, with: -> email { email.strip.downcase }
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# User.normalize_value_for(:email, " CRUISE-CONTROL@EXAMPLE.COM\n")
|
99
|
+
# # => "cruise-control@example.com"
|
100
|
+
def normalize_value_for(name, value)
|
101
|
+
type_for_attribute(name).cast(value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
def normalize_changed_in_place_attributes
|
107
|
+
self.class.normalized_attributes.each do |name|
|
108
|
+
normalize_attribute(name) if attribute_changed_in_place?(name)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class NormalizedValueType < DelegateClass(ActiveModel::Type::Value) # :nodoc:
|
113
|
+
include ActiveModel::Type::SerializeCastValue
|
114
|
+
|
115
|
+
attr_reader :cast_type, :normalizer, :normalize_nil
|
116
|
+
alias :normalize_nil? :normalize_nil
|
117
|
+
|
118
|
+
def initialize(cast_type:, normalizer:, normalize_nil:)
|
119
|
+
@cast_type = cast_type
|
120
|
+
@normalizer = normalizer
|
121
|
+
@normalize_nil = normalize_nil
|
122
|
+
super(cast_type)
|
123
|
+
end
|
124
|
+
|
125
|
+
def cast(value)
|
126
|
+
normalize(super(value))
|
127
|
+
end
|
128
|
+
|
129
|
+
def serialize(value)
|
130
|
+
serialize_cast_value(cast(value))
|
131
|
+
end
|
132
|
+
|
133
|
+
def serialize_cast_value(value)
|
134
|
+
ActiveModel::Type::SerializeCastValue.serialize(cast_type, value)
|
135
|
+
end
|
136
|
+
|
137
|
+
def ==(other)
|
138
|
+
self.class == other.class &&
|
139
|
+
normalize_nil? == other.normalize_nil? &&
|
140
|
+
normalizer == other.normalizer &&
|
141
|
+
cast_type == other.cast_type
|
142
|
+
end
|
143
|
+
alias eql? ==
|
144
|
+
|
145
|
+
def hash
|
146
|
+
[self.class, cast_type, normalizer, normalize_nil?].hash
|
147
|
+
end
|
148
|
+
|
149
|
+
def inspect
|
150
|
+
Kernel.instance_method(:inspect).bind_call(self)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
def normalize(value)
|
155
|
+
normalizer.call(value) unless value.nil? && !normalize_nil?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|