activerecord 6.1.7 → 7.2.2
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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- 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/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +284 -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 +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- 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 +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- 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 +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- 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 +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- 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 +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -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 +170 -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 +157 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- 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 +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +59 -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 +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- 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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -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 +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- 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 +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- 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 -7
- 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/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- 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/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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 +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- 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/templates/create_table_migration.rb.tt +4 -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 +103 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
data/lib/active_record/enum.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/hash/slice"
|
3
4
|
require "active_support/core_ext/object/deep_dup"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -7,7 +8,7 @@ module ActiveRecord
|
|
7
8
|
# but can be queried by name. Example:
|
8
9
|
#
|
9
10
|
# class Conversation < ActiveRecord::Base
|
10
|
-
# enum status
|
11
|
+
# enum :status, [ :active, :archived ]
|
11
12
|
# end
|
12
13
|
#
|
13
14
|
# # conversation.update! status: 0
|
@@ -41,26 +42,33 @@ module ActiveRecord
|
|
41
42
|
# Conversation.where(status: [:active, :archived])
|
42
43
|
# Conversation.where.not(status: :active)
|
43
44
|
#
|
44
|
-
# Defining scopes can be disabled by setting +:
|
45
|
+
# Defining scopes can be disabled by setting +:scopes+ to +false+.
|
45
46
|
#
|
46
47
|
# class Conversation < ActiveRecord::Base
|
47
|
-
# enum status
|
48
|
+
# enum :status, [ :active, :archived ], scopes: false
|
48
49
|
# end
|
49
50
|
#
|
50
|
-
# You can set the default enum value by setting +:
|
51
|
+
# You can set the default enum value by setting +:default+, like:
|
51
52
|
#
|
52
53
|
# class Conversation < ActiveRecord::Base
|
53
|
-
# enum status
|
54
|
+
# enum :status, [ :active, :archived ], default: :active
|
54
55
|
# end
|
55
56
|
#
|
56
57
|
# conversation = Conversation.new
|
57
58
|
# conversation.status # => "active"
|
58
59
|
#
|
59
|
-
#
|
60
|
+
# It's possible to explicitly map the relation between attribute and
|
60
61
|
# database integer with a hash:
|
61
62
|
#
|
62
63
|
# class Conversation < ActiveRecord::Base
|
63
|
-
# enum status
|
64
|
+
# enum :status, active: 0, archived: 1
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Finally it's also possible to use a string column to persist the enumerated value.
|
68
|
+
# Note that this will likely lead to slower database queries:
|
69
|
+
#
|
70
|
+
# class Conversation < ActiveRecord::Base
|
71
|
+
# enum :status, active: "active", archived: "archived"
|
64
72
|
# end
|
65
73
|
#
|
66
74
|
# Note that when an array is used, the implicit mapping from the values to database
|
@@ -75,7 +83,7 @@ module ActiveRecord
|
|
75
83
|
#
|
76
84
|
# In rare circumstances you might need to access the mapping directly.
|
77
85
|
# The mappings are exposed through a class method with the pluralized attribute
|
78
|
-
# name, which return the mapping in a
|
86
|
+
# name, which return the mapping in a ActiveSupport::HashWithIndifferentAccess :
|
79
87
|
#
|
80
88
|
# Conversation.statuses[:active] # => 0
|
81
89
|
# Conversation.statuses["archived"] # => 1
|
@@ -85,14 +93,14 @@ module ActiveRecord
|
|
85
93
|
#
|
86
94
|
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
87
95
|
#
|
88
|
-
# You can use the +:
|
96
|
+
# You can use the +:prefix+ or +:suffix+ options when you need to define
|
89
97
|
# multiple enums with same values. If the passed value is +true+, the methods
|
90
98
|
# are prefixed/suffixed with the name of the enum. It is also possible to
|
91
99
|
# supply a custom value:
|
92
100
|
#
|
93
101
|
# class Conversation < ActiveRecord::Base
|
94
|
-
# enum status
|
95
|
-
# enum comments_status
|
102
|
+
# enum :status, [ :active, :archived ], suffix: true
|
103
|
+
# enum :comments_status, [ :active, :inactive ], prefix: :comments
|
96
104
|
# end
|
97
105
|
#
|
98
106
|
# With the above example, the bang and predicate methods along with the
|
@@ -103,24 +111,70 @@ module ActiveRecord
|
|
103
111
|
#
|
104
112
|
# conversation.comments_inactive!
|
105
113
|
# conversation.comments_active? # => false
|
106
|
-
|
114
|
+
#
|
115
|
+
# If you want to disable the auto-generated methods on the model, you can do
|
116
|
+
# so by setting the +:instance_methods+ option to false:
|
117
|
+
#
|
118
|
+
# class Conversation < ActiveRecord::Base
|
119
|
+
# enum :status, [ :active, :archived ], instance_methods: false
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# If you want the enum value to be validated before saving, use the option +:validate+:
|
123
|
+
#
|
124
|
+
# class Conversation < ActiveRecord::Base
|
125
|
+
# enum :status, [ :active, :archived ], validate: true
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# conversation = Conversation.new
|
129
|
+
#
|
130
|
+
# conversation.status = :unknown
|
131
|
+
# conversation.valid? # => false
|
132
|
+
#
|
133
|
+
# conversation.status = nil
|
134
|
+
# conversation.valid? # => false
|
135
|
+
#
|
136
|
+
# conversation.status = :active
|
137
|
+
# conversation.valid? # => true
|
138
|
+
#
|
139
|
+
# It is also possible to pass additional validation options:
|
140
|
+
#
|
141
|
+
# class Conversation < ActiveRecord::Base
|
142
|
+
# enum :status, [ :active, :archived ], validate: { allow_nil: true }
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# conversation = Conversation.new
|
146
|
+
#
|
147
|
+
# conversation.status = :unknown
|
148
|
+
# conversation.valid? # => false
|
149
|
+
#
|
150
|
+
# conversation.status = nil
|
151
|
+
# conversation.valid? # => true
|
152
|
+
#
|
153
|
+
# conversation.status = :active
|
154
|
+
# conversation.valid? # => true
|
155
|
+
#
|
156
|
+
# Otherwise +ArgumentError+ will raise:
|
157
|
+
#
|
158
|
+
# class Conversation < ActiveRecord::Base
|
159
|
+
# enum :status, [ :active, :archived ]
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# conversation = Conversation.new
|
163
|
+
#
|
164
|
+
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
107
165
|
module Enum
|
108
166
|
def self.extended(base) # :nodoc:
|
109
167
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
110
168
|
end
|
111
169
|
|
112
|
-
def inherited(base) # :nodoc:
|
113
|
-
base.defined_enums = defined_enums.deep_dup
|
114
|
-
super
|
115
|
-
end
|
116
|
-
|
117
170
|
class EnumType < Type::Value # :nodoc:
|
118
171
|
delegate :type, to: :subtype
|
119
172
|
|
120
|
-
def initialize(name, mapping, subtype)
|
173
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
121
174
|
@name = name
|
122
175
|
@mapping = mapping
|
123
176
|
@subtype = subtype
|
177
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
124
178
|
end
|
125
179
|
|
126
180
|
def cast(value)
|
@@ -128,10 +182,8 @@ module ActiveRecord
|
|
128
182
|
value.to_s
|
129
183
|
elsif mapping.has_value?(value)
|
130
184
|
mapping.key(value)
|
131
|
-
elsif value.blank?
|
132
|
-
nil
|
133
185
|
else
|
134
|
-
|
186
|
+
value.presence
|
135
187
|
end
|
136
188
|
end
|
137
189
|
|
@@ -140,10 +192,16 @@ module ActiveRecord
|
|
140
192
|
end
|
141
193
|
|
142
194
|
def serialize(value)
|
143
|
-
mapping.fetch(value, value)
|
195
|
+
subtype.serialize(mapping.fetch(value, value))
|
196
|
+
end
|
197
|
+
|
198
|
+
def serializable?(value, &block)
|
199
|
+
subtype.serializable?(mapping.fetch(value, value), &block)
|
144
200
|
end
|
145
201
|
|
146
202
|
def assert_valid_value(value)
|
203
|
+
return unless @_raise_on_invalid_values
|
204
|
+
|
147
205
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
148
206
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
149
207
|
end
|
@@ -155,16 +213,34 @@ module ActiveRecord
|
|
155
213
|
attr_reader :name, :mapping
|
156
214
|
end
|
157
215
|
|
158
|
-
def enum(
|
159
|
-
|
160
|
-
|
161
|
-
|
216
|
+
def enum(name = nil, values = nil, **options)
|
217
|
+
if name
|
218
|
+
values, options = options, {} unless values
|
219
|
+
return _enum(name, values, **options)
|
220
|
+
end
|
221
|
+
|
222
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
223
|
+
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
162
224
|
|
163
|
-
|
164
|
-
default[:default] = definitions.delete(:_default) if definitions.key?(:_default)
|
225
|
+
definitions.each { |name, values| _enum(name, values, **options) }
|
165
226
|
|
166
|
-
|
227
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
228
|
+
Defining enums with keyword arguments is deprecated and will be removed
|
229
|
+
in Rails 8.0. Positional arguments should be used instead:
|
230
|
+
|
231
|
+
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
232
|
+
MSG
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
def inherited(base)
|
237
|
+
base.defined_enums = defined_enums.deep_dup
|
238
|
+
super
|
239
|
+
end
|
240
|
+
|
241
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
167
242
|
assert_valid_enum_definition_values(values)
|
243
|
+
assert_valid_enum_options(options)
|
168
244
|
# statuses = { }
|
169
245
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
170
246
|
name = name.to_s
|
@@ -177,24 +253,27 @@ module ActiveRecord
|
|
177
253
|
detect_enum_conflict!(name, name)
|
178
254
|
detect_enum_conflict!(name, "#{name}=")
|
179
255
|
|
180
|
-
|
256
|
+
attribute(name, **options)
|
257
|
+
|
258
|
+
decorate_attributes([name]) do |_name, subtype|
|
259
|
+
if subtype == ActiveModel::Type.default_value
|
260
|
+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
261
|
+
" backed by a database column or declared with an explicit type" \
|
262
|
+
" via `attribute`."
|
263
|
+
end
|
181
264
|
|
182
|
-
|
183
|
-
EnumType.new(
|
265
|
+
subtype = subtype.subtype if EnumType === subtype
|
266
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
184
267
|
end
|
185
268
|
|
186
269
|
value_method_names = []
|
187
270
|
_enum_methods_module.module_eval do
|
188
|
-
prefix = if
|
189
|
-
"#{name}_"
|
190
|
-
elsif enum_prefix
|
191
|
-
"#{enum_prefix}_"
|
271
|
+
prefix = if prefix
|
272
|
+
prefix == true ? "#{name}_" : "#{prefix}_"
|
192
273
|
end
|
193
274
|
|
194
|
-
suffix = if
|
195
|
-
"_#{name}"
|
196
|
-
elsif enum_suffix
|
197
|
-
"_#{enum_suffix}"
|
275
|
+
suffix = if suffix
|
276
|
+
suffix == true ? "_#{name}" : "_#{suffix}"
|
198
277
|
end
|
199
278
|
|
200
279
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
@@ -204,23 +283,27 @@ module ActiveRecord
|
|
204
283
|
|
205
284
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
206
285
|
value_method_names << value_method_name
|
207
|
-
define_enum_methods(name, value_method_name, value,
|
286
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
208
287
|
|
209
288
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
210
289
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
211
290
|
|
212
291
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
213
292
|
value_method_names << value_method_alias
|
214
|
-
define_enum_methods(name, value_method_alias, value,
|
293
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
215
294
|
end
|
216
295
|
end
|
217
296
|
end
|
218
|
-
detect_negative_enum_conditions!(value_method_names) if
|
297
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
298
|
+
|
299
|
+
if validate
|
300
|
+
validate = {} unless Hash === validate
|
301
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
302
|
+
end
|
303
|
+
|
219
304
|
enum_values.freeze
|
220
305
|
end
|
221
|
-
end
|
222
306
|
|
223
|
-
private
|
224
307
|
class EnumMethods < Module # :nodoc:
|
225
308
|
def initialize(klass)
|
226
309
|
@klass = klass
|
@@ -229,21 +312,23 @@ module ActiveRecord
|
|
229
312
|
private
|
230
313
|
attr_reader :klass
|
231
314
|
|
232
|
-
def define_enum_methods(name, value_method_name, value,
|
233
|
-
|
234
|
-
|
235
|
-
|
315
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
316
|
+
if instance_methods
|
317
|
+
# def active?() status_for_database == 0 end
|
318
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
319
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
236
320
|
|
237
|
-
|
238
|
-
|
239
|
-
|
321
|
+
# def active!() update!(status: 0) end
|
322
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
323
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
324
|
+
end
|
240
325
|
|
241
|
-
|
242
|
-
|
243
|
-
if enum_scopes != false
|
326
|
+
if scopes
|
327
|
+
# scope :active, -> { where(status: 0) }
|
244
328
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
245
329
|
klass.scope value_method_name, -> { where(name => value) }
|
246
330
|
|
331
|
+
# scope :not_active, -> { where.not(status: 0) }
|
247
332
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
248
333
|
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
249
334
|
end
|
@@ -260,15 +345,36 @@ module ActiveRecord
|
|
260
345
|
end
|
261
346
|
|
262
347
|
def assert_valid_enum_definition_values(values)
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
348
|
+
case values
|
349
|
+
when Hash
|
350
|
+
if values.empty?
|
351
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
352
|
+
end
|
353
|
+
|
354
|
+
if values.keys.any?(&:blank?)
|
355
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
356
|
+
end
|
357
|
+
when Array
|
358
|
+
if values.empty?
|
359
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
360
|
+
end
|
361
|
+
|
362
|
+
unless values.all?(Symbol) || values.all?(String)
|
363
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
364
|
+
end
|
365
|
+
|
366
|
+
if values.any?(&:blank?)
|
367
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
368
|
+
end
|
369
|
+
else
|
370
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
268
371
|
end
|
372
|
+
end
|
269
373
|
|
270
|
-
|
271
|
-
|
374
|
+
def assert_valid_enum_options(options)
|
375
|
+
invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
|
376
|
+
unless invalid_keys.empty?
|
377
|
+
raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
|
272
378
|
end
|
273
379
|
end
|
274
380
|
|
@@ -283,6 +389,8 @@ module ActiveRecord
|
|
283
389
|
raise_conflict_error(enum_name, method_name, type: "class")
|
284
390
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
285
391
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
392
|
+
elsif klass_method && method_name.to_sym == :id
|
393
|
+
raise_conflict_error(enum_name, method_name)
|
286
394
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
287
395
|
raise_conflict_error(enum_name, method_name)
|
288
396
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|