activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- 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 +25 -19
- 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 +23 -8
- 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 +26 -14
- 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 +30 -27
- 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 +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- 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 +323 -88
- 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 +217 -63
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- 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 +53 -54
- 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 +101 -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 +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- 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 +45 -46
- 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 +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- 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 +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -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 +39 -10
- 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 +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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 +129 -28
- data/lib/active_record/errors.rb +151 -31
- 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 +29 -8
- 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 +234 -117
- 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 +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- 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 +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- 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 +18 -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 +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- 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 +7 -19
- data/lib/active_record/relation.rb +580 -90
- 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 +63 -14
- 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 +27 -6
- 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 +16 -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 +106 -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 +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/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/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.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -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 +112 -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 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -7,15 +7,69 @@ require "active_record/database_configurations/url_config"
|
|
7
7
|
require "active_record/database_configurations/connection_url_resolver"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
10
|
+
# = Active Record Database Configurations
|
11
|
+
#
|
12
|
+
# +ActiveRecord::DatabaseConfigurations+ returns an array of +DatabaseConfig+
|
13
|
+
# objects that are constructed from the application's database
|
14
|
+
# configuration hash or URL string.
|
15
|
+
#
|
16
|
+
# The array of +DatabaseConfig+ objects in an application default to either a
|
17
|
+
# HashConfig or UrlConfig. You can retrieve your application's config by using
|
18
|
+
# ActiveRecord::Base.configurations.
|
19
|
+
#
|
20
|
+
# If you register a custom handler, objects will be created according to the
|
21
|
+
# conditions of the handler. See ::register_db_config_handler for more on
|
22
|
+
# registering custom handlers.
|
13
23
|
class DatabaseConfigurations
|
14
24
|
class InvalidConfigurationError < StandardError; end
|
15
25
|
|
16
26
|
attr_reader :configurations
|
17
27
|
delegate :any?, to: :configurations
|
18
28
|
|
29
|
+
singleton_class.attr_accessor :db_config_handlers # :nodoc:
|
30
|
+
self.db_config_handlers = [] # :nodoc:
|
31
|
+
|
32
|
+
# Allows an application to register a custom handler for database configuration
|
33
|
+
# objects. This is useful for creating a custom handler that responds to
|
34
|
+
# methods your application needs but Active Record doesn't implement. For
|
35
|
+
# example if you are using Vitess, you may want your Vitess configurations
|
36
|
+
# to respond to `sharded?`. To implement this define the following in an
|
37
|
+
# initializer:
|
38
|
+
#
|
39
|
+
# ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
|
40
|
+
# next unless config.key?(:vitess)
|
41
|
+
# VitessConfig.new(env_name, name, config)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Note: applications must handle the condition in which custom config should be
|
45
|
+
# created in your handler registration otherwise all objects will use the custom
|
46
|
+
# handler.
|
47
|
+
#
|
48
|
+
# Then define your +VitessConfig+ to respond to the methods your application
|
49
|
+
# needs. It is recommended that you inherit from one of the existing
|
50
|
+
# database config classes to avoid having to reimplement all methods. Custom
|
51
|
+
# config handlers should only implement methods Active Record does not.
|
52
|
+
#
|
53
|
+
# class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
|
54
|
+
# def sharded?
|
55
|
+
# configuration_hash.fetch("sharded", false)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# For configs that have a +:vitess+ key, a +VitessConfig+ object will be
|
60
|
+
# created instead of a +UrlConfig+.
|
61
|
+
def self.register_db_config_handler(&block)
|
62
|
+
db_config_handlers << block
|
63
|
+
end
|
64
|
+
|
65
|
+
register_db_config_handler do |env_name, name, url, config|
|
66
|
+
if url
|
67
|
+
UrlConfig.new(env_name, name, url, config)
|
68
|
+
else
|
69
|
+
HashConfig.new(env_name, name, config)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
19
73
|
def initialize(configurations = {})
|
20
74
|
@configurations = build_configs(configurations)
|
21
75
|
end
|
@@ -23,8 +77,8 @@ module ActiveRecord
|
|
23
77
|
# Collects the configs for the environment and optionally the specification
|
24
78
|
# name passed in. To include replica configurations pass <tt>include_hidden: true</tt>.
|
25
79
|
#
|
26
|
-
# If a name is provided a single DatabaseConfig object will be
|
27
|
-
# returned, otherwise an array of DatabaseConfig objects will be
|
80
|
+
# If a name is provided a single +DatabaseConfig+ object will be
|
81
|
+
# returned, otherwise an array of +DatabaseConfig+ objects will be
|
28
82
|
# returned that corresponds with the environment and type requested.
|
29
83
|
#
|
30
84
|
# ==== Options
|
@@ -34,20 +88,14 @@ module ActiveRecord
|
|
34
88
|
# * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
|
35
89
|
# to +nil+. If no +env_name+ is specified the config for the default env and the
|
36
90
|
# passed +name+ will be returned.
|
37
|
-
# * <tt>
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# Defaults to +false+.
|
91
|
+
# * <tt>config_key:</tt> Selects configs that contain a particular key in the configuration
|
92
|
+
# hash. Useful for selecting configs that use a custom db config handler or finding
|
93
|
+
# configs with hashes that contain a particular key.
|
41
94
|
# * <tt>include_hidden:</tt> Determines whether to include replicas and configurations
|
42
|
-
# hidden by
|
95
|
+
# hidden by <tt>database_tasks: false</tt> in the returned list. Most of the time we're only
|
43
96
|
# iterating over the primary connections (i.e. migrations don't need to run for the
|
44
97
|
# write and read connection). Defaults to +false+.
|
45
|
-
def configs_for(env_name: nil, name: nil,
|
46
|
-
if include_replicas
|
47
|
-
include_hidden = include_replicas
|
48
|
-
ActiveSupport::Deprecation.warn("The kwarg `include_replicas` is deprecated in favor of `include_hidden`. When `include_hidden` is passed, configurations with `replica: true` or `database_tasks: false` will be returned. `include_replicas` will be removed in Rails 7.1.")
|
49
|
-
end
|
50
|
-
|
98
|
+
def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false)
|
51
99
|
env_name ||= default_env if name
|
52
100
|
configs = env_with_configs(env_name)
|
53
101
|
|
@@ -57,26 +105,32 @@ module ActiveRecord
|
|
57
105
|
end
|
58
106
|
end
|
59
107
|
|
108
|
+
if config_key
|
109
|
+
configs = configs.select do |db_config|
|
110
|
+
db_config.configuration_hash.key?(config_key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
60
114
|
if name
|
61
115
|
configs.find do |db_config|
|
62
|
-
db_config.name == name
|
116
|
+
db_config.name == name.to_s
|
63
117
|
end
|
64
118
|
else
|
65
119
|
configs
|
66
120
|
end
|
67
121
|
end
|
68
122
|
|
69
|
-
# Returns a single DatabaseConfig object based on the requested environment.
|
123
|
+
# Returns a single +DatabaseConfig+ object based on the requested environment.
|
70
124
|
#
|
71
125
|
# If the application has multiple databases +find_db_config+ will return
|
72
|
-
# the first DatabaseConfig for the environment.
|
126
|
+
# the first +DatabaseConfig+ for the environment.
|
73
127
|
def find_db_config(env)
|
74
|
-
|
75
|
-
|
76
|
-
.
|
77
|
-
|
78
|
-
|
79
|
-
|
128
|
+
env = env.to_s
|
129
|
+
configurations.find do |db_config|
|
130
|
+
db_config.for_current_env? && (db_config.env_name == env || db_config.name == env)
|
131
|
+
end || configurations.find do |db_config|
|
132
|
+
db_config.env_name == env
|
133
|
+
end
|
80
134
|
end
|
81
135
|
|
82
136
|
# A primary configuration is one that is named primary or if there is
|
@@ -93,8 +147,6 @@ module ActiveRecord
|
|
93
147
|
end
|
94
148
|
|
95
149
|
# Checks if the application's configurations are empty.
|
96
|
-
#
|
97
|
-
# Aliased to blank?
|
98
150
|
def empty?
|
99
151
|
configurations.empty?
|
100
152
|
end
|
@@ -219,15 +271,16 @@ module ActiveRecord
|
|
219
271
|
end
|
220
272
|
|
221
273
|
def build_db_config_from_hash(env_name, name, config)
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
config_without_url.delete :url
|
274
|
+
url = config[:url]
|
275
|
+
config_without_url = config.dup
|
276
|
+
config_without_url.delete :url
|
226
277
|
|
227
|
-
|
228
|
-
|
229
|
-
|
278
|
+
DatabaseConfigurations.db_config_handlers.reverse_each do |handler|
|
279
|
+
config = handler.call(env_name, name, url, config_without_url)
|
280
|
+
return config if config
|
230
281
|
end
|
282
|
+
|
283
|
+
nil
|
231
284
|
end
|
232
285
|
|
233
286
|
def merge_db_environment_variables(current_env, configs)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/core_ext/string/inquiry"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
|
-
#
|
6
|
+
# = Delegated types
|
7
7
|
#
|
8
8
|
# Class hierarchies can map to relational database tables in many ways. Active Record, for example, offers
|
9
9
|
# purely abstract classes, where the superclass doesn't persist any attributes, and single-table inheritance,
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
36
36
|
#
|
37
37
|
# Let's look at that entry/message/comment example using delegated types:
|
38
38
|
#
|
39
|
-
# # Schema: entries[ id, account_id, creator_id,
|
39
|
+
# # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
|
40
40
|
# class Entry < ApplicationRecord
|
41
41
|
# belongs_to :account
|
42
42
|
# belongs_to :creator
|
@@ -51,12 +51,12 @@ module ActiveRecord
|
|
51
51
|
# end
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
# # Schema: messages[ id, subject, body ]
|
54
|
+
# # Schema: messages[ id, subject, body, created_at, updated_at ]
|
55
55
|
# class Message < ApplicationRecord
|
56
56
|
# include Entryable
|
57
57
|
# end
|
58
58
|
#
|
59
|
-
# # Schema: comments[ id, content ]
|
59
|
+
# # Schema: comments[ id, content, created_at, updated_at ]
|
60
60
|
# class Comment < ApplicationRecord
|
61
61
|
# include Entryable
|
62
62
|
# end
|
@@ -102,17 +102,37 @@ module ActiveRecord
|
|
102
102
|
# You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
|
103
103
|
# like so:
|
104
104
|
#
|
105
|
-
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
|
105
|
+
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
|
106
106
|
#
|
107
107
|
# If you need more complicated composition, or you need to perform dependent validation, you should build a factory
|
108
108
|
# method or class to take care of the complicated needs. This could be as simple as:
|
109
109
|
#
|
110
110
|
# class Entry < ApplicationRecord
|
111
|
-
# def self.create_with_comment(content, creator: Current.user)
|
112
|
-
# create! entryable: Comment.new(content: content), creator: creator
|
111
|
+
# def self.create_with_comment(content, creator: Current.user, account: Current.account)
|
112
|
+
# create! entryable: Comment.new(content: content), creator: creator, account: account
|
113
113
|
# end
|
114
114
|
# end
|
115
115
|
#
|
116
|
+
# == Querying across records
|
117
|
+
#
|
118
|
+
# A consequence of delegated types is that querying attributes spread across multiple classes becomes slightly more
|
119
|
+
# tricky, but not impossible.
|
120
|
+
#
|
121
|
+
# The simplest method is to join the "superclass" to the "subclass" and apply the query parameters (i.e. <tt>#where</tt>)
|
122
|
+
# in appropriate places:
|
123
|
+
#
|
124
|
+
# Comment.joins(:entry).where(comments: { content: 'Hello!' }, entry: { creator: Current.user } )
|
125
|
+
#
|
126
|
+
# For convenience, add a scope on the concern. Now all classes that implement the concern will automatically include
|
127
|
+
# the method:
|
128
|
+
#
|
129
|
+
# # app/models/concerns/entryable.rb
|
130
|
+
# scope :with_entry, ->(attrs) { joins(:entry).where(entry: attrs) }
|
131
|
+
#
|
132
|
+
# Now the query can be shortened significantly:
|
133
|
+
#
|
134
|
+
# Comment.where(content: 'Hello!').with_entry(creator: Current.user)
|
135
|
+
#
|
116
136
|
# == Adding further delegation
|
117
137
|
#
|
118
138
|
# The delegated type shouldn't just answer the question of what the underlying class is called. In fact, that's
|
@@ -138,7 +158,7 @@ module ActiveRecord
|
|
138
158
|
#
|
139
159
|
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
140
160
|
#
|
141
|
-
# == Nested Attributes
|
161
|
+
# == Nested \Attributes
|
142
162
|
#
|
143
163
|
# Enabling nested attributes on a delegated_type association allows you to
|
144
164
|
# create the entry and message in one go:
|
@@ -192,6 +212,11 @@ module ActiveRecord
|
|
192
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
193
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
194
214
|
# the default <tt>:foreign_key</tt>.
|
215
|
+
# [:foreign_type]
|
216
|
+
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
217
|
+
# +role+ with a "_type" suffix. A class that defines a
|
218
|
+
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
219
|
+
# the default <tt>:foreign_type</tt>.
|
195
220
|
# [:primary_key]
|
196
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
197
222
|
# By default this is +id+.
|
@@ -211,11 +236,15 @@ module ActiveRecord
|
|
211
236
|
private
|
212
237
|
def define_delegated_type_methods(role, types:, options:)
|
213
238
|
primary_key = options[:primary_key] || "id"
|
214
|
-
role_type = "#{role}_type"
|
239
|
+
role_type = options[:foreign_type] || "#{role}_type"
|
215
240
|
role_id = options[:foreign_key] || "#{role}_id"
|
216
241
|
|
242
|
+
define_singleton_method "#{role}_types" do
|
243
|
+
types.map(&:to_s)
|
244
|
+
end
|
245
|
+
|
217
246
|
define_method "#{role}_class" do
|
218
|
-
public_send(
|
247
|
+
public_send(role_type).constantize
|
219
248
|
end
|
220
249
|
|
221
250
|
define_method "#{role}_name" do
|
@@ -4,6 +4,8 @@ module ActiveRecord
|
|
4
4
|
class DestroyAssociationAsyncError < StandardError
|
5
5
|
end
|
6
6
|
|
7
|
+
# = Active Record Destroy Association Async Job
|
8
|
+
#
|
7
9
|
# Job to destroy the records associated with a destroyed record in background.
|
8
10
|
class DestroyAssociationAsyncJob < ActiveJob::Base
|
9
11
|
queue_as { ActiveRecord.queues[:destroy] }
|
@@ -17,7 +19,7 @@ module ActiveRecord
|
|
17
19
|
)
|
18
20
|
association_model = association_class.constantize
|
19
21
|
owner_class = owner_model_name.constantize
|
20
|
-
owner = owner_class.find_by(owner_class.primary_key
|
22
|
+
owner = owner_class.find_by(owner_class.primary_key => [owner_id])
|
21
23
|
|
22
24
|
if !owner_destroyed?(owner, ensuring_owner_was_method)
|
23
25
|
raise DestroyAssociationAsyncError, "owner record not destroyed"
|
@@ -12,12 +12,12 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def method_missing(name,
|
15
|
+
def method_missing(name, ...)
|
16
16
|
match = Method.match(self, name)
|
17
17
|
|
18
18
|
if match && match.valid?
|
19
19
|
match.define
|
20
|
-
send(name,
|
20
|
+
send(name, ...)
|
21
21
|
else
|
22
22
|
super
|
23
23
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Encryption
|
5
|
+
class AutoFilteredParameters
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
@attributes_by_class = Concurrent::Map.new
|
9
|
+
@collecting = true
|
10
|
+
|
11
|
+
install_collecting_hook
|
12
|
+
end
|
13
|
+
|
14
|
+
def enable
|
15
|
+
apply_collected_attributes
|
16
|
+
@collecting = false
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
attr_reader :app
|
21
|
+
|
22
|
+
def install_collecting_hook
|
23
|
+
ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute|
|
24
|
+
attribute_was_declared(klass, attribute)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def attribute_was_declared(klass, attribute)
|
29
|
+
if collecting?
|
30
|
+
collect_for_later(klass, attribute)
|
31
|
+
else
|
32
|
+
apply_filter(klass, attribute)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply_collected_attributes
|
37
|
+
@attributes_by_class.each do |klass, attributes|
|
38
|
+
attributes.each do |attribute|
|
39
|
+
apply_filter(klass, attribute)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def collecting?
|
45
|
+
@collecting
|
46
|
+
end
|
47
|
+
|
48
|
+
def collect_for_later(klass, attribute)
|
49
|
+
@attributes_by_class[klass] ||= Concurrent::Array.new
|
50
|
+
@attributes_by_class[klass] << attribute
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply_filter(klass, attribute)
|
54
|
+
filter = [("#{klass.model_name.element}" if klass.name), attribute.to_s].compact.join(".")
|
55
|
+
unless excluded_from_filter_parameters?(filter)
|
56
|
+
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
|
57
|
+
klass.filter_attributes += [ attribute ]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def excluded_from_filter_parameters?(filter_parameter)
|
62
|
+
ActiveRecord::Encryption.config.excluded_from_filter_parameters.find { |excluded_filter| excluded_filter.to_s == filter_parameter }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "openssl"
|
4
|
-
require "base64"
|
5
4
|
|
6
5
|
module ActiveRecord
|
7
6
|
module Encryption
|
@@ -80,6 +79,10 @@ module ActiveRecord
|
|
80
79
|
raise ActiveRecord::Encryption::Errors::Decryption
|
81
80
|
end
|
82
81
|
|
82
|
+
def inspect # :nodoc:
|
83
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
84
|
+
end
|
85
|
+
|
83
86
|
private
|
84
87
|
def generate_iv(cipher, clear_text)
|
85
88
|
if @deterministic
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "openssl"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Encryption
|
5
7
|
# Container of configuration options
|
6
8
|
class Config
|
7
|
-
attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt,
|
9
|
+
attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
|
8
10
|
:support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
|
9
11
|
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption
|
10
12
|
|
@@ -21,6 +23,27 @@ module ActiveRecord
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
26
|
+
def support_sha1_for_non_deterministic_encryption=(value)
|
27
|
+
if value && has_primary_key?
|
28
|
+
sha1_key_generator = ActiveRecord::Encryption::KeyGenerator.new(hash_digest_class: OpenSSL::Digest::SHA1)
|
29
|
+
sha1_key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key, key_generator: sha1_key_generator)
|
30
|
+
add_previous_scheme key_provider: sha1_key_provider
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
%w(key_derivation_salt primary_key deterministic_key).each do |key|
|
35
|
+
silence_redefinition_of_method "has_#{key}?"
|
36
|
+
define_method("has_#{key}?") do
|
37
|
+
instance_variable_get(:"@#{key}").presence
|
38
|
+
end
|
39
|
+
|
40
|
+
silence_redefinition_of_method key
|
41
|
+
define_method(key) do
|
42
|
+
public_send("has_#{key}?") or
|
43
|
+
raise Errors::Configuration, "Missing Active Record encryption credential: active_record_encryption.#{key}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
24
47
|
private
|
25
48
|
def set_defaults
|
26
49
|
self.store_key_references = false
|
@@ -31,6 +54,7 @@ module ActiveRecord
|
|
31
54
|
self.excluded_from_filter_parameters = []
|
32
55
|
self.previous_schemes = []
|
33
56
|
self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
|
57
|
+
self.hash_digest_class = OpenSSL::Digest::SHA1
|
34
58
|
|
35
59
|
# TODO: Setting to false for now as the implementation is a bit experimental
|
36
60
|
self.extend_queries = false
|
@@ -17,24 +17,29 @@ module ActiveRecord
|
|
17
17
|
delegate name, to: :context
|
18
18
|
end
|
19
19
|
|
20
|
-
def configure(primary_key
|
20
|
+
def configure(primary_key: nil, deterministic_key: nil, key_derivation_salt: nil, **properties) # :nodoc:
|
21
21
|
config.primary_key = primary_key
|
22
22
|
config.deterministic_key = deterministic_key
|
23
23
|
config.key_derivation_salt = key_derivation_salt
|
24
24
|
|
25
|
-
|
25
|
+
# Set the default for this property here instead of in +Config#set_defaults+ as this needs
|
26
|
+
# to happen *after* the keys have been set.
|
27
|
+
properties[:support_sha1_for_non_deterministic_encryption] = true if properties[:support_sha1_for_non_deterministic_encryption].nil?
|
26
28
|
|
27
29
|
properties.each do |name, value|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
ActiveRecord::Encryption.config.send "#{name}=", value if ActiveRecord::Encryption.config.respond_to?("#{name}=")
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveRecord::Encryption.reset_default_context
|
34
|
+
|
35
|
+
properties.each do |name, value|
|
36
|
+
ActiveRecord::Encryption.context.send "#{name}=", value if ActiveRecord::Encryption.context.respond_to?("#{name}=")
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
35
40
|
# Register callback to be invoked when an encrypted attribute is declared.
|
36
41
|
#
|
37
|
-
# === Example
|
42
|
+
# === Example
|
38
43
|
#
|
39
44
|
# ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute_name|
|
40
45
|
# ...
|
@@ -49,18 +54,6 @@ module ActiveRecord
|
|
49
54
|
block.call(klass, name)
|
50
55
|
end
|
51
56
|
end
|
52
|
-
|
53
|
-
def install_auto_filtered_parameters_hook(application) # :nodoc:
|
54
|
-
ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, encrypted_attribute_name|
|
55
|
-
filter_parameter = [("#{klass.model_name.element}" if klass.name), encrypted_attribute_name.to_s].compact.join(".")
|
56
|
-
application.config.filter_parameters << filter_parameter unless excluded_from_filter_parameters?(filter_parameter)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
def excluded_from_filter_parameters?(filter_parameter)
|
62
|
-
ActiveRecord::Encryption.config.excluded_from_filter_parameters.find { |excluded_filter| excluded_filter.to_s == filter_parameter }
|
63
|
-
end
|
64
57
|
end
|
65
58
|
end
|
66
59
|
end
|
@@ -12,9 +12,7 @@ module ActiveRecord
|
|
12
12
|
class Context
|
13
13
|
PROPERTIES = %i[ key_provider key_generator cipher message_serializer encryptor frozen_encryption ]
|
14
14
|
|
15
|
-
PROPERTIES
|
16
|
-
attr_accessor name
|
17
|
-
end
|
15
|
+
attr_accessor(*PROPERTIES)
|
18
16
|
|
19
17
|
def initialize
|
20
18
|
set_defaults
|
@@ -22,6 +20,11 @@ module ActiveRecord
|
|
22
20
|
|
23
21
|
alias frozen_encryption? frozen_encryption
|
24
22
|
|
23
|
+
silence_redefinition_of_method :key_provider
|
24
|
+
def key_provider
|
25
|
+
@key_provider ||= build_default_key_provider
|
26
|
+
end
|
27
|
+
|
25
28
|
private
|
26
29
|
def set_defaults
|
27
30
|
self.frozen_encryption = false
|
@@ -30,6 +33,10 @@ module ActiveRecord
|
|
30
33
|
self.encryptor = ActiveRecord::Encryption::Encryptor.new
|
31
34
|
self.message_serializer = ActiveRecord::Encryption::MessageSerializer.new
|
32
35
|
end
|
36
|
+
|
37
|
+
def build_default_key_provider
|
38
|
+
ActiveRecord::Encryption::DerivedSecretKeyProvider.new(ActiveRecord::Encryption.config.primary_key)
|
39
|
+
end
|
33
40
|
end
|
34
41
|
end
|
35
42
|
end
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
14
14
|
extend ActiveSupport::Concern
|
15
15
|
|
16
16
|
included do
|
17
|
-
|
17
|
+
mattr_accessor :default_context, default: Context.new
|
18
18
|
thread_mattr_accessor :custom_contexts
|
19
19
|
end
|
20
20
|
|
@@ -66,6 +66,10 @@ module ActiveRecord
|
|
66
66
|
def current_custom_context
|
67
67
|
self.custom_contexts&.last
|
68
68
|
end
|
69
|
+
|
70
|
+
def reset_default_context
|
71
|
+
self.default_context = Context.new
|
72
|
+
end
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
@@ -4,9 +4,15 @@ module ActiveRecord
|
|
4
4
|
module Encryption
|
5
5
|
# A KeyProvider that derives keys from passwords.
|
6
6
|
class DerivedSecretKeyProvider < KeyProvider
|
7
|
-
def initialize(passwords)
|
8
|
-
super(Array(passwords).collect { |password|
|
7
|
+
def initialize(passwords, key_generator: ActiveRecord::Encryption.key_generator)
|
8
|
+
super(Array(passwords).collect { |password| derive_key_from(password, using: key_generator) })
|
9
9
|
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def derive_key_from(password, using: key_generator)
|
13
|
+
secret = using.derive_key_from(password)
|
14
|
+
ActiveRecord::Encryption::Key.new(secret)
|
15
|
+
end
|
10
16
|
end
|
11
17
|
end
|
12
18
|
end
|