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
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module"
|
4
|
+
require "active_support/core_ext/array"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module Encryption
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
eager_autoload do
|
11
|
+
autoload :AutoFilteredParameters
|
12
|
+
autoload :Cipher
|
13
|
+
autoload :Config
|
14
|
+
autoload :Configurable
|
15
|
+
autoload :Context
|
16
|
+
autoload :Contexts
|
17
|
+
autoload :DerivedSecretKeyProvider
|
18
|
+
autoload :EncryptableRecord
|
19
|
+
autoload :EncryptedAttributeType
|
20
|
+
autoload :EncryptedFixtures
|
21
|
+
autoload :EncryptingOnlyEncryptor
|
22
|
+
autoload :DeterministicKeyProvider
|
23
|
+
autoload :Encryptor
|
24
|
+
autoload :EnvelopeEncryptionKeyProvider
|
25
|
+
autoload :Errors
|
26
|
+
autoload :ExtendedDeterministicQueries
|
27
|
+
autoload :ExtendedDeterministicUniquenessValidator
|
28
|
+
autoload :Key
|
29
|
+
autoload :KeyGenerator
|
30
|
+
autoload :KeyProvider
|
31
|
+
autoload :Message
|
32
|
+
autoload :MessageSerializer
|
33
|
+
autoload :NullEncryptor
|
34
|
+
autoload :Properties
|
35
|
+
autoload :ReadOnlyNullEncryptor
|
36
|
+
autoload :Scheme
|
37
|
+
end
|
38
|
+
|
39
|
+
class Cipher
|
40
|
+
extend ActiveSupport::Autoload
|
41
|
+
|
42
|
+
eager_autoload do
|
43
|
+
autoload :Aes256Gcm
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
include Configurable
|
48
|
+
include Contexts
|
49
|
+
|
50
|
+
def self.eager_load!
|
51
|
+
super
|
52
|
+
|
53
|
+
Cipher.eager_load!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
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,79 @@ 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
|
113
|
-
|
114
|
-
|
170
|
+
def load_schema! # :nodoc:
|
171
|
+
attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
|
172
|
+
unless columns_hash.key?(name)
|
173
|
+
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
174
|
+
raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
|
175
|
+
end
|
176
|
+
end
|
115
177
|
end
|
116
178
|
|
117
179
|
class EnumType < Type::Value # :nodoc:
|
118
180
|
delegate :type, to: :subtype
|
119
181
|
|
120
|
-
def initialize(name, mapping, subtype)
|
182
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
121
183
|
@name = name
|
122
184
|
@mapping = mapping
|
123
185
|
@subtype = subtype
|
186
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
124
187
|
end
|
125
188
|
|
126
189
|
def cast(value)
|
@@ -128,10 +191,8 @@ module ActiveRecord
|
|
128
191
|
value.to_s
|
129
192
|
elsif mapping.has_value?(value)
|
130
193
|
mapping.key(value)
|
131
|
-
elsif value.blank?
|
132
|
-
nil
|
133
194
|
else
|
134
|
-
|
195
|
+
value.presence
|
135
196
|
end
|
136
197
|
end
|
137
198
|
|
@@ -140,10 +201,16 @@ module ActiveRecord
|
|
140
201
|
end
|
141
202
|
|
142
203
|
def serialize(value)
|
143
|
-
mapping.fetch(value, value)
|
204
|
+
subtype.serialize(mapping.fetch(value, value))
|
205
|
+
end
|
206
|
+
|
207
|
+
def serializable?(value, &block)
|
208
|
+
subtype.serializable?(mapping.fetch(value, value), &block)
|
144
209
|
end
|
145
210
|
|
146
211
|
def assert_valid_value(value)
|
212
|
+
return unless @_raise_on_invalid_values
|
213
|
+
|
147
214
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
148
215
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
149
216
|
end
|
@@ -155,15 +222,25 @@ module ActiveRecord
|
|
155
222
|
attr_reader :name, :mapping
|
156
223
|
end
|
157
224
|
|
158
|
-
def enum(
|
159
|
-
|
160
|
-
|
161
|
-
|
225
|
+
def enum(name = nil, values = nil, **options)
|
226
|
+
if name
|
227
|
+
values, options = options, {} unless values
|
228
|
+
return _enum(name, values, **options)
|
229
|
+
end
|
230
|
+
|
231
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
232
|
+
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
233
|
+
|
234
|
+
definitions.each { |name, values| _enum(name, values, **options) }
|
235
|
+
end
|
162
236
|
|
163
|
-
|
164
|
-
|
237
|
+
private
|
238
|
+
def inherited(base)
|
239
|
+
base.defined_enums = defined_enums.deep_dup
|
240
|
+
super
|
241
|
+
end
|
165
242
|
|
166
|
-
|
243
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
167
244
|
assert_valid_enum_definition_values(values)
|
168
245
|
# statuses = { }
|
169
246
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
@@ -177,24 +254,19 @@ module ActiveRecord
|
|
177
254
|
detect_enum_conflict!(name, name)
|
178
255
|
detect_enum_conflict!(name, "#{name}=")
|
179
256
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
EnumType.new(attr, enum_values, subtype)
|
257
|
+
attribute(name, **options) do |subtype|
|
258
|
+
subtype = subtype.subtype if EnumType === subtype
|
259
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
184
260
|
end
|
185
261
|
|
186
262
|
value_method_names = []
|
187
263
|
_enum_methods_module.module_eval do
|
188
|
-
prefix = if
|
189
|
-
"#{name}_"
|
190
|
-
elsif enum_prefix
|
191
|
-
"#{enum_prefix}_"
|
264
|
+
prefix = if prefix
|
265
|
+
prefix == true ? "#{name}_" : "#{prefix}_"
|
192
266
|
end
|
193
267
|
|
194
|
-
suffix = if
|
195
|
-
"_#{name}"
|
196
|
-
elsif enum_suffix
|
197
|
-
"_#{enum_suffix}"
|
268
|
+
suffix = if suffix
|
269
|
+
suffix == true ? "_#{name}" : "_#{suffix}"
|
198
270
|
end
|
199
271
|
|
200
272
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
@@ -204,23 +276,27 @@ module ActiveRecord
|
|
204
276
|
|
205
277
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
206
278
|
value_method_names << value_method_name
|
207
|
-
define_enum_methods(name, value_method_name, value,
|
279
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
208
280
|
|
209
281
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
210
282
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
211
283
|
|
212
284
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
213
285
|
value_method_names << value_method_alias
|
214
|
-
define_enum_methods(name, value_method_alias, value,
|
286
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
215
287
|
end
|
216
288
|
end
|
217
289
|
end
|
218
|
-
detect_negative_enum_conditions!(value_method_names) if
|
290
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
291
|
+
|
292
|
+
if validate
|
293
|
+
validate = {} unless Hash === validate
|
294
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
295
|
+
end
|
296
|
+
|
219
297
|
enum_values.freeze
|
220
298
|
end
|
221
|
-
end
|
222
299
|
|
223
|
-
private
|
224
300
|
class EnumMethods < Module # :nodoc:
|
225
301
|
def initialize(klass)
|
226
302
|
@klass = klass
|
@@ -229,21 +305,23 @@ module ActiveRecord
|
|
229
305
|
private
|
230
306
|
attr_reader :klass
|
231
307
|
|
232
|
-
def define_enum_methods(name, value_method_name, value,
|
233
|
-
|
234
|
-
|
235
|
-
|
308
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
309
|
+
if instance_methods
|
310
|
+
# def active?() status_for_database == 0 end
|
311
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
312
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
236
313
|
|
237
|
-
|
238
|
-
|
239
|
-
|
314
|
+
# def active!() update!(status: 0) end
|
315
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
316
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
317
|
+
end
|
240
318
|
|
241
|
-
|
242
|
-
|
243
|
-
if enum_scopes != false
|
319
|
+
if scopes
|
320
|
+
# scope :active, -> { where(status: 0) }
|
244
321
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
245
322
|
klass.scope value_method_name, -> { where(name => value) }
|
246
323
|
|
324
|
+
# scope :not_active, -> { where.not(status: 0) }
|
247
325
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
248
326
|
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
249
327
|
end
|
@@ -260,15 +338,29 @@ module ActiveRecord
|
|
260
338
|
end
|
261
339
|
|
262
340
|
def assert_valid_enum_definition_values(values)
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
end
|
341
|
+
case values
|
342
|
+
when Hash
|
343
|
+
if values.empty?
|
344
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
345
|
+
end
|
269
346
|
|
270
|
-
|
271
|
-
|
347
|
+
if values.keys.any?(&:blank?)
|
348
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
349
|
+
end
|
350
|
+
when Array
|
351
|
+
if values.empty?
|
352
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
353
|
+
end
|
354
|
+
|
355
|
+
unless values.all?(Symbol) || values.all?(String)
|
356
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
357
|
+
end
|
358
|
+
|
359
|
+
if values.any?(&:blank?)
|
360
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
361
|
+
end
|
362
|
+
else
|
363
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
272
364
|
end
|
273
365
|
end
|
274
366
|
|
@@ -283,6 +375,8 @@ module ActiveRecord
|
|
283
375
|
raise_conflict_error(enum_name, method_name, type: "class")
|
284
376
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
285
377
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
378
|
+
elsif klass_method && method_name.to_sym == :id
|
379
|
+
raise_conflict_error(enum_name, method_name)
|
286
380
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
287
381
|
raise_conflict_error(enum_name, method_name)
|
288
382
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|