activerecord 8.0.2 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -2
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/belongs_to_association.rb +9 -1
  5. data/lib/active_record/associations/collection_association.rb +3 -3
  6. data/lib/active_record/attribute_methods/query.rb +34 -0
  7. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  8. data/lib/active_record/attribute_methods.rb +1 -1
  9. data/lib/active_record/attributes.rb +35 -24
  10. data/lib/active_record/autosave_association.rb +1 -1
  11. data/lib/active_record/base.rb +2 -2
  12. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -1
  13. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -4
  14. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -6
  15. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +4 -1
  16. data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
  17. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +9 -2
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -2
  19. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -2
  20. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
  21. data/lib/active_record/connection_handling.rb +1 -1
  22. data/lib/active_record/core.rb +8 -6
  23. data/lib/active_record/database_configurations/hash_config.rb +8 -2
  24. data/lib/active_record/delegated_type.rb +1 -1
  25. data/lib/active_record/encryption/encryptable_record.rb +1 -1
  26. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  27. data/lib/active_record/encryption/encryptor.rb +27 -25
  28. data/lib/active_record/enum.rb +13 -12
  29. data/lib/active_record/errors.rb +3 -3
  30. data/lib/active_record/fixture_set/table_row.rb +19 -2
  31. data/lib/active_record/gem_version.rb +1 -1
  32. data/lib/active_record/migration.rb +5 -5
  33. data/lib/active_record/query_logs.rb +4 -0
  34. data/lib/active_record/querying.rb +4 -4
  35. data/lib/active_record/railtie.rb +2 -2
  36. data/lib/active_record/railties/databases.rake +8 -16
  37. data/lib/active_record/relation/calculations.rb +15 -16
  38. data/lib/active_record/relation/finder_methods.rb +14 -13
  39. data/lib/active_record/relation/query_methods.rb +4 -4
  40. data/lib/active_record/relation/spawn_methods.rb +6 -6
  41. data/lib/active_record/relation/where_clause.rb +8 -2
  42. data/lib/active_record/relation.rb +14 -4
  43. data/lib/active_record/secure_token.rb +3 -3
  44. data/lib/active_record/signed_id.rb +3 -3
  45. data/lib/active_record/tasks/database_tasks.rb +22 -14
  46. data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
  47. data/lib/active_record/test_databases.rb +1 -1
  48. data/lib/active_record/transactions.rb +3 -1
  49. data/lib/active_record.rb +3 -2
  50. data/lib/arel/crud.rb +2 -0
  51. data/lib/arel/delete_manager.rb +5 -0
  52. data/lib/arel/nodes/delete_statement.rb +4 -2
  53. data/lib/arel/nodes/update_statement.rb +4 -2
  54. data/lib/arel/select_manager.rb +6 -2
  55. data/lib/arel/update_manager.rb +5 -0
  56. data/lib/arel/visitors/dot.rb +2 -0
  57. data/lib/arel/visitors/to_sql.rb +2 -0
  58. metadata +10 -10
@@ -277,7 +277,7 @@ module ActiveRecord
277
277
  return super if StatementCache.unsupported_value?(id)
278
278
 
279
279
  cached_find_by([primary_key], [id]) ||
280
- raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
280
+ raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id.inspect}", name, primary_key, id))
281
281
  end
282
282
 
283
283
  def find_by(*args) # :nodoc:
@@ -600,7 +600,7 @@ module ActiveRecord
600
600
  #
601
601
  # topic = Topic.new(title: "Budget", author_name: "Jason")
602
602
  # topic.slice(:title, :author_name)
603
- # => { "title" => "Budget", "author_name" => "Jason" }
603
+ # # => { "title" => "Budget", "author_name" => "Jason" }
604
604
  #
605
605
  #--
606
606
  # Implemented by ActiveModel::Access#slice.
@@ -614,7 +614,7 @@ module ActiveRecord
614
614
  #
615
615
  # topic = Topic.new(title: "Budget", author_name: "Jason")
616
616
  # topic.values_at(:title, :author_name)
617
- # => ["Budget", "Jason"]
617
+ # # => ["Budget", "Jason"]
618
618
  #
619
619
  #--
620
620
  # Implemented by ActiveModel::Access#values_at.
@@ -691,12 +691,14 @@ module ActiveRecord
691
691
  # Sets the record to strict_loading mode. This will raise an error
692
692
  # if the record tries to lazily load an association.
693
693
  #
694
+ # NOTE: Strict loading is disabled during validation in order to let the record validate its association.
695
+ #
694
696
  # user = User.first
695
697
  # user.strict_loading! # => true
696
698
  # user.address.city
697
- # => ActiveRecord::StrictLoadingViolationError
699
+ # # => ActiveRecord::StrictLoadingViolationError
698
700
  # user.comments.to_a
699
- # => ActiveRecord::StrictLoadingViolationError
701
+ # # => ActiveRecord::StrictLoadingViolationError
700
702
  #
701
703
  # ==== Parameters
702
704
  #
@@ -716,7 +718,7 @@ module ActiveRecord
716
718
  # user.address.city # => "Tatooine"
717
719
  # user.comments.to_a # => [#<Comment:0x00...]
718
720
  # user.comments.first.ratings.to_a
719
- # => ActiveRecord::StrictLoadingViolationError
721
+ # # => ActiveRecord::StrictLoadingViolationError
720
722
  def strict_loading!(value = true, mode: :all)
721
723
  unless [:all, :n_plus_one_only].include?(mode)
722
724
  raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
@@ -146,7 +146,7 @@ module ActiveRecord
146
146
  #
147
147
  # If the config option is set that will be used. Otherwise Rails will generate
148
148
  # the filename from the database config name.
149
- def schema_dump(format = ActiveRecord.schema_format)
149
+ def schema_dump(format = schema_format)
150
150
  if configuration_hash.key?(:schema_dump)
151
151
  if config = configuration_hash[:schema_dump]
152
152
  config
@@ -158,6 +158,12 @@ module ActiveRecord
158
158
  end
159
159
  end
160
160
 
161
+ def schema_format # :nodoc:
162
+ format = configuration_hash[:schema_format]&.to_sym || ActiveRecord.schema_format
163
+ raise "Invalid schema format" unless [ :ruby, :sql ].include? format
164
+ format
165
+ end
166
+
161
167
  def database_tasks? # :nodoc:
162
168
  !replica? && !!configuration_hash.fetch(:database_tasks, true)
163
169
  end
@@ -168,7 +174,7 @@ module ActiveRecord
168
174
 
169
175
  private
170
176
  def schema_file_type(format)
171
- case format
177
+ case format.to_sym
172
178
  when :ruby
173
179
  "schema.rb"
174
180
  when :sql
@@ -202,7 +202,7 @@ module ActiveRecord
202
202
  # @entry.access_notice_message
203
203
  # @entry.access_notice_message?
204
204
  #
205
- # === Options
205
+ # ==== Options
206
206
  #
207
207
  # The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
208
208
  # The following options can be included to specialize the behavior of the delegated type convenience methods.
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  class_methods do
17
17
  # Encrypts the +name+ attribute.
18
18
  #
19
- # === Options
19
+ # ==== Options
20
20
  #
21
21
  # * <tt>:key_provider</tt> - A key provider to provide encryption and decryption keys. Defaults to
22
22
  # +ActiveRecord::Encryption.key_provider+.
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  delegate :key_provider, :downcase?, :deterministic?, :previous_schemes, :with_context, :fixed?, to: :scheme
16
16
  delegate :accessor, :type, to: :cast_type
17
17
 
18
- # === Options
18
+ # ==== Options
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
@@ -11,41 +11,43 @@ module ActiveRecord
11
11
  # It interacts with a KeyProvider for getting the keys, and delegate to
12
12
  # ActiveRecord::Encryption::Cipher the actual encryption algorithm.
13
13
  class Encryptor
14
- # The compressor to use for compressing the payload
14
+ # The compressor to use for compressing the payload.
15
15
  attr_reader :compressor
16
16
 
17
- # === Options
17
+ # ==== Options
18
18
  #
19
- # * <tt>:compress</tt> - Boolean indicating whether records should be compressed before encryption.
20
- # Defaults to +true+.
21
- # * <tt>:compressor</tt> - The compressor to use.
22
- # 1. If compressor is provided, it will be used.
23
- # 2. If not, it will use ActiveRecord::Encryption.config.compressor which default value is +Zlib+.
24
- # If you want to use a custom compressor, it must respond to +deflate+ and +inflate+.
19
+ # [+:compress+]
20
+ # Boolean indicating whether records should be compressed before
21
+ # encryption. Defaults to +true+.
22
+ #
23
+ # [+:compressor+]
24
+ # The compressor to use. It must respond to +deflate+ and +inflate+.
25
+ # If not provided, will default to +ActiveRecord::Encryption.config.compressor+,
26
+ # which itself defaults to +Zlib+.
25
27
  def initialize(compress: true, compressor: nil)
26
28
  @compress = compress
27
29
  @compressor = compressor || ActiveRecord::Encryption.config.compressor
28
30
  end
29
31
 
30
- # Encrypts +clean_text+ and returns the encrypted result
32
+ # Encrypts +clean_text+ and returns the encrypted result.
31
33
  #
32
34
  # Internally, it will:
33
35
  #
34
- # 1. Create a new ActiveRecord::Encryption::Message
35
- # 2. Compress and encrypt +clean_text+ as the message payload
36
- # 3. Serialize it with +ActiveRecord::Encryption.message_serializer+ (+ActiveRecord::Encryption::SafeMarshal+
37
- # by default)
38
- # 4. Encode the result with Base 64
36
+ # 1. Create a new ActiveRecord::Encryption::Message.
37
+ # 2. Compress and encrypt +clean_text+ as the message payload.
38
+ # 3. Serialize it with +ActiveRecord::Encryption.message_serializer+
39
+ # (+ActiveRecord::Encryption::SafeMarshal+ by default).
40
+ # 4. Encode the result with Base64.
39
41
  #
40
- # === Options
42
+ # ==== Options
41
43
  #
42
- # [:key_provider]
44
+ # [+:key_provider+]
43
45
  # Key provider to use for the encryption operation. It will default to
44
46
  # +ActiveRecord::Encryption.key_provider+ when not provided.
45
47
  #
46
- # [:cipher_options]
48
+ # [+:cipher_options+]
47
49
  # Cipher-specific options that will be passed to the Cipher configured in
48
- # +ActiveRecord::Encryption.cipher+
50
+ # +ActiveRecord::Encryption.cipher+.
49
51
  def encrypt(clear_text, key_provider: default_key_provider, cipher_options: {})
50
52
  clear_text = force_encoding_if_needed(clear_text) if cipher_options[:deterministic]
51
53
 
@@ -53,17 +55,17 @@ module ActiveRecord
53
55
  serialize_message build_encrypted_message(clear_text, key_provider: key_provider, cipher_options: cipher_options)
54
56
  end
55
57
 
56
- # Decrypts an +encrypted_text+ and returns the result as clean text
58
+ # Decrypts an +encrypted_text+ and returns the result as clean text.
57
59
  #
58
- # === Options
60
+ # ==== Options
59
61
  #
60
- # [:key_provider]
62
+ # [+:key_provider+]
61
63
  # Key provider to use for the encryption operation. It will default to
62
- # +ActiveRecord::Encryption.key_provider+ when not provided
64
+ # +ActiveRecord::Encryption.key_provider+ when not provided.
63
65
  #
64
- # [:cipher_options]
66
+ # [+:cipher_options+]
65
67
  # Cipher-specific options that will be passed to the Cipher configured in
66
- # +ActiveRecord::Encryption.cipher+
68
+ # +ActiveRecord::Encryption.cipher+.
67
69
  def decrypt(encrypted_text, key_provider: default_key_provider, cipher_options: {})
68
70
  message = deserialize_message(encrypted_text)
69
71
  keys = key_provider.decryption_keys(message)
@@ -73,7 +75,7 @@ module ActiveRecord
73
75
  raise Errors::Decryption
74
76
  end
75
77
 
76
- # Returns whether the text is encrypted or not
78
+ # Returns whether the text is encrypted or not.
77
79
  def encrypted?(text)
78
80
  deserialize_message(text)
79
81
  true
@@ -119,7 +119,18 @@ module ActiveRecord
119
119
  # enum :status, [ :active, :archived ], instance_methods: false
120
120
  # end
121
121
  #
122
- # If you want the enum value to be validated before saving, use the option +:validate+:
122
+ # By default, an +ArgumentError+ will be raised when assigning an invalid value:
123
+ #
124
+ # class Conversation < ActiveRecord::Base
125
+ # enum :status, [ :active, :archived ]
126
+ # end
127
+ #
128
+ # conversation = Conversation.new
129
+ #
130
+ # conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
131
+ #
132
+ # If, instead, you want the enum value to be validated before saving, use the
133
+ # +:validate+ option:
123
134
  #
124
135
  # class Conversation < ActiveRecord::Base
125
136
  # enum :status, [ :active, :archived ], validate: true
@@ -136,7 +147,7 @@ module ActiveRecord
136
147
  # conversation.status = :active
137
148
  # conversation.valid? # => true
138
149
  #
139
- # It is also possible to pass additional validation options:
150
+ # You may also pass additional validation options:
140
151
  #
141
152
  # class Conversation < ActiveRecord::Base
142
153
  # enum :status, [ :active, :archived ], validate: { allow_nil: true }
@@ -152,16 +163,6 @@ module ActiveRecord
152
163
  #
153
164
  # conversation.status = :active
154
165
  # conversation.valid? # => true
155
- #
156
- # Otherwise +ArgumentError+ will raise:
157
- #
158
- # class Conversation < ActiveRecord::Base
159
- # enum :status, [ :active, :archived ]
160
- # end
161
- #
162
- # conversation = Conversation.new
163
- #
164
- # conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
165
166
  module Enum
166
167
  def self.extended(base) # :nodoc:
167
168
  base.class_attribute(:defined_enums, instance_writer: false, default: {})
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
 
14
14
  # Raised when the single-table inheritance mechanism fails to locate the subclass
15
15
  # (for example due to improper usage of column that
16
- # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
16
+ # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema.inheritance_column]
17
17
  # points to).
18
18
  class SubclassNotFound < ActiveRecordError
19
19
  end
@@ -444,7 +444,7 @@ module ActiveRecord
444
444
  UnknownAttributeError = ActiveModel::UnknownAttributeError
445
445
 
446
446
  # Raised when an error occurred while doing a mass assignment to an attribute through the
447
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
447
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
448
448
  # The exception has an +attribute+ property that is the name of the offending attribute.
449
449
  class AttributeAssignmentError < ActiveRecordError
450
450
  attr_reader :exception, :attribute
@@ -457,7 +457,7 @@ module ActiveRecord
457
457
  end
458
458
 
459
459
  # Raised when there are multiple errors while doing a mass assignment through the
460
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
460
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=]
461
461
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
462
462
  # objects, each corresponding to the error while assigning to an attribute.
463
463
  class MultiparameterAssignmentErrors < ActiveRecordError
@@ -193,8 +193,25 @@ module ActiveRecord
193
193
 
194
194
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
195
195
  joins = targets.map do |target|
196
- join = { lhs_key => @row[model_metadata.primary_key_name],
197
- rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
196
+ join = {}
197
+
198
+ if rhs_key.is_a?(Array)
199
+ composite_key = ActiveRecord::FixtureSet.composite_identify(target, rhs_key)
200
+ composite_key.each do |column, value|
201
+ join[column] = value
202
+ end
203
+ else
204
+ join[rhs_key] = ActiveRecord::FixtureSet.identify(target, column_type)
205
+ end
206
+
207
+ if lhs_key.is_a?(Array)
208
+ lhs_key.zip(model_metadata.primary_key_name).each do |fkey, pkey|
209
+ join[fkey] = @row[pkey]
210
+ end
211
+ else
212
+ join[lhs_key] = @row[model_metadata.primary_key_name]
213
+ end
214
+
198
215
  association.timestamp_column_names.each do |col|
199
216
  join[col] = @now
200
217
  end
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
- TINY = 2
12
+ TINY = 3
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -148,11 +148,10 @@ module ActiveRecord
148
148
  include ActiveSupport::ActionableError
149
149
 
150
150
  action "Run pending migrations" do
151
- ActiveRecord::Tasks::DatabaseTasks.migrate
151
+ ActiveRecord::Tasks::DatabaseTasks.migrate_all
152
152
 
153
153
  if ActiveRecord.dump_schema_after_migration
154
- connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
155
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(connection.pool.db_config)
154
+ ActiveRecord::Tasks::DatabaseTasks.dump_all
156
155
  end
157
156
  end
158
157
 
@@ -747,7 +746,7 @@ module ActiveRecord
747
746
  private
748
747
  def any_schema_needs_update?
749
748
  !db_configs_in_current_env.all? do |db_config|
750
- Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord.schema_format)
749
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config)
751
750
  end
752
751
  end
753
752
 
@@ -1529,7 +1528,8 @@ module ActiveRecord
1529
1528
  return if down? && !migrated.include?(migration.version.to_i)
1530
1529
  return if up? && migrated.include?(migration.version.to_i)
1531
1530
 
1532
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1531
+ message = up? ? "Migrating to" : "Reverting"
1532
+ Base.logger.info "#{message} #{migration.name} (#{migration.version})" if Base.logger
1533
1533
 
1534
1534
  ddl_transaction(migration) do
1535
1535
  migration.migrate(@direction)
@@ -28,6 +28,10 @@ module ActiveRecord
28
28
  # * +database+
29
29
  # * +source_location+
30
30
  #
31
+ # WARNING: Calculating the +source_location+ of a query can be slow, so you should consider its impact if using it in a production environment.
32
+ #
33
+ # Also see {config.active_record.verbose_query_logs}[https://guides.rubyonrails.org/debugging_rails_applications.html#verbose-query-logs].
34
+ #
31
35
  # Action Controller adds default tags when loaded:
32
36
  #
33
37
  # * +controller+
@@ -46,8 +46,8 @@ module ActiveRecord
46
46
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
47
47
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
48
48
  #
49
- # Note that building your own SQL query string from user input may expose your application to
50
- # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
49
+ # Note that building your own SQL query string from user input {may expose your application to
50
+ # injection attacks}[https://guides.rubyonrails.org/security.html#sql-injection].
51
51
  def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
52
52
  result = with_connection do |c|
53
53
  _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
@@ -55,7 +55,7 @@ module ActiveRecord
55
55
  _load_from_sql(result, &block)
56
56
  end
57
57
 
58
- # Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
58
+ # Same as #find_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
59
59
  def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
60
60
  with_connection do |c|
61
61
  _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  end
113
113
  end
114
114
 
115
- # Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
115
+ # Same as #count_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
116
116
  def async_count_by_sql(sql)
117
117
  with_connection do |c|
118
118
  c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
@@ -23,8 +23,8 @@ module ActiveRecord
23
23
  config.action_dispatch.rescue_responses.merge!(
24
24
  "ActiveRecord::RecordNotFound" => :not_found,
25
25
  "ActiveRecord::StaleObjectError" => :conflict,
26
- "ActiveRecord::RecordInvalid" => :unprocessable_entity,
27
- "ActiveRecord::RecordNotSaved" => :unprocessable_entity
26
+ "ActiveRecord::RecordInvalid" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT,
27
+ "ActiveRecord::RecordNotSaved" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT
28
28
  )
29
29
 
30
30
  config.active_record.use_schema_cache_dump = true
@@ -447,18 +447,14 @@ db_namespace = namespace :db do
447
447
  namespace :schema do
448
448
  desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
449
449
  task dump: :load_config do
450
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do |pool|
451
- db_config = pool.db_config
452
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
453
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
454
- end
450
+ ActiveRecord::Tasks::DatabaseTasks.dump_all
455
451
 
456
452
  db_namespace["schema:dump"].reenable
457
453
  end
458
454
 
459
455
  desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the database"
460
456
  task load: [:load_config, :check_protected_environments] do
461
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ActiveRecord.schema_format, ENV["SCHEMA"])
457
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ENV["SCHEMA_FORMAT"], ENV["SCHEMA"])
462
458
  end
463
459
 
464
460
  namespace :dump do
@@ -467,8 +463,7 @@ db_namespace = namespace :db do
467
463
  task name => :load_config do
468
464
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
469
465
  db_config = pool.db_config
470
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
471
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
466
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
472
467
  end
473
468
 
474
469
  db_namespace["schema:dump:#{name}"].reenable
@@ -478,12 +473,11 @@ db_namespace = namespace :db do
478
473
 
479
474
  namespace :load do
480
475
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
481
- desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the #{name} database"
476
+ desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) into the #{name} database"
482
477
  task name => "db:test:purge:#{name}" do
483
478
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
484
479
  db_config = pool.db_config
485
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
486
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
480
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
487
481
  end
488
482
  end
489
483
  end
@@ -527,13 +521,12 @@ db_namespace = namespace :db do
527
521
  end
528
522
 
529
523
  namespace :test do
530
- # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
524
+ # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on configuration)"
531
525
  task load_schema: %w(db:test:purge) do
532
526
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test") do |pool|
533
527
  db_config = pool.db_config
534
528
  ActiveRecord::Schema.verbose = false
535
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
536
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
529
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
537
530
  end
538
531
  end
539
532
 
@@ -558,8 +551,7 @@ db_namespace = namespace :db do
558
551
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test", name: name) do |pool|
559
552
  db_config = pool.db_config
560
553
  ActiveRecord::Schema.verbose = false
561
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
562
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
554
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
563
555
  end
564
556
  end
565
557
  end
@@ -60,37 +60,37 @@ module ActiveRecord
60
60
  # Person.distinct.count(:age)
61
61
  # # => counts the number of different age values
62
62
  #
63
- # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
63
+ # If +count+ is used with {Relation#group}[rdoc-ref:QueryMethods#group],
64
64
  # it returns a Hash whose keys represent the aggregated column,
65
65
  # and the values are the respective amounts:
66
66
  #
67
67
  # Person.group(:city).count
68
68
  # # => { 'Rome' => 5, 'Paris' => 3 }
69
69
  #
70
- # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
70
+ # If +count+ is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
71
71
  # keys are an array containing the individual values of each column and the value
72
- # of each key would be the #count.
72
+ # of each key would be the count.
73
73
  #
74
74
  # Article.group(:status, :category).count
75
75
  # # => {["draft", "business"]=>10, ["draft", "technology"]=>4, ["published", "technology"]=>2}
76
76
  #
77
- # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
77
+ # If +count+ is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
78
78
  #
79
79
  # Person.select(:age).count
80
80
  # # => counts the number of different age values
81
81
  #
82
- # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
82
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid +count+ expressions. The specifics differ
83
83
  # between databases. In invalid cases, an error from the database is thrown.
84
84
  #
85
- # When given a block, loads all records in the relation, if the relation
86
- # hasn't been loaded yet. Calls the block with each record in the relation.
87
- # Returns the number of records for which the block returns a truthy value.
85
+ # When given a block, calls the block with each record in the relation and
86
+ # returns the number of records for which the block returns a truthy value.
88
87
  #
89
88
  # Person.count { |person| person.age > 21 }
90
89
  # # => counts the number of people older that 21
91
90
  #
92
- # Note: If there are a lot of records in the relation, loading all records
93
- # could result in performance issues.
91
+ # If the relation hasn't been loaded yet, calling +count+ with a block will
92
+ # load all records in the relation. If there are a lot of records in the
93
+ # relation, loading all records could result in performance issues.
94
94
  def count(column_name = nil)
95
95
  if block_given?
96
96
  unless column_name.nil?
@@ -159,16 +159,15 @@ module ActiveRecord
159
159
  #
160
160
  # Person.sum(:age) # => 4562
161
161
  #
162
- # When given a block, loads all records in the relation, if the relation
163
- # hasn't been loaded yet. Calls the block with each record in the relation.
164
- # Returns the sum of +initial_value_or_column+ and the block return
165
- # values:
162
+ # When given a block, calls the block with each record in the relation and
163
+ # returns the sum of +initial_value_or_column+ plus the block return values:
166
164
  #
167
165
  # Person.sum { |person| person.age } # => 4562
168
166
  # Person.sum(1000) { |person| person.age } # => 5562
169
167
  #
170
- # Note: If there are a lot of records in the relation, loading all records
171
- # could result in performance issues.
168
+ # If the relation hasn't been loaded yet, calling +sum+ with a block will
169
+ # load all records in the relation. If there are a lot of records in the
170
+ # relation, loading all records could result in performance issues.
172
171
  def sum(initial_value_or_column = 0, &block)
173
172
  if block_given?
174
173
  map(&block).sum(initial_value_or_column)
@@ -24,22 +24,22 @@ module ActiveRecord
24
24
  # TravelRoute.primary_key = [:origin, :destination]
25
25
  #
26
26
  # TravelRoute.find(["Ottawa", "London"])
27
- # => #<TravelRoute origin: "Ottawa", destination: "London">
27
+ # # => #<TravelRoute origin: "Ottawa", destination: "London">
28
28
  #
29
29
  # TravelRoute.find([["Paris", "Montreal"]])
30
- # => [#<TravelRoute origin: "Paris", destination: "Montreal">]
30
+ # # => [#<TravelRoute origin: "Paris", destination: "Montreal">]
31
31
  #
32
32
  # TravelRoute.find(["New York", "Las Vegas"], ["New York", "Portland"])
33
- # => [
34
- # #<TravelRoute origin: "New York", destination: "Las Vegas">,
35
- # #<TravelRoute origin: "New York", destination: "Portland">
36
- # ]
33
+ # # => [
34
+ # # #<TravelRoute origin: "New York", destination: "Las Vegas">,
35
+ # # #<TravelRoute origin: "New York", destination: "Portland">
36
+ # # ]
37
37
  #
38
38
  # TravelRoute.find([["Berlin", "London"], ["Barcelona", "Lisbon"]])
39
- # => [
40
- # #<TravelRoute origin: "Berlin", destination: "London">,
41
- # #<TravelRoute origin: "Barcelona", destination: "Lisbon">
42
- # ]
39
+ # # => [
40
+ # # #<TravelRoute origin: "Berlin", destination: "London">,
41
+ # # #<TravelRoute origin: "Barcelona", destination: "Lisbon">
42
+ # # ]
43
43
  #
44
44
  # NOTE: The returned records are in the same order as the ids you provide.
45
45
  # If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
@@ -424,12 +424,13 @@ module ActiveRecord
424
424
  error << " with#{conditions}" if conditions
425
425
  raise RecordNotFound.new(error, name, key)
426
426
  elsif Array.wrap(ids).size == 1
427
- error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
427
+ id = Array.wrap(ids)[0]
428
+ error = "Couldn't find #{name} with '#{key}'=#{id.inspect}#{conditions}"
428
429
  raise RecordNotFound.new(error, name, key, ids)
429
430
  else
430
431
  error = +"Couldn't find all #{name.pluralize} with '#{key}': "
431
- error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
432
- error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
432
+ error << "(#{ids.map(&:inspect).join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
433
+ error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.map(&:inspect).join(', ')}." if not_found_ids
433
434
  raise RecordNotFound.new(error, name, key, ids)
434
435
  end
435
436
  end
@@ -510,7 +510,7 @@ module ActiveRecord
510
510
  # # WITH RECURSIVE post_and_replies AS (
511
511
  # # (SELECT * FROM posts WHERE id = 42)
512
512
  # # UNION ALL
513
- # # (SELECT * FROM posts JOIN posts_and_replies ON posts.in_reply_to_id = posts_and_replies.id)
513
+ # # (SELECT * FROM posts JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id)
514
514
  # # )
515
515
  # # SELECT * FROM posts
516
516
  #
@@ -1299,13 +1299,13 @@ module ActiveRecord
1299
1299
  #
1300
1300
  # users = User.readonly
1301
1301
  # users.first.save
1302
- # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
1302
+ # # => ActiveRecord::ReadOnlyRecord: User is marked as readonly
1303
1303
  #
1304
1304
  # To make a readonly relation writable, pass +false+.
1305
1305
  #
1306
1306
  # users.readonly(false)
1307
1307
  # users.first.save
1308
- # => true
1308
+ # # => true
1309
1309
  def readonly(value = true)
1310
1310
  spawn.readonly!(value)
1311
1311
  end
@@ -1320,7 +1320,7 @@ module ActiveRecord
1320
1320
  #
1321
1321
  # user = User.strict_loading.first
1322
1322
  # user.comments.to_a
1323
- # => ActiveRecord::StrictLoadingViolationError
1323
+ # # => ActiveRecord::StrictLoadingViolationError
1324
1324
  def strict_loading(value = true)
1325
1325
  spawn.strict_loading!(value)
1326
1326
  end