activerecord 7.0.2.4 → 7.0.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +172 -0
- data/lib/active_record/associations/collection_association.rb +1 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +7 -4
- data/lib/active_record/associations/join_dependency.rb +17 -13
- data/lib/active_record/associations.rb +15 -15
- data/lib/active_record/attribute_methods/serialization.rb +34 -50
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/base.rb +3 -3
- data/lib/active_record/coders/yaml_column.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -5
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +3 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -2
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +3 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/encryption/configurable.rb +8 -2
- data/lib/active_record/encryption/contexts.rb +3 -3
- data/lib/active_record/encryption/derived_secret_key_provider.rb +1 -1
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +2 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -4
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/properties.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/fixtures.rb +4 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +10 -5
- data/lib/active_record/middleware/database_selector.rb +13 -6
- data/lib/active_record/middleware/shard_selector.rb +4 -4
- data/lib/active_record/migration/command_recorder.rb +3 -3
- data/lib/active_record/migration/compatibility.rb +7 -26
- data/lib/active_record/migration.rb +5 -4
- data/lib/active_record/model_schema.rb +22 -10
- data/lib/active_record/persistence.rb +9 -8
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +20 -16
- data/lib/active_record/railties/databases.rake +16 -11
- data/lib/active_record/reflection.rb +6 -0
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +27 -6
- data/lib/active_record/relation.rb +7 -6
- data/lib/active_record/sanitization.rb +6 -5
- data/lib/active_record/scoping/default.rb +5 -7
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +2 -2
- data/lib/active_record/store.rb +7 -2
- data/lib/active_record/tasks/database_tasks.rb +26 -21
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -2
- data/lib/active_record/test_fixtures.rb +12 -5
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +3 -3
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +14 -0
- metadata +10 -10
@@ -139,7 +139,7 @@ module ActiveRecord
|
|
139
139
|
end
|
140
140
|
|
141
141
|
def field_ordered_value(column, values) # :nodoc:
|
142
|
-
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse])
|
142
|
+
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse.map { |value| Arel::Nodes.build_quoted(value) }])
|
143
143
|
Arel::Nodes::Descending.new(field)
|
144
144
|
end
|
145
145
|
|
@@ -711,6 +711,14 @@ module ActiveRecord
|
|
711
711
|
options[:comment] = column.comment
|
712
712
|
end
|
713
713
|
|
714
|
+
unless options.key?(:collation)
|
715
|
+
options[:collation] = column.collation
|
716
|
+
end
|
717
|
+
|
718
|
+
unless options.key?(:auto_increment)
|
719
|
+
options[:auto_increment] = column.auto_increment?
|
720
|
+
end
|
721
|
+
|
714
722
|
td = create_table_definition(table_name)
|
715
723
|
cd = td.new_column_definition(column.name, type, **options)
|
716
724
|
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
|
@@ -158,7 +158,21 @@ module ActiveRecord
|
|
158
158
|
MySQL::TableDefinition.new(self, name, **options)
|
159
159
|
end
|
160
160
|
|
161
|
+
def default_type(table_name, field_name)
|
162
|
+
match = create_table_info(table_name)&.match(/`#{field_name}` (.+) DEFAULT ('|\d+|[A-z]+)/)
|
163
|
+
default_pre = match[2] if match
|
164
|
+
|
165
|
+
if default_pre == "'"
|
166
|
+
:string
|
167
|
+
elsif default_pre&.match?(/^\d+$/)
|
168
|
+
:integer
|
169
|
+
elsif default_pre&.match?(/^[A-z]+$/)
|
170
|
+
:function
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
161
174
|
def new_column_from_field(table_name, field)
|
175
|
+
field_name = field.fetch(:Field)
|
162
176
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
163
177
|
default, default_function = field[:Default], nil
|
164
178
|
|
@@ -168,9 +182,13 @@ module ActiveRecord
|
|
168
182
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
169
183
|
default = +"(#{default})" unless default.start_with?("(")
|
170
184
|
default, default_function = nil, default
|
171
|
-
elsif type_metadata.type == :text && default
|
185
|
+
elsif type_metadata.type == :text && default&.start_with?("'")
|
172
186
|
# strip and unescape quotes
|
173
187
|
default = default[1...-1].gsub("\\'", "'")
|
188
|
+
elsif default&.match?(/\A\d/)
|
189
|
+
# Its a number so we can skip the query to check if it is a function
|
190
|
+
elsif default && default_type(table_name, field_name) == :function
|
191
|
+
default, default_function = nil, default
|
174
192
|
end
|
175
193
|
|
176
194
|
MySQL::Column.new(
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
27
27
|
end
|
28
28
|
|
29
|
-
unless key = scanner.
|
29
|
+
unless key = scanner.scan(/^(\\[\\"]|[^\\"])*?(?=")/)
|
30
30
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
31
31
|
end
|
32
32
|
|
@@ -41,7 +41,7 @@ module ActiveRecord
|
|
41
41
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
42
42
|
end
|
43
43
|
|
44
|
-
unless value = scanner.
|
44
|
+
unless value = scanner.scan(/^(\\[\\"]|[^\\"])*?(?=")/)
|
45
45
|
raise(ArgumentError, ERROR % scanner.string.inspect)
|
46
46
|
end
|
47
47
|
|
@@ -45,8 +45,10 @@ Rails needs superuser privileges to disable referential integrity.
|
|
45
45
|
BEGIN
|
46
46
|
FOR r IN (
|
47
47
|
SELECT FORMAT(
|
48
|
-
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I''; ALTER TABLE %I VALIDATE CONSTRAINT %I;',
|
48
|
+
'UPDATE pg_constraint SET convalidated=false WHERE conname = ''%I'' AND connamespace::regnamespace = ''%I''::regnamespace; ALTER TABLE %I.%I VALIDATE CONSTRAINT %I;',
|
49
49
|
constraint_name,
|
50
|
+
table_schema,
|
51
|
+
table_schema,
|
50
52
|
table_name,
|
51
53
|
constraint_name
|
52
54
|
) AS constraint_check
|
@@ -528,8 +528,10 @@ module ActiveRecord
|
|
528
528
|
SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
|
529
529
|
FROM pg_constraint c
|
530
530
|
JOIN pg_class t ON c.conrelid = t.oid
|
531
|
+
JOIN pg_namespace n ON n.oid = c.connamespace
|
531
532
|
WHERE c.contype = 'c'
|
532
533
|
AND t.relname = #{scope[:name]}
|
534
|
+
AND n.nspname = #{scope[:schema]}
|
533
535
|
SQL
|
534
536
|
|
535
537
|
check_info.map do |row|
|
@@ -104,7 +104,7 @@ module ActiveRecord
|
|
104
104
|
|
105
105
|
##
|
106
106
|
# :singleton-method:
|
107
|
-
# PostgreSQL supports multiple types for DateTimes. By default if you use
|
107
|
+
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
108
108
|
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
109
|
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
110
|
# store DateTimes as "timestamp with time zone":
|
@@ -116,8 +116,8 @@ module ActiveRecord
|
|
116
116
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
117
117
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
118
118
|
#
|
119
|
-
# If you're using
|
120
|
-
# setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
|
119
|
+
# If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
|
120
|
+
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
121
121
|
class_attribute :datetime_type, default: :timestamp
|
122
122
|
|
123
123
|
NATIVE_DATABASE_TYPES = {
|
@@ -1063,5 +1063,6 @@ module ActiveRecord
|
|
1063
1063
|
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
1064
1064
|
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
1065
1065
|
end
|
1066
|
+
ActiveSupport.run_load_hooks(:active_record_postgresqladapter, PostgreSQLAdapter)
|
1066
1067
|
end
|
1067
1068
|
end
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
WHERE name = #{quote(row['name'])} AND type = 'index'
|
22
22
|
SQL
|
23
23
|
|
24
|
-
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?\z/i =~ index_sql
|
24
|
+
/\bON\b\s*"?(\w+?)"?\s*\((?<expressions>.+?)\)(?:\s*WHERE\b\s*(?<where>.+))?(?:\s*\/\*.*\*\/)?\z/i =~ index_sql
|
25
25
|
|
26
26
|
columns = exec_query("PRAGMA index_info(#{quote(row['name'])})", "SCHEMA").map do |col|
|
27
27
|
col["name"]
|
@@ -60,7 +60,7 @@ module ActiveRecord
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
63
|
-
return if options
|
63
|
+
return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
|
64
64
|
|
65
65
|
to_table ||= options[:to_table]
|
66
66
|
options = options.except(:name, :to_table, :validate)
|
@@ -341,7 +341,7 @@ module ActiveRecord
|
|
341
341
|
end
|
342
342
|
|
343
343
|
def get_database_version # :nodoc:
|
344
|
-
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
344
|
+
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
|
345
345
|
end
|
346
346
|
|
347
347
|
def check_version # :nodoc:
|
@@ -477,8 +477,13 @@ module ActiveRecord
|
|
477
477
|
options[:rename][column.name.to_sym] ||
|
478
478
|
column.name) : column.name
|
479
479
|
|
480
|
+
if column.has_default?
|
481
|
+
type = lookup_cast_type_from_column(column)
|
482
|
+
default = type.deserialize(column.default)
|
483
|
+
end
|
484
|
+
|
480
485
|
@definition.column(column_name, column.type,
|
481
|
-
limit: column.limit, default:
|
486
|
+
limit: column.limit, default: default,
|
482
487
|
precision: column.precision, scale: column.scale,
|
483
488
|
null: column.null, collation: column.collation,
|
484
489
|
primary_key: column_name == from_primary_key
|
@@ -44,7 +44,7 @@ module ActiveRecord
|
|
44
44
|
#
|
45
45
|
# ActiveRecord::Base.establish_connection(:production)
|
46
46
|
#
|
47
|
-
# The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
|
47
|
+
# The exceptions AdapterNotSpecified, AdapterNotFound, and +ArgumentError+
|
48
48
|
# may be returned on an error.
|
49
49
|
def establish_connection(config_or_env = nil)
|
50
50
|
config_or_env ||= DEFAULT_ENV.call.to_sym
|
@@ -108,7 +108,7 @@ module ActiveRecord
|
|
108
108
|
connections
|
109
109
|
end
|
110
110
|
|
111
|
-
# Connects to a role (
|
111
|
+
# Connects to a role (e.g. writing, reading, or a custom role) and/or
|
112
112
|
# shard for the duration of the block. At the end of the block the
|
113
113
|
# connection will be returned to the original role / shard.
|
114
114
|
#
|
data/lib/active_record/core.rb
CHANGED
@@ -60,7 +60,7 @@ module ActiveRecord
|
|
60
60
|
##
|
61
61
|
# :singleton-method:
|
62
62
|
# Force enumeration of all columns in SELECT statements.
|
63
|
-
# e.g.
|
63
|
+
# e.g. <tt>SELECT first_name, last_name FROM ...</tt> instead of <tt>SELECT * FROM ...</tt>
|
64
64
|
# This avoids +PreparedStatementCacheExpired+ errors when a column is added
|
65
65
|
# to the database while the app is running.
|
66
66
|
class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
|
@@ -238,7 +238,7 @@ module ActiveRecord
|
|
238
238
|
def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
|
239
239
|
case ActiveRecord.action_on_strict_loading_violation
|
240
240
|
when :raise
|
241
|
-
message =
|
241
|
+
message = reflection.strict_loading_violation_message(owner)
|
242
242
|
raise ActiveRecord::StrictLoadingViolationError.new(message)
|
243
243
|
when :log
|
244
244
|
name = "strict_loading_violation.active_record"
|
@@ -411,7 +411,7 @@ module ActiveRecord
|
|
411
411
|
end
|
412
412
|
end
|
413
413
|
|
414
|
-
#
|
414
|
+
# Override the default class equality method to provide support for decorated models.
|
415
415
|
def ===(object) # :nodoc:
|
416
416
|
object.is_a?(self)
|
417
417
|
end
|
@@ -136,7 +136,7 @@ module ActiveRecord
|
|
136
136
|
# end
|
137
137
|
# end
|
138
138
|
#
|
139
|
-
# Now you can list a bunch of entries, call
|
139
|
+
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
140
140
|
#
|
141
141
|
# == Nested Attributes
|
142
142
|
#
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Encryption
|
5
|
-
# Configuration API for
|
5
|
+
# Configuration API for ActiveRecord::Encryption
|
6
6
|
module Configurable
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
@@ -52,9 +52,15 @@ module ActiveRecord
|
|
52
52
|
|
53
53
|
def install_auto_filtered_parameters_hook(application) # :nodoc:
|
54
54
|
ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, encrypted_attribute_name|
|
55
|
-
|
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)
|
56
57
|
end
|
57
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
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Encryption
|
5
|
-
#
|
5
|
+
# ActiveRecord::Encryption uses encryption contexts to configure the different entities used to
|
6
6
|
# encrypt/decrypt at a given moment in time.
|
7
7
|
#
|
8
|
-
# By default, the library uses a default encryption context. This is the
|
8
|
+
# By default, the library uses a default encryption context. This is the Context that gets configured
|
9
9
|
# initially via +config.active_record.encryption+ options. Library users can define nested encryption contexts
|
10
10
|
# when running blocks of code.
|
11
11
|
#
|
12
|
-
# See
|
12
|
+
# See Context.
|
13
13
|
module Contexts
|
14
14
|
extend ActiveSupport::Concern
|
15
15
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Encryption
|
5
|
-
# A
|
5
|
+
# A KeyProvider that derives keys from passwords.
|
6
6
|
class DerivedSecretKeyProvider < KeyProvider
|
7
7
|
def initialize(passwords)
|
8
8
|
super(Array(passwords).collect { |password| Key.derive_from(password) })
|
@@ -18,12 +18,10 @@ module ActiveRecord
|
|
18
18
|
#
|
19
19
|
# === Options
|
20
20
|
#
|
21
|
-
# * <tt>:key_provider</tt> -
|
22
|
-
#
|
21
|
+
# * <tt>:key_provider</tt> - A key provider to provide encryption and decryption keys. Defaults to
|
22
|
+
# +ActiveRecord::Encryption.key_provider+.
|
23
23
|
# * <tt>:key</tt> - A password to derive the key from. It's a shorthand for a +:key_provider+ that
|
24
24
|
# serves derivated keys. Both options can't be used at the same time.
|
25
|
-
# * <tt>:key_provider</tt> - Set a +:key_provider+ to provide encryption and decryption keys. If not
|
26
|
-
# provided, it will default to the key provider set with `config.key_provider`.
|
27
25
|
# * <tt>:deterministic</tt> - By default, encryption is not deterministic. It will use a random
|
28
26
|
# initialization vector for each encryption operation. This means that encrypting the same content
|
29
27
|
# with the same key twice will generate different ciphertexts. When set to +true+, it will generate the
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Encryption
|
5
|
-
# An
|
5
|
+
# An ActiveModel::Type::Value that encrypts/decrypts strings of text.
|
6
6
|
#
|
7
7
|
# This is the central piece that connects the encryption system with +encrypts+ declarations in the
|
8
8
|
# model classes. Whenever you declare an attribute as encrypted, it configures an +EncryptedAttributeType+
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
#
|
20
20
|
# * <tt>:scheme</tt> - A +Scheme+ with the encryption properties for this attribute.
|
21
21
|
# * <tt>:cast_type</tt> - A type that will be used to serialize (before encrypting) and deserialize
|
22
|
-
# (after decrypting).
|
22
|
+
# (after decrypting). ActiveModel::Type::String by default.
|
23
23
|
def initialize(scheme:, cast_type: ActiveModel::Type::String.new, previous_type: false)
|
24
24
|
super()
|
25
25
|
@scheme = scheme
|
@@ -6,17 +6,17 @@ require "active_support/core_ext/numeric"
|
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
8
|
module Encryption
|
9
|
-
# An encryptor exposes the encryption API that
|
9
|
+
# An encryptor exposes the encryption API that ActiveRecord::Encryption::EncryptedAttributeType
|
10
10
|
# uses for encrypting and decrypting attribute values.
|
11
11
|
#
|
12
|
-
# It interacts with a
|
13
|
-
#
|
12
|
+
# It interacts with a KeyProvider for getting the keys, and delegate to
|
13
|
+
# ActiveRecord::Encryption::Cipher the actual encryption algorithm.
|
14
14
|
class Encryptor
|
15
15
|
# Encrypts +clean_text+ and returns the encrypted result
|
16
16
|
#
|
17
17
|
# Internally, it will:
|
18
18
|
#
|
19
|
-
# 1. Create a new
|
19
|
+
# 1. Create a new ActiveRecord::Encryption::Message
|
20
20
|
# 2. Compress and encrypt +clean_text+ as the message payload
|
21
21
|
# 3. Serialize it with +ActiveRecord::Encryption.message_serializer+ (+ActiveRecord::Encryption::SafeMarshal+
|
22
22
|
# by default)
|
@@ -26,10 +26,10 @@ module ActiveRecord
|
|
26
26
|
#
|
27
27
|
# [:key_provider]
|
28
28
|
# Key provider to use for the encryption operation. It will default to
|
29
|
-
# +ActiveRecord::Encryption.key_provider+ when not provided
|
29
|
+
# +ActiveRecord::Encryption.key_provider+ when not provided.
|
30
30
|
#
|
31
31
|
# [:cipher_options]
|
32
|
-
#
|
32
|
+
# Cipher-specific options that will be passed to the Cipher configured in
|
33
33
|
# +ActiveRecord::Encryption.cipher+
|
34
34
|
def encrypt(clear_text, key_provider: default_key_provider, cipher_options: {})
|
35
35
|
clear_text = force_encoding_if_needed(clear_text) if cipher_options[:deterministic]
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
# +ActiveRecord::Encryption.key_provider+ when not provided
|
48
48
|
#
|
49
49
|
# [:cipher_options]
|
50
|
-
#
|
50
|
+
# Cipher-specific options that will be passed to the Cipher configured in
|
51
51
|
# +ActiveRecord::Encryption.cipher+
|
52
52
|
def decrypt(encrypted_text, key_provider: default_key_provider, cipher_options: {})
|
53
53
|
message = deserialize_message(encrypted_text)
|
@@ -4,13 +4,13 @@ module ActiveRecord
|
|
4
4
|
module Encryption
|
5
5
|
# Implements a simple envelope encryption approach where:
|
6
6
|
#
|
7
|
-
# * It generates a random data-encryption key for each encryption operation
|
7
|
+
# * It generates a random data-encryption key for each encryption operation.
|
8
8
|
# * It stores the generated key along with the encrypted payload. It encrypts this key
|
9
|
-
# with the master key provided in the
|
9
|
+
# with the master key provided in the +active_record_encryption.primary_key+ credential.
|
10
10
|
#
|
11
11
|
# This provider can work with multiple master keys. It will use the last one for encrypting.
|
12
12
|
#
|
13
|
-
# When
|
13
|
+
# When +config.active_record.encryption.store_key_references+ is true, it will also store a reference to
|
14
14
|
# the specific master key that was used to encrypt the data-encryption key. When not set,
|
15
15
|
# it will try all the configured master keys looking for the right one, in order to
|
16
16
|
# return the right decryption key.
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module Encryption
|
5
5
|
# Automatically expand encrypted arguments to support querying both encrypted and unencrypted data
|
6
6
|
#
|
7
|
-
# Active Record Encryption supports querying the db using deterministic attributes. For example:
|
7
|
+
# Active Record \Encryption supports querying the db using deterministic attributes. For example:
|
8
8
|
#
|
9
9
|
# Contact.find_by(email_address: "jorge@hey.com")
|
10
10
|
#
|
@@ -16,10 +16,10 @@ module ActiveRecord
|
|
16
16
|
#
|
17
17
|
# This patches ActiveRecord to support this automatically. It addresses both:
|
18
18
|
#
|
19
|
-
# * ActiveRecord::Base
|
20
|
-
# * ActiveRecord::Relation
|
19
|
+
# * ActiveRecord::Base - Used in <tt>Contact.find_by_email_address(...)</tt>
|
20
|
+
# * ActiveRecord::Relation - Used in <tt>Contact.internal.find_by_email_address(...)</tt>
|
21
21
|
#
|
22
|
-
#
|
22
|
+
# ActiveRecord::Base relies on ActiveRecord::Relation (ActiveRecord::QueryMethods) but it does
|
23
23
|
# some prepared statements caching. That's why we need to intercept +ActiveRecord::Base+ as soon
|
24
24
|
# as it's invoked (so that the proper prepared statement is cached).
|
25
25
|
#
|
@@ -12,7 +12,7 @@ module ActiveRecord
|
|
12
12
|
#
|
13
13
|
# message.headers.encrypted_data_key # instead of message.headers[:k]
|
14
14
|
#
|
15
|
-
# See +Properties
|
15
|
+
# See +Properties::DEFAULT_PROPERTIES+, Key, Message
|
16
16
|
class Properties
|
17
17
|
ALLOWED_VALUE_CLASSES = [String, ActiveRecord::Encryption::Message, Numeric, TrueClass, FalseClass, Symbol, NilClass]
|
18
18
|
|
data/lib/active_record/enum.rb
CHANGED
@@ -83,7 +83,7 @@ module ActiveRecord
|
|
83
83
|
#
|
84
84
|
# In rare circumstances you might need to access the mapping directly.
|
85
85
|
# The mappings are exposed through a class method with the pluralized attribute
|
86
|
-
# name, which return the mapping in a
|
86
|
+
# name, which return the mapping in a ActiveSupport::HashWithIndifferentAccess :
|
87
87
|
#
|
88
88
|
# Conversation.statuses[:active] # => 0
|
89
89
|
# Conversation.statuses["archived"] # => 1
|
@@ -241,13 +241,13 @@ module ActiveRecord
|
|
241
241
|
# The generated ID for a given label is constant, so we can discover
|
242
242
|
# any fixture's ID without loading anything, as long as we know the label.
|
243
243
|
#
|
244
|
-
# == Label references for associations (belongs_to
|
244
|
+
# == Label references for associations (+belongs_to+, +has_one+, +has_many+)
|
245
245
|
#
|
246
246
|
# Specifying foreign keys in fixtures can be very fragile, not to
|
247
247
|
# mention difficult to read. Since Active Record can figure out the ID of
|
248
248
|
# any fixture from its label, you can specify FK's by label instead of ID.
|
249
249
|
#
|
250
|
-
# === belongs_to
|
250
|
+
# === +belongs_to+
|
251
251
|
#
|
252
252
|
# Let's break out some more monkeys and pirates.
|
253
253
|
#
|
@@ -286,7 +286,7 @@ module ActiveRecord
|
|
286
286
|
# a target *label* for the *association* (monkey: george) rather than
|
287
287
|
# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
|
288
288
|
#
|
289
|
-
# ==== Polymorphic belongs_to
|
289
|
+
# ==== Polymorphic +belongs_to+
|
290
290
|
#
|
291
291
|
# Supporting polymorphic relationships is a little bit more complicated, since
|
292
292
|
# Active Record needs to know what type your association is pointing at. Something
|
@@ -311,7 +311,7 @@ module ActiveRecord
|
|
311
311
|
#
|
312
312
|
# Just provide the polymorphic target type and Active Record will take care of the rest.
|
313
313
|
#
|
314
|
-
# === has_and_belongs_to_many or has_many :through
|
314
|
+
# === +has_and_belongs_to_many+ or <tt>has_many :through</tt>
|
315
315
|
#
|
316
316
|
# Time to give our monkey some fruit.
|
317
317
|
#
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
# Returns the version of
|
4
|
+
# Returns the currently loaded version of Active Record as a <tt>Gem::Version</tt>.
|
5
5
|
def self.gem_version
|
6
6
|
Gem::Version.new VERSION::STRING
|
7
7
|
end
|
@@ -9,8 +9,8 @@ module ActiveRecord
|
|
9
9
|
module VERSION
|
10
10
|
MAJOR = 7
|
11
11
|
MINOR = 0
|
12
|
-
TINY =
|
13
|
-
PRE =
|
12
|
+
TINY = 4
|
13
|
+
PRE = nil
|
14
14
|
|
15
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
16
16
|
end
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
# Locking::Pessimistic provides support for row-level locking using
|
6
6
|
# SELECT ... FOR UPDATE and other lock types.
|
7
7
|
#
|
8
|
-
# Chain <tt>ActiveRecord::Base#find</tt> to
|
8
|
+
# Chain <tt>ActiveRecord::Base#find</tt> to ActiveRecord::QueryMethods#lock to obtain an exclusive
|
9
9
|
# lock on the selected rows:
|
10
10
|
# # select * from accounts where id=1 for update
|
11
11
|
# Account.lock.find(1)
|
@@ -81,11 +81,11 @@ module ActiveRecord
|
|
81
81
|
|
82
82
|
# Wraps the passed block in a transaction, locking the object
|
83
83
|
# before yielding. You can pass the SQL locking clause
|
84
|
-
# as an optional argument (see
|
84
|
+
# as an optional argument (see #lock!).
|
85
85
|
#
|
86
86
|
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
|
87
87
|
# and <tt>joinable:</tt> to the wrapping transaction (see
|
88
|
-
#
|
88
|
+
# ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction).
|
89
89
|
def with_lock(*args)
|
90
90
|
transaction_opts = args.extract_options!
|
91
91
|
lock = args.present? ? args.first : true
|
@@ -22,10 +22,8 @@ module ActiveRecord
|
|
22
22
|
def strict_loading_violation(event)
|
23
23
|
debug do
|
24
24
|
owner = event.payload[:owner]
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
|
25
|
+
reflection = event.payload[:reflection]
|
26
|
+
color(reflection.strict_loading_violation_message(owner), RED)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
@@ -51,7 +49,14 @@ module ActiveRecord
|
|
51
49
|
|
52
50
|
binds = []
|
53
51
|
payload[:binds].each_with_index do |attr, i|
|
54
|
-
attribute_name = attr.respond_to?(:name)
|
52
|
+
attribute_name = if attr.respond_to?(:name)
|
53
|
+
attr.name
|
54
|
+
elsif attr.respond_to?(:[]) && attr[i].respond_to?(:name)
|
55
|
+
attr[i].name
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
55
60
|
filtered_params = filter(attribute_name, casted_params[i])
|
56
61
|
|
57
62
|
binds << render_bind(attr, filtered_params)
|
@@ -19,19 +19,22 @@ module ActiveRecord
|
|
19
19
|
# that informs the application when to read from a primary or read from a
|
20
20
|
# replica.
|
21
21
|
#
|
22
|
-
# To use the DatabaseSelector in your application with default settings
|
23
|
-
# the
|
22
|
+
# To use the DatabaseSelector in your application with default settings,
|
23
|
+
# run the provided generator.
|
24
24
|
#
|
25
|
-
#
|
26
|
-
# require "active_support/core_ext/integer/time"
|
25
|
+
# bin/rails g active_record:multi_db
|
27
26
|
#
|
28
|
-
#
|
27
|
+
# This will create a file named +config/initializers/multi_db.rb+ with the
|
28
|
+
# following contents:
|
29
|
+
#
|
30
|
+
# Rails.application.configure do
|
29
31
|
# config.active_record.database_selector = { delay: 2.seconds }
|
30
32
|
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
31
33
|
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
32
34
|
# end
|
33
35
|
#
|
34
|
-
#
|
36
|
+
# Alternatively you can set the options in your environment config or
|
37
|
+
# any other config file loaded on boot.
|
35
38
|
#
|
36
39
|
# The default behavior can be changed by setting the config options to a
|
37
40
|
# custom class:
|
@@ -39,6 +42,10 @@ module ActiveRecord
|
|
39
42
|
# config.active_record.database_selector = { delay: 2.seconds }
|
40
43
|
# config.active_record.database_resolver = MyResolver
|
41
44
|
# config.active_record.database_resolver_context = MyResolver::MySession
|
45
|
+
#
|
46
|
+
# Note: If you are using `rails new my_app --minimal` you will need to call
|
47
|
+
# `require "active_support/core_ext/integer/time"` to load the libraries
|
48
|
+
# for +Time+.
|
42
49
|
class DatabaseSelector
|
43
50
|
def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
|
44
51
|
@app = app
|