activerecord 7.0.8.7 → 7.2.3
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 +781 -1777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +30 -30
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +40 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +35 -21
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -33
- data/lib/active_record/attributes.rb +96 -71
- data/lib/active_record/autosave_association.rb +81 -39
- data/lib/active_record/base.rb +11 -7
- data/lib/active_record/callbacks.rb +11 -25
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
- 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 +124 -1
- data/lib/active_record/connection_handling.rb +101 -105
- data/lib/active_record/core.rb +273 -178
- data/lib/active_record/counter_cache.rb +69 -35
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +56 -27
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +46 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
- data/lib/active_record/encryption/encryptor.rb +35 -19
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +130 -28
- data/lib/active_record/errors.rb +154 -34
- data/lib/active_record/explain.rb +21 -12
- 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 +48 -10
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +236 -118
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +96 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +35 -10
- data/lib/active_record/railtie.rb +131 -87
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +147 -155
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +270 -108
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +97 -21
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
- 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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +585 -109
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +592 -92
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +90 -23
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +33 -11
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- 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 +108 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- 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 +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- 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 +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +4 -2
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +114 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +56 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/message_pack"
|
|
4
|
+
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
module Encryption
|
|
7
|
+
# A message serializer that serializes +Messages+ with MessagePack.
|
|
8
|
+
#
|
|
9
|
+
# The message is converted to a hash with this structure:
|
|
10
|
+
#
|
|
11
|
+
# {
|
|
12
|
+
# p: <payload>,
|
|
13
|
+
# h: {
|
|
14
|
+
# header1: value1,
|
|
15
|
+
# header2: value2,
|
|
16
|
+
# ...
|
|
17
|
+
# }
|
|
18
|
+
# }
|
|
19
|
+
#
|
|
20
|
+
# Then it is converted to the MessagePack format.
|
|
21
|
+
class MessagePackMessageSerializer
|
|
22
|
+
def dump(message)
|
|
23
|
+
raise Errors::ForbiddenClass unless message.is_a?(Message)
|
|
24
|
+
ActiveSupport::MessagePack.dump(message_to_hash(message))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load(serialized_content)
|
|
28
|
+
data = ActiveSupport::MessagePack.load(serialized_content)
|
|
29
|
+
hash_to_message(data, 1)
|
|
30
|
+
rescue RuntimeError
|
|
31
|
+
raise Errors::Decryption
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def binary?
|
|
35
|
+
true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def message_to_hash(message)
|
|
40
|
+
{
|
|
41
|
+
"p" => message.payload,
|
|
42
|
+
"h" => headers_to_hash(message.headers)
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def headers_to_hash(headers)
|
|
47
|
+
headers.transform_values do |value|
|
|
48
|
+
value.is_a?(Message) ? message_to_hash(value) : value
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def hash_to_message(data, level)
|
|
53
|
+
validate_message_data_format(data, level)
|
|
54
|
+
Message.new(payload: data["p"], headers: parse_properties(data["h"], level))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_message_data_format(data, level)
|
|
58
|
+
if level > 2
|
|
59
|
+
raise Errors::Decryption, "More than one level of hash nesting in headers is not supported"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
unless data.is_a?(Hash) && data.has_key?("p")
|
|
63
|
+
raise Errors::Decryption, "Invalid data format: hash without payload"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_properties(headers, level)
|
|
68
|
+
Properties.new.tap do |properties|
|
|
69
|
+
headers&.each do |key, value|
|
|
70
|
+
properties[key] = value.is_a?(Hash) ? hash_to_message(value, level + 1) : value
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
6
|
module Encryption
|
|
5
7
|
# A message serializer that serializes +Messages+ with JSON.
|
|
@@ -31,6 +33,10 @@ module ActiveRecord
|
|
|
31
33
|
JSON.dump message_to_json(message)
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
def binary?
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
private
|
|
35
41
|
def parse_message(data, level)
|
|
36
42
|
validate_message_data_format(data, level)
|
|
@@ -14,10 +14,10 @@ module ActiveRecord
|
|
|
14
14
|
#
|
|
15
15
|
# See +Properties::DEFAULT_PROPERTIES+, Key, Message
|
|
16
16
|
class Properties
|
|
17
|
-
ALLOWED_VALUE_CLASSES = [String, ActiveRecord::Encryption::Message, Numeric, TrueClass, FalseClass, Symbol, NilClass]
|
|
17
|
+
ALLOWED_VALUE_CLASSES = [String, ActiveRecord::Encryption::Message, Numeric, Integer, Float, BigDecimal, TrueClass, FalseClass, Symbol, NilClass]
|
|
18
18
|
|
|
19
19
|
delegate_missing_to :data
|
|
20
|
-
delegate :==, to: :data
|
|
20
|
+
delegate :==, :[], :each, :key?, to: :data
|
|
21
21
|
|
|
22
22
|
# For each entry it generates an accessor exposing the full name
|
|
23
23
|
DEFAULT_PROPERTIES = {
|
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def validate_value_type(value)
|
|
57
|
-
unless ALLOWED_VALUE_CLASSES.
|
|
57
|
+
unless ALLOWED_VALUE_CLASSES.include?(value.class) || ALLOWED_VALUE_CLASSES.any? { |klass| value.is_a?(klass) }
|
|
58
58
|
raise ActiveRecord::Encryption::Errors::ForbiddenClass, "Can't store a #{value.class}, only properties of type #{ALLOWED_VALUE_CLASSES.inspect} are allowed"
|
|
59
59
|
end
|
|
60
60
|
end
|
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
|
10
10
|
class Scheme
|
|
11
11
|
attr_accessor :previous_schemes
|
|
12
12
|
|
|
13
|
-
def initialize(key_provider: nil, key: nil, deterministic: nil, downcase: nil, ignore_case: nil,
|
|
13
|
+
def initialize(key_provider: nil, key: nil, deterministic: nil, support_unencrypted_data: nil, downcase: nil, ignore_case: nil,
|
|
14
14
|
previous_schemes: nil, **context_properties)
|
|
15
15
|
# Initializing all attributes to +nil+ as we want to allow a "not set" semantics so that we
|
|
16
16
|
# can merge schemes without overriding values with defaults. See +#merge+
|
|
@@ -18,6 +18,7 @@ module ActiveRecord
|
|
|
18
18
|
@key_provider_param = key_provider
|
|
19
19
|
@key = key
|
|
20
20
|
@deterministic = deterministic
|
|
21
|
+
@support_unencrypted_data = support_unencrypted_data
|
|
21
22
|
@downcase = downcase || ignore_case
|
|
22
23
|
@ignore_case = ignore_case
|
|
23
24
|
@previous_schemes_param = previous_schemes
|
|
@@ -36,7 +37,11 @@ module ActiveRecord
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def deterministic?
|
|
39
|
-
|
|
40
|
+
!!@deterministic
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def support_unencrypted_data?
|
|
44
|
+
@support_unencrypted_data.nil? ? ActiveRecord::Encryption.config.support_unencrypted_data : @support_unencrypted_data
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
def fixed?
|
|
@@ -45,10 +50,7 @@ module ActiveRecord
|
|
|
45
50
|
end
|
|
46
51
|
|
|
47
52
|
def key_provider
|
|
48
|
-
@
|
|
49
|
-
validate_keys!
|
|
50
|
-
@key_provider_param || build_key_provider
|
|
51
|
-
end
|
|
53
|
+
@key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def merge(other_scheme)
|
|
@@ -56,7 +58,7 @@ module ActiveRecord
|
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
def to_h
|
|
59
|
-
{ key_provider: @key_provider_param,
|
|
61
|
+
{ key_provider: @key_provider_param, deterministic: @deterministic, downcase: @downcase, ignore_case: @ignore_case,
|
|
60
62
|
previous_schemes: @previous_schemes_param, **@context_properties }.compact
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -68,31 +70,30 @@ module ActiveRecord
|
|
|
68
70
|
end
|
|
69
71
|
end
|
|
70
72
|
|
|
73
|
+
def compatible_with?(other_scheme)
|
|
74
|
+
deterministic? == other_scheme.deterministic?
|
|
75
|
+
end
|
|
76
|
+
|
|
71
77
|
private
|
|
72
78
|
def validate_config!
|
|
73
79
|
raise Errors::Configuration, "ignore_case: can only be used with deterministic encryption" if @ignore_case && !@deterministic
|
|
74
80
|
raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
|
|
75
81
|
end
|
|
76
82
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
def key_provider_from_key
|
|
84
|
+
@key_provider_from_key ||= if @key.present?
|
|
85
|
+
DerivedSecretKeyProvider.new(@key)
|
|
86
|
+
end
|
|
81
87
|
end
|
|
82
88
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"active_record_encryption.#{key} or by setting config.active_record.encryption.#{key}"
|
|
89
|
+
def deterministic_key_provider
|
|
90
|
+
@deterministic_key_provider ||= if @deterministic
|
|
91
|
+
DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
|
|
87
92
|
end
|
|
88
93
|
end
|
|
89
94
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if @deterministic && (deterministic_key = ActiveRecord::Encryption.config.deterministic_key)
|
|
94
|
-
DeterministicKeyProvider.new(deterministic_key)
|
|
95
|
-
end
|
|
95
|
+
def default_key_provider
|
|
96
|
+
ActiveRecord::Encryption.key_provider
|
|
96
97
|
end
|
|
97
98
|
end
|
|
98
99
|
end
|
|
@@ -8,6 +8,7 @@ module ActiveRecord
|
|
|
8
8
|
extend ActiveSupport::Autoload
|
|
9
9
|
|
|
10
10
|
eager_autoload do
|
|
11
|
+
autoload :AutoFilteredParameters
|
|
11
12
|
autoload :Cipher
|
|
12
13
|
autoload :Config
|
|
13
14
|
autoload :Configurable
|
|
@@ -52,4 +53,6 @@ module ActiveRecord
|
|
|
52
53
|
Cipher.eager_load!
|
|
53
54
|
end
|
|
54
55
|
end
|
|
56
|
+
|
|
57
|
+
ActiveSupport.run_load_hooks :active_record_encryption, Encryption
|
|
55
58
|
end
|
data/lib/active_record/enum.rb
CHANGED
|
@@ -111,23 +111,71 @@ module ActiveRecord
|
|
|
111
111
|
#
|
|
112
112
|
# conversation.comments_inactive!
|
|
113
113
|
# conversation.comments_active? # => false
|
|
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
|
+
# By default, an +ArgumentError+ will be raised when assigning an invalid value:
|
|
123
|
+
#
|
|
124
|
+
# class Conversation < ActiveRecord::Base
|
|
125
|
+
# enum :status, [ :active, :archived ]
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# conversation = Conversation.new
|
|
129
|
+
#
|
|
130
|
+
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
|
131
|
+
#
|
|
132
|
+
# If, instead, you want the enum value to be validated before saving, use the
|
|
133
|
+
# +:validate+ option:
|
|
134
|
+
#
|
|
135
|
+
# class Conversation < ActiveRecord::Base
|
|
136
|
+
# enum :status, [ :active, :archived ], validate: true
|
|
137
|
+
# end
|
|
138
|
+
#
|
|
139
|
+
# conversation = Conversation.new
|
|
140
|
+
#
|
|
141
|
+
# conversation.status = :unknown
|
|
142
|
+
# conversation.valid? # => false
|
|
143
|
+
#
|
|
144
|
+
# conversation.status = nil
|
|
145
|
+
# conversation.valid? # => false
|
|
146
|
+
#
|
|
147
|
+
# conversation.status = :active
|
|
148
|
+
# conversation.valid? # => true
|
|
149
|
+
#
|
|
150
|
+
# You may also pass additional validation options:
|
|
151
|
+
#
|
|
152
|
+
# class Conversation < ActiveRecord::Base
|
|
153
|
+
# enum :status, [ :active, :archived ], validate: { allow_nil: true }
|
|
154
|
+
# end
|
|
155
|
+
#
|
|
156
|
+
# conversation = Conversation.new
|
|
157
|
+
#
|
|
158
|
+
# conversation.status = :unknown
|
|
159
|
+
# conversation.valid? # => false
|
|
160
|
+
#
|
|
161
|
+
# conversation.status = nil
|
|
162
|
+
# conversation.valid? # => true
|
|
163
|
+
#
|
|
164
|
+
# conversation.status = :active
|
|
165
|
+
# conversation.valid? # => true
|
|
114
166
|
module Enum
|
|
115
167
|
def self.extended(base) # :nodoc:
|
|
116
168
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
|
117
169
|
end
|
|
118
170
|
|
|
119
|
-
def inherited(base) # :nodoc:
|
|
120
|
-
base.defined_enums = defined_enums.deep_dup
|
|
121
|
-
super
|
|
122
|
-
end
|
|
123
|
-
|
|
124
171
|
class EnumType < Type::Value # :nodoc:
|
|
125
172
|
delegate :type, to: :subtype
|
|
126
173
|
|
|
127
|
-
def initialize(name, mapping, subtype)
|
|
174
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
|
128
175
|
@name = name
|
|
129
176
|
@mapping = mapping
|
|
130
177
|
@subtype = subtype
|
|
178
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
|
131
179
|
end
|
|
132
180
|
|
|
133
181
|
def cast(value)
|
|
@@ -153,6 +201,8 @@ module ActiveRecord
|
|
|
153
201
|
end
|
|
154
202
|
|
|
155
203
|
def assert_valid_value(value)
|
|
204
|
+
return unless @_raise_on_invalid_values
|
|
205
|
+
|
|
156
206
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
|
157
207
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
|
158
208
|
end
|
|
@@ -170,15 +220,28 @@ module ActiveRecord
|
|
|
170
220
|
return _enum(name, values, **options)
|
|
171
221
|
end
|
|
172
222
|
|
|
173
|
-
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
|
|
223
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
|
174
224
|
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
|
175
225
|
|
|
176
226
|
definitions.each { |name, values| _enum(name, values, **options) }
|
|
227
|
+
|
|
228
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
229
|
+
Defining enums with keyword arguments is deprecated and will be removed
|
|
230
|
+
in Rails 8.0. Positional arguments should be used instead:
|
|
231
|
+
|
|
232
|
+
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
|
233
|
+
MSG
|
|
177
234
|
end
|
|
178
235
|
|
|
179
236
|
private
|
|
180
|
-
def
|
|
237
|
+
def inherited(base)
|
|
238
|
+
base.defined_enums = defined_enums.deep_dup
|
|
239
|
+
super
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
|
181
243
|
assert_valid_enum_definition_values(values)
|
|
244
|
+
assert_valid_enum_options(options)
|
|
182
245
|
# statuses = { }
|
|
183
246
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
|
184
247
|
name = name.to_s
|
|
@@ -191,9 +254,17 @@ module ActiveRecord
|
|
|
191
254
|
detect_enum_conflict!(name, name)
|
|
192
255
|
detect_enum_conflict!(name, "#{name}=")
|
|
193
256
|
|
|
194
|
-
attribute(name, **options)
|
|
257
|
+
attribute(name, **options)
|
|
258
|
+
|
|
259
|
+
decorate_attributes([name]) do |_name, subtype|
|
|
260
|
+
if subtype == ActiveModel::Type.default_value
|
|
261
|
+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
|
262
|
+
" backed by a database column or declared with an explicit type" \
|
|
263
|
+
" via `attribute`."
|
|
264
|
+
end
|
|
265
|
+
|
|
195
266
|
subtype = subtype.subtype if EnumType === subtype
|
|
196
|
-
EnumType.new(name, enum_values, subtype)
|
|
267
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
|
197
268
|
end
|
|
198
269
|
|
|
199
270
|
value_method_names = []
|
|
@@ -213,18 +284,24 @@ module ActiveRecord
|
|
|
213
284
|
|
|
214
285
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
|
215
286
|
value_method_names << value_method_name
|
|
216
|
-
define_enum_methods(name, value_method_name, value, scopes)
|
|
287
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
|
217
288
|
|
|
218
289
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
|
219
290
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
|
220
291
|
|
|
221
292
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
|
222
293
|
value_method_names << value_method_alias
|
|
223
|
-
define_enum_methods(name, value_method_alias, value, scopes)
|
|
294
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
|
224
295
|
end
|
|
225
296
|
end
|
|
226
297
|
end
|
|
227
298
|
detect_negative_enum_conditions!(value_method_names) if scopes
|
|
299
|
+
|
|
300
|
+
if validate
|
|
301
|
+
validate = {} unless Hash === validate
|
|
302
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
|
303
|
+
end
|
|
304
|
+
|
|
228
305
|
enum_values.freeze
|
|
229
306
|
end
|
|
230
307
|
|
|
@@ -236,21 +313,23 @@ module ActiveRecord
|
|
|
236
313
|
private
|
|
237
314
|
attr_reader :klass
|
|
238
315
|
|
|
239
|
-
def define_enum_methods(name, value_method_name, value, scopes)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
316
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
|
317
|
+
if instance_methods
|
|
318
|
+
# def active?() status_for_database == 0 end
|
|
319
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
|
320
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
|
243
321
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
322
|
+
# def active!() update!(status: 0) end
|
|
323
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
|
324
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
|
325
|
+
end
|
|
247
326
|
|
|
248
|
-
# scope :active, -> { where(status: 0) }
|
|
249
|
-
# scope :not_active, -> { where.not(status: 0) }
|
|
250
327
|
if scopes
|
|
328
|
+
# scope :active, -> { where(status: 0) }
|
|
251
329
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
|
252
330
|
klass.scope value_method_name, -> { where(name => value) }
|
|
253
331
|
|
|
332
|
+
# scope :not_active, -> { where.not(status: 0) }
|
|
254
333
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
|
255
334
|
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
|
256
335
|
end
|
|
@@ -267,15 +346,36 @@ module ActiveRecord
|
|
|
267
346
|
end
|
|
268
347
|
|
|
269
348
|
def assert_valid_enum_definition_values(values)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
349
|
+
case values
|
|
350
|
+
when Hash
|
|
351
|
+
if values.empty?
|
|
352
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
if values.keys.any?(&:blank?)
|
|
356
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
|
357
|
+
end
|
|
358
|
+
when Array
|
|
359
|
+
if values.empty?
|
|
360
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
unless values.all?(Symbol) || values.all?(String)
|
|
364
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
if values.any?(&:blank?)
|
|
368
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
|
369
|
+
end
|
|
370
|
+
else
|
|
371
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
|
275
372
|
end
|
|
373
|
+
end
|
|
276
374
|
|
|
277
|
-
|
|
278
|
-
|
|
375
|
+
def assert_valid_enum_options(options)
|
|
376
|
+
invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
|
|
377
|
+
unless invalid_keys.empty?
|
|
378
|
+
raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
|
|
279
379
|
end
|
|
280
380
|
end
|
|
281
381
|
|
|
@@ -290,6 +390,8 @@ module ActiveRecord
|
|
|
290
390
|
raise_conflict_error(enum_name, method_name, type: "class")
|
|
291
391
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
|
292
392
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
|
393
|
+
elsif klass_method && method_name.to_sym == :id
|
|
394
|
+
raise_conflict_error(enum_name, method_name)
|
|
293
395
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
|
294
396
|
raise_conflict_error(enum_name, method_name)
|
|
295
397
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|