activerecord 7.0.2.3 → 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.

Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +177 -0
  3. data/lib/active_record/associations/collection_association.rb +1 -2
  4. data/lib/active_record/associations/collection_proxy.rb +2 -2
  5. data/lib/active_record/associations/has_many_association.rb +7 -4
  6. data/lib/active_record/associations/join_dependency.rb +17 -13
  7. data/lib/active_record/associations.rb +15 -15
  8. data/lib/active_record/attribute_methods/serialization.rb +34 -50
  9. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  10. data/lib/active_record/attribute_methods.rb +1 -1
  11. data/lib/active_record/base.rb +3 -3
  12. data/lib/active_record/coders/yaml_column.rb +10 -2
  13. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -1
  14. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
  15. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -3
  16. data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -5
  17. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +9 -1
  18. data/lib/active_record/connection_adapters/mysql/quoting.rb +3 -1
  19. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -1
  20. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  21. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  22. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -1
  23. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -0
  24. data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -3
  25. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  26. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -2
  27. data/lib/active_record/connection_handling.rb +2 -2
  28. data/lib/active_record/core.rb +3 -3
  29. data/lib/active_record/delegated_type.rb +1 -1
  30. data/lib/active_record/encryption/configurable.rb +8 -2
  31. data/lib/active_record/encryption/contexts.rb +3 -3
  32. data/lib/active_record/encryption/derived_secret_key_provider.rb +1 -1
  33. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  34. data/lib/active_record/encryption/encryptable_record.rb +2 -4
  35. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  36. data/lib/active_record/encryption/encryptor.rb +7 -7
  37. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  38. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -4
  39. data/lib/active_record/encryption/message.rb +1 -1
  40. data/lib/active_record/encryption/properties.rb +1 -1
  41. data/lib/active_record/encryption/scheme.rb +1 -1
  42. data/lib/active_record/enum.rb +1 -1
  43. data/lib/active_record/fixtures.rb +4 -4
  44. data/lib/active_record/gem_version.rb +3 -3
  45. data/lib/active_record/locking/pessimistic.rb +3 -3
  46. data/lib/active_record/log_subscriber.rb +10 -5
  47. data/lib/active_record/middleware/database_selector.rb +13 -6
  48. data/lib/active_record/middleware/shard_selector.rb +4 -4
  49. data/lib/active_record/migration/command_recorder.rb +3 -3
  50. data/lib/active_record/migration/compatibility.rb +7 -26
  51. data/lib/active_record/migration.rb +5 -4
  52. data/lib/active_record/model_schema.rb +22 -10
  53. data/lib/active_record/persistence.rb +9 -8
  54. data/lib/active_record/querying.rb +1 -1
  55. data/lib/active_record/railtie.rb +20 -16
  56. data/lib/active_record/railties/databases.rake +16 -11
  57. data/lib/active_record/reflection.rb +6 -0
  58. data/lib/active_record/relation/batches.rb +3 -3
  59. data/lib/active_record/relation/query_methods.rb +27 -6
  60. data/lib/active_record/relation.rb +7 -6
  61. data/lib/active_record/sanitization.rb +6 -5
  62. data/lib/active_record/scoping/default.rb +5 -7
  63. data/lib/active_record/serialization.rb +5 -0
  64. data/lib/active_record/signed_id.rb +2 -2
  65. data/lib/active_record/store.rb +7 -2
  66. data/lib/active_record/tasks/database_tasks.rb +26 -21
  67. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -2
  68. data/lib/active_record/test_fixtures.rb +12 -5
  69. data/lib/active_record/translation.rb +1 -1
  70. data/lib/active_record/validations/associated.rb +3 -3
  71. data/lib/active_record/validations/presence.rb +2 -2
  72. data/lib/active_record/validations/uniqueness.rb +3 -3
  73. data/lib/active_record/version.rb +1 -1
  74. data/lib/active_record.rb +14 -0
  75. 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))
@@ -8,7 +8,9 @@ module ActiveRecord
8
8
  module Quoting # :nodoc:
9
9
  def quote_bound_value(value)
10
10
  case value
11
- when Numeric
11
+ when Rational
12
+ quote(value.to_f.to_s)
13
+ when Numeric, ActiveSupport::Duration
12
14
  quote(value.to_s)
13
15
  when BigDecimal
14
16
  quote(value.to_s("F"))
@@ -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.scan_until(/(?<!\\)(?=")/)
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.scan_until(/(?<!\\)(?=")/)
44
+ unless value = scanner.scan(/^(\\[\\"]|[^\\"])*?(?=")/)
45
45
  raise(ArgumentError, ERROR % scanner.string.inspect)
46
46
  end
47
47
 
@@ -43,7 +43,7 @@ module ActiveRecord
43
43
 
44
44
  # Quotes strings for use in SQL input.
45
45
  def quote_string(s) # :nodoc:
46
- PG::Connection.escape(s)
46
+ @connection.escape(s)
47
47
  end
48
48
 
49
49
  # Checks the following cases:
@@ -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 `datetime`
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 :ruby as your config.active_record.schema_format and you change this
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[:if_exists] == true && !foreign_key_exists?(from_table, to_table)
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: column.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 (ex writing, reading or a custom role) and/or
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
  #
@@ -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. `SELECT first_name, last_name FROM ...` instead of `SELECT * FROM ...`
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 = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
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
- # Overwrite the default class equality method to provide support for decorated models.
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 +Entry#title+, and polymorphism will provide you with the answer.
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 +ActiveRecord::Encryption+
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
- application.config.filter_parameters << encrypted_attribute_name unless ActiveRecord::Encryption.config.excluded_from_filter_parameters.include?(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)
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
- # +ActiveRecord::Encryption+ uses encryption contexts to configure the different entities used to
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 +Context+ that gets configured
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 +Context+.
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 +KeyProvider+ that derives keys from passwords.
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) })
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Encryption
5
- # A +KeyProvider+ that derives keys from passwords.
5
+ # A KeyProvider that derives keys from passwords.
6
6
  class DeterministicKeyProvider < DerivedSecretKeyProvider
7
7
  def initialize(password)
8
8
  passwords = Array(password)
@@ -18,12 +18,10 @@ module ActiveRecord
18
18
  #
19
19
  # === Options
20
20
  #
21
- # * <tt>:key_provider</tt> - Configure a +KeyProvider+ for serving the keys to encrypt and
22
- # decrypt this attribute. If not provided, it will default to +ActiveRecord::Encryption.key_provider+.
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 +ActiveModel::Type+ that encrypts/decrypts strings of text.
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). +ActiveModel::Type::String+ by default.
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 +ActiveRecord::Encryption::EncryptedAttributeType+
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 +KeyProvider+ for getting the keys, and delegate to
13
- # +ActiveRecord::Encryption::Cipher+ the actual encryption algorithm.
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 +ActiveRecord::Encryption::Message+
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
- # +Cipher+-specific options that will be passed to the Cipher configured in
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
- # +Cipher+-specific options that will be passed to the Cipher configured in
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 credential +active_record.encryption.master key+
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 `config.store_key_references` is true, it will also store a reference to
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: Used in +Contact.find_by_email_address(...)+
20
- # * ActiveRecord::Relation: Used in +Contact.internal.find_by_email_address(...)+
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
- # +ActiveRecord::Base+ relies on +ActiveRecord::Relation+ (+ActiveRecord::QueryMethods+) but it does
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
  #
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  # * An encrypted payload
8
8
  # * A list of unencrypted headers
9
9
  #
10
- # See +Encryptor#encrypt+
10
+ # See Encryptor#encrypt
11
11
  class Message
12
12
  attr_accessor :payload, :headers
13
13
 
@@ -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#DEFAULT_PROPERTIES+, +Key+, +Message+
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
 
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  #
7
7
  # It validates and serves attribute encryption options.
8
8
  #
9
- # See +EncryptedAttributeType+, +Context+
9
+ # See EncryptedAttributeType, Context
10
10
  class Scheme
11
11
  attr_accessor :previous_schemes
12
12
 
@@ -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 +HashWithIndifferentAccess+:
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, has_one, has_many)
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 the currently loaded Active Record as a <tt>Gem::Version</tt>
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 = 2
13
- PRE = "3"
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 <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
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 <tt>#lock!</tt>).
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
- # <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
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
- association = event.payload[:reflection].klass
26
- name = event.payload[:reflection].name
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) ? attr.name : attr[i].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 add
23
- # the following options to your environment config:
22
+ # To use the DatabaseSelector in your application with default settings,
23
+ # run the provided generator.
24
24
  #
25
- # # This require is only necessary when using `rails new app --minimal`
26
- # require "active_support/core_ext/integer/time"
25
+ # bin/rails g active_record:multi_db
27
26
  #
28
- # class Application < Rails::Application
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
- # New applications will include these lines commented out in the production.rb.
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