activerecord 7.0.0 → 7.0.4

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +357 -0
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_record/associations/collection_association.rb +1 -2
  5. data/lib/active_record/associations/collection_proxy.rb +2 -2
  6. data/lib/active_record/associations/has_many_association.rb +7 -4
  7. data/lib/active_record/associations/join_dependency.rb +17 -13
  8. data/lib/active_record/associations.rb +38 -17
  9. data/lib/active_record/attribute_methods/serialization.rb +34 -50
  10. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  11. data/lib/active_record/attribute_methods.rb +2 -2
  12. data/lib/active_record/autosave_association.rb +2 -2
  13. data/lib/active_record/base.rb +3 -3
  14. data/lib/active_record/coders/yaml_column.rb +10 -2
  15. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -1
  16. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  17. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -4
  18. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  19. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +12 -7
  20. data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -5
  21. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +10 -2
  22. data/lib/active_record/connection_adapters/mysql/database_statements.rb +1 -1
  23. data/lib/active_record/connection_adapters/mysql/quoting.rb +3 -1
  24. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  25. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  26. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  27. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -0
  28. data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
  29. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -1
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +10 -3
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +6 -5
  32. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  33. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -14
  34. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +35 -2
  35. data/lib/active_record/connection_handling.rb +2 -2
  36. data/lib/active_record/core.rb +3 -3
  37. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  38. data/lib/active_record/database_configurations.rb +1 -1
  39. data/lib/active_record/delegated_type.rb +1 -1
  40. data/lib/active_record/encryption/configurable.rb +9 -3
  41. data/lib/active_record/encryption/contexts.rb +3 -3
  42. data/lib/active_record/encryption/derived_secret_key_provider.rb +1 -1
  43. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  44. data/lib/active_record/encryption/encryptable_record.rb +2 -4
  45. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  46. data/lib/active_record/encryption/encryptor.rb +7 -7
  47. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  48. data/lib/active_record/encryption/extended_deterministic_queries.rb +28 -28
  49. data/lib/active_record/encryption/message.rb +1 -1
  50. data/lib/active_record/encryption/properties.rb +1 -1
  51. data/lib/active_record/encryption/scheme.rb +1 -1
  52. data/lib/active_record/enum.rb +1 -1
  53. data/lib/active_record/fixtures.rb +5 -5
  54. data/lib/active_record/gem_version.rb +2 -2
  55. data/lib/active_record/integration.rb +2 -2
  56. data/lib/active_record/locking/pessimistic.rb +3 -3
  57. data/lib/active_record/log_subscriber.rb +10 -5
  58. data/lib/active_record/middleware/database_selector.rb +13 -6
  59. data/lib/active_record/middleware/shard_selector.rb +4 -4
  60. data/lib/active_record/migration/command_recorder.rb +3 -3
  61. data/lib/active_record/migration/compatibility.rb +10 -7
  62. data/lib/active_record/migration.rb +6 -5
  63. data/lib/active_record/model_schema.rb +22 -10
  64. data/lib/active_record/persistence.rb +9 -8
  65. data/lib/active_record/querying.rb +1 -1
  66. data/lib/active_record/railtie.rb +22 -18
  67. data/lib/active_record/railties/databases.rake +16 -11
  68. data/lib/active_record/reflection.rb +7 -1
  69. data/lib/active_record/relation/batches.rb +3 -3
  70. data/lib/active_record/relation/calculations.rb +3 -2
  71. data/lib/active_record/relation/delegation.rb +1 -1
  72. data/lib/active_record/relation/query_methods.rb +46 -11
  73. data/lib/active_record/relation.rb +22 -6
  74. data/lib/active_record/sanitization.rb +6 -5
  75. data/lib/active_record/schema.rb +38 -23
  76. data/lib/active_record/schema_dumper.rb +15 -16
  77. data/lib/active_record/scoping/default.rb +5 -7
  78. data/lib/active_record/serialization.rb +5 -0
  79. data/lib/active_record/signed_id.rb +2 -2
  80. data/lib/active_record/store.rb +7 -2
  81. data/lib/active_record/tasks/database_tasks.rb +32 -23
  82. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -2
  83. data/lib/active_record/test_fixtures.rb +12 -5
  84. data/lib/active_record/translation.rb +1 -1
  85. data/lib/active_record/validations/associated.rb +3 -3
  86. data/lib/active_record/validations/presence.rb +2 -2
  87. data/lib/active_record/validations/uniqueness.rb +3 -3
  88. data/lib/active_record/version.rb +1 -1
  89. data/lib/active_record.rb +15 -1
  90. metadata +13 -13
@@ -10,10 +10,10 @@ module ActiveRecord
10
10
  module QueryMethods
11
11
  include ActiveModel::ForbiddenAttributesProtection
12
12
 
13
- # WhereChain objects act as placeholder for queries in which #where does not have any parameter.
14
- # In this case, #where must be chained with #not to return a new relation.
13
+ # WhereChain objects act as placeholder for queries in which +where+ does not have any parameter.
14
+ # In this case, +where+ can be chained to return a new relation.
15
15
  class WhereChain
16
- def initialize(scope)
16
+ def initialize(scope) # :nodoc:
17
17
  @scope = scope
18
18
  end
19
19
 
@@ -40,6 +40,13 @@ module ActiveRecord
40
40
  #
41
41
  # User.where.not(name: "Jon", role: "admin")
42
42
  # # SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')
43
+ #
44
+ # If there is a non-nil condition on a nullable column in the hash condition, the records that have
45
+ # nil values on the nullable column won't be returned.
46
+ # User.create!(nullable_country: nil)
47
+ # User.where.not(nullable_country: "UK")
48
+ # # SELECT * FROM users WHERE NOT (nullable_country = 'UK')
49
+ # # => []
43
50
  def not(opts, *rest)
44
51
  where_clause = @scope.send(:build_where_clause, opts, rest)
45
52
 
@@ -68,7 +75,7 @@ module ActiveRecord
68
75
  # # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
69
76
  def associated(*associations)
70
77
  associations.each do |association|
71
- reflection = @scope.klass._reflect_on_association(association)
78
+ reflection = scope_association_reflection(association)
72
79
  @scope.joins!(association)
73
80
  self.not(reflection.table_name => { reflection.association_primary_key => nil })
74
81
  end
@@ -96,13 +103,22 @@ module ActiveRecord
96
103
  # # WHERE "authors"."id" IS NULL AND "comments"."id" IS NULL
97
104
  def missing(*associations)
98
105
  associations.each do |association|
99
- reflection = @scope.klass._reflect_on_association(association)
106
+ reflection = scope_association_reflection(association)
100
107
  @scope.left_outer_joins!(association)
101
108
  @scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
102
109
  end
103
110
 
104
111
  @scope
105
112
  end
113
+
114
+ private
115
+ def scope_association_reflection(association)
116
+ reflection = @scope.klass._reflect_on_association(association)
117
+ unless reflection
118
+ raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{@scope.name}`.")
119
+ end
120
+ reflection
121
+ end
106
122
  end
107
123
 
108
124
  FROZEN_EMPTY_ARRAY = [].freeze
@@ -153,7 +169,7 @@ module ActiveRecord
153
169
  #
154
170
  # users = User.includes(:address, friends: [:address, :followers])
155
171
  #
156
- # === conditions
172
+ # === Conditions
157
173
  #
158
174
  # If you want to add string conditions to your included models, you'll have
159
175
  # to explicitly reference them. For example:
@@ -424,18 +440,23 @@ module ActiveRecord
424
440
  # adapter this will either use a CASE statement or a built-in function.
425
441
  #
426
442
  # User.in_order_of(:id, [1, 5, 3])
427
- # # SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)
443
+ # # SELECT "users".* FROM "users"
444
+ # # ORDER BY FIELD("users"."id", 1, 5, 3)
445
+ # # WHERE "users"."id" IN (1, 5, 3)
428
446
  #
429
447
  def in_order_of(column, values)
430
448
  klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
449
+ return spawn.none! if values.empty?
431
450
 
432
451
  references = column_references([column])
433
452
  self.references_values |= references unless references.empty?
434
453
 
435
454
  values = values.map { |value| type_caster.type_cast_for_database(column, value) }
436
- column = order_column(column.to_s) if column.is_a?(Symbol)
455
+ arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
437
456
 
438
- spawn.order!(connection.field_ordered_value(column, values))
457
+ spawn
458
+ .order!(connection.field_ordered_value(arel_column, values))
459
+ .where!(arel_column.in(values))
439
460
  end
440
461
 
441
462
  # Replaces any existing order defined on the relation with the specified order.
@@ -696,12 +717,26 @@ module ActiveRecord
696
717
  # === no argument
697
718
  #
698
719
  # If no argument is passed, #where returns a new instance of WhereChain, that
699
- # can be chained with #not to return a new relation that negates the where clause.
720
+ # can be chained with WhereChain#not, WhereChain#missing, or WhereChain#associated.
721
+ #
722
+ # Chaining with WhereChain#not:
700
723
  #
701
724
  # User.where.not(name: "Jon")
702
725
  # # SELECT * FROM users WHERE name != 'Jon'
703
726
  #
704
- # See WhereChain for more details on #not.
727
+ # Chaining with WhereChain#associated:
728
+ #
729
+ # Post.where.associated(:author)
730
+ # # SELECT "posts".* FROM "posts"
731
+ # # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
732
+ # # WHERE "authors"."id" IS NOT NULL
733
+ #
734
+ # Chaining with WhereChain#missing:
735
+ #
736
+ # Post.where.missing(:author)
737
+ # # SELECT "posts".* FROM "posts"
738
+ # # LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
739
+ # # WHERE "authors"."id" IS NULL
705
740
  #
706
741
  # === blank condition
707
742
  #
@@ -388,7 +388,7 @@ module ActiveRecord
388
388
  end
389
389
 
390
390
  if timestamp
391
- "#{size}-#{timestamp.utc.to_formatted_s(cache_timestamp_format)}"
391
+ "#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
392
392
  else
393
393
  "#{size}"
394
394
  end
@@ -429,10 +429,10 @@ module ActiveRecord
429
429
  end
430
430
  end
431
431
 
432
- def _exec_scope(*args, &block) # :nodoc:
432
+ def _exec_scope(...) # :nodoc:
433
433
  @delegate_to_klass = true
434
434
  registry = klass.scope_registry
435
- _scoping(nil, registry) { instance_exec(*args, &block) || self }
435
+ _scoping(nil, registry) { instance_exec(...) || self }
436
436
  ensure
437
437
  @delegate_to_klass = false
438
438
  end
@@ -646,6 +646,21 @@ module ActiveRecord
646
646
  # Schedule the query to be performed from a background thread pool.
647
647
  #
648
648
  # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
649
+ #
650
+ # When the +Relation+ is iterated, if the background query wasn't executed yet,
651
+ # it will be performed by the foreground thread.
652
+ #
653
+ # Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
654
+ # for queries to actually be executed concurrently. Otherwise it defaults to
655
+ # executing them in the foreground.
656
+ #
657
+ # +load_async+ will also fall back to executing in the foreground in the test environment when transactional
658
+ # fixtures are enabled.
659
+ #
660
+ # If the query was actually executed in the background, the Active Record logs will show
661
+ # it by prefixing the log line with <tt>ASYNC</tt>:
662
+ #
663
+ # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
649
664
  def load_async
650
665
  return load if !connection.async_enabled?
651
666
 
@@ -697,6 +712,7 @@ module ActiveRecord
697
712
  @to_sql = @arel = @loaded = @should_eager_load = nil
698
713
  @offsets = @take = nil
699
714
  @cache_keys = nil
715
+ @cache_versions = nil
700
716
  @records = nil
701
717
  self
702
718
  end
@@ -721,7 +737,7 @@ module ActiveRecord
721
737
  #
722
738
  # User.where(name: 'Oscar').where_values_hash
723
739
  # # => {name: "Oscar"}
724
- def where_values_hash(relation_table_name = klass.table_name)
740
+ def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
725
741
  where_clause.to_h(relation_table_name)
726
742
  end
727
743
 
@@ -741,7 +757,7 @@ module ActiveRecord
741
757
  # Joins that are also marked for preloading. In which case we should just eager load them.
742
758
  # Note that this is a naive implementation because we could have strings and symbols which
743
759
  # represent the same association, but that aren't matched by this. Also, we could have
744
- # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
760
+ # nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
745
761
  def joined_includes_values
746
762
  includes_values & joins_values
747
763
  end
@@ -902,7 +918,7 @@ module ActiveRecord
902
918
  preload_associations(records) unless skip_preloading_value
903
919
 
904
920
  records.each(&:readonly!) if readonly_value
905
- records.each(&:strict_loading!) if strict_loading_value
921
+ records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
906
922
 
907
923
  records
908
924
  end
@@ -92,16 +92,17 @@ module ActiveRecord
92
92
  end
93
93
 
94
94
  # Sanitizes a +string+ so that it is safe to use within an SQL
95
- # LIKE statement. This method uses +escape_character+ to escape all occurrences of "\", "_" and "%".
95
+ # LIKE statement. This method uses +escape_character+ to escape all
96
+ # occurrences of itself, "_" and "%".
96
97
  #
97
- # sanitize_sql_like("100%")
98
- # # => "100\\%"
98
+ # sanitize_sql_like("100% true!")
99
+ # # => "100\\% true!"
99
100
  #
100
101
  # sanitize_sql_like("snake_cased_string")
101
102
  # # => "snake\\_cased\\_string"
102
103
  #
103
- # sanitize_sql_like("100%", "!")
104
- # # => "100!%"
104
+ # sanitize_sql_like("100% true!", "!")
105
+ # # => "100!% true!!"
105
106
  #
106
107
  # sanitize_sql_like("snake_cased_string", "!")
107
108
  # # => "snake!_cased!_string"
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  #
11
11
  # Usage:
12
12
  #
13
- # ActiveRecord::Schema.define do
13
+ # ActiveRecord::Schema[7.0].define do
14
14
  # create_table :authors do |t|
15
15
  # t.string :name, null: false
16
16
  # end
@@ -30,32 +30,47 @@ module ActiveRecord
30
30
  # ActiveRecord::Schema is only supported by database adapters that also
31
31
  # support migrations, the two features being very similar.
32
32
  class Schema < Migration::Current
33
- # Eval the given block. All methods available to the current connection
34
- # adapter are available within the block, so you can easily use the
35
- # database definition DSL to build up your schema (
36
- # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
37
- # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
38
- #
39
- # The +info+ hash is optional, and if given is used to define metadata
40
- # about the current schema (currently, only the schema's version):
41
- #
42
- # ActiveRecord::Schema.define(version: 2038_01_19_000001) do
43
- # ...
44
- # end
45
- def self.define(info = {}, &block)
46
- new.define(info, &block)
47
- end
33
+ module Definition
34
+ extend ActiveSupport::Concern
35
+
36
+ module ClassMethods
37
+ # Eval the given block. All methods available to the current connection
38
+ # adapter are available within the block, so you can easily use the
39
+ # database definition DSL to build up your schema (
40
+ # {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
41
+ # {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
42
+ #
43
+ # The +info+ hash is optional, and if given is used to define metadata
44
+ # about the current schema (currently, only the schema's version):
45
+ #
46
+ # ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do
47
+ # ...
48
+ # end
49
+ def define(info = {}, &block)
50
+ new.define(info, &block)
51
+ end
52
+ end
53
+
54
+ def define(info, &block) # :nodoc:
55
+ instance_eval(&block)
48
56
 
49
- def define(info, &block) # :nodoc:
50
- instance_eval(&block)
57
+ if info[:version].present?
58
+ connection.schema_migration.create_table
59
+ connection.assume_migrated_upto_version(info[:version])
60
+ end
51
61
 
52
- if info[:version].present?
53
- connection.schema_migration.create_table
54
- connection.assume_migrated_upto_version(info[:version])
62
+ ActiveRecord::InternalMetadata.create_table
63
+ ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
55
64
  end
65
+ end
66
+
67
+ include Definition
56
68
 
57
- ActiveRecord::InternalMetadata.create_table
58
- ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
69
+ def self.[](version)
70
+ @class_for_version ||= {}
71
+ @class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
72
+ include Definition
73
+ end
59
74
  end
60
75
  end
61
76
  end
@@ -74,22 +74,21 @@ module ActiveRecord
74
74
  end
75
75
 
76
76
  def header(stream)
77
- stream.puts <<HEADER
78
- # This file is auto-generated from the current state of the database. Instead
79
- # of editing this file, please use the migrations feature of Active Record to
80
- # incrementally modify your database, and then regenerate this schema definition.
81
- #
82
- # This file is the source Rails uses to define your schema when running `bin/rails
83
- # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
84
- # be faster and is potentially less error prone than running all of your
85
- # migrations from scratch. Old migrations may fail to apply correctly if those
86
- # migrations use external dependencies or application code.
87
- #
88
- # It's strongly recommended that you check this file into your version control system.
89
-
90
- ActiveRecord::Schema.define(#{define_params}) do
91
-
92
- HEADER
77
+ stream.puts <<~HEADER
78
+ # This file is auto-generated from the current state of the database. Instead
79
+ # of editing this file, please use the migrations feature of Active Record to
80
+ # incrementally modify your database, and then regenerate this schema definition.
81
+ #
82
+ # This file is the source Rails uses to define your schema when running `bin/rails
83
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
84
+ # be faster and is potentially less error prone than running all of your
85
+ # migrations from scratch. Old migrations may fail to apply correctly if those
86
+ # migrations use external dependencies or application code.
87
+ #
88
+ # It's strongly recommended that you check this file into your version control system.
89
+
90
+ ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
91
+ HEADER
93
92
  end
94
93
 
95
94
  def trailer(stream)
@@ -48,10 +48,6 @@ module ActiveRecord
48
48
  super || default_scopes.any? || respond_to?(:default_scope)
49
49
  end
50
50
 
51
- def before_remove_const # :nodoc:
52
- self.current_scope = nil
53
- end
54
-
55
51
  # Checks if the model has any default scopes. If all_queries
56
52
  # is set to true, the method will check if there are any
57
53
  # default_scopes for the model where +all_queries+ is true.
@@ -83,7 +79,7 @@ module ActiveRecord
83
79
  # <tt>all_queries: true</tt>:
84
80
  #
85
81
  # class Article < ActiveRecord::Base
86
- # default_scope { where(blog_id: 1) }, all_queries: true
82
+ # default_scope -> { where(blog_id: 1) }, all_queries: true
87
83
  # end
88
84
  #
89
85
  # Applying a default scope to all queries will ensure that records
@@ -150,11 +146,13 @@ module ActiveRecord
150
146
  end
151
147
  elsif default_scopes.any?
152
148
  evaluate_default_scope do
153
- default_scopes.inject(relation) do |default_scope, scope_obj|
149
+ default_scopes.inject(relation) do |combined_scope, scope_obj|
154
150
  if execute_scope?(all_queries, scope_obj)
155
151
  scope = scope_obj.scope.respond_to?(:to_proc) ? scope_obj.scope : scope_obj.scope.method(:call)
156
152
 
157
- default_scope.instance_exec(&scope) || default_scope
153
+ combined_scope.instance_exec(&scope) || combined_scope
154
+ else
155
+ combined_scope
158
156
  end
159
157
  end
160
158
  end
@@ -20,5 +20,10 @@ module ActiveRecord # :nodoc:
20
20
 
21
21
  super(options)
22
22
  end
23
+
24
+ private
25
+ def attribute_names_for_serialization
26
+ attribute_names
27
+ end
23
28
  end
24
29
  end
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  end
48
48
  end
49
49
 
50
- # Works like +find_signed+, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
50
+ # Works like find_signed, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
51
51
  # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
52
52
  # or has been tampered with. It will also raise an +ActiveRecord::RecordNotFound+ exception if
53
53
  # the valid signed id can't find a record.
@@ -97,7 +97,7 @@ module ActiveRecord
97
97
 
98
98
  # Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
99
99
  # This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
100
- # It can further more be set to expire (the default is not to expire), and scoped down with a specific purpose.
100
+ # It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
101
101
  # If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
102
102
  # record. If a purpose is set, this too must match.
103
103
  #
@@ -59,7 +59,7 @@ module ActiveRecord
59
59
  # u.color = 'green'
60
60
  # u.color_changed? # => true
61
61
  # u.color_was # => 'black'
62
- # u.color_change # => ['black', 'red']
62
+ # u.color_change # => ['black', 'green']
63
63
  #
64
64
  # # Add additional accessors to an existing store through store_accessor
65
65
  # class SuperUser < User
@@ -268,7 +268,7 @@ module ActiveRecord
268
268
  end
269
269
 
270
270
  def dump(obj)
271
- @coder.dump self.class.as_indifferent_hash(obj)
271
+ @coder.dump as_regular_hash(obj)
272
272
  end
273
273
 
274
274
  def load(yaml)
@@ -285,6 +285,11 @@ module ActiveRecord
285
285
  ActiveSupport::HashWithIndifferentAccess.new
286
286
  end
287
287
  end
288
+
289
+ private
290
+ def as_regular_hash(obj)
291
+ obj.respond_to?(:to_hash) ? obj.to_hash : {}
292
+ end
288
293
  end
289
294
  end
290
295
  end
@@ -188,29 +188,31 @@ module ActiveRecord
188
188
  def prepare_all
189
189
  seed = false
190
190
 
191
- configs_for(env_name: env).each do |db_config|
191
+ each_current_configuration(env) do |db_config|
192
192
  ActiveRecord::Base.establish_connection(db_config)
193
193
 
194
- # Skipped when no database
195
- migrate
196
-
197
- if ActiveRecord.dump_schema_after_migration
198
- dump_schema(db_config, ActiveRecord.schema_format)
199
- end
200
- rescue ActiveRecord::NoDatabaseError
201
- create_current(db_config.env_name, db_config.name)
202
-
203
- if File.exist?(schema_dump_path(db_config))
204
- load_schema(
205
- db_config,
206
- ActiveRecord.schema_format,
207
- nil
208
- )
209
- else
194
+ begin
195
+ # Skipped when no database
210
196
  migrate
211
- end
212
197
 
213
- seed = true
198
+ if ActiveRecord.dump_schema_after_migration
199
+ dump_schema(db_config, ActiveRecord.schema_format)
200
+ end
201
+ rescue ActiveRecord::NoDatabaseError
202
+ create(db_config)
203
+
204
+ if File.exist?(schema_dump_path(db_config))
205
+ load_schema(
206
+ db_config,
207
+ ActiveRecord.schema_format,
208
+ nil
209
+ )
210
+ else
211
+ migrate
212
+ end
213
+
214
+ seed = true
215
+ end
214
216
  end
215
217
 
216
218
  ActiveRecord::Base.establish_connection
@@ -257,8 +259,12 @@ module ActiveRecord
257
259
  scope = ENV["SCOPE"]
258
260
  verbose_was, Migration.verbose = Migration.verbose, verbose?
259
261
 
260
- Base.connection.migration_context.migrate(target_version || version) do |migration|
261
- scope.blank? || scope == migration.scope
262
+ Base.connection.migration_context.migrate(target_version) do |migration|
263
+ if version.blank?
264
+ scope.blank? || scope == migration.scope
265
+ else
266
+ migration.version == version
267
+ end
262
268
  end.tap do |migrations_ran|
263
269
  Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
264
270
  end
@@ -360,6 +366,7 @@ module ActiveRecord
360
366
 
361
367
  def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
362
368
  file ||= schema_dump_path(db_config, format)
369
+ return unless file
363
370
 
364
371
  verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
365
372
  check_schema_file(file)
@@ -385,7 +392,7 @@ module ActiveRecord
385
392
 
386
393
  file ||= schema_dump_path(db_config)
387
394
 
388
- return true unless File.exist?(file)
395
+ return true unless file && File.exist?(file)
389
396
 
390
397
  ActiveRecord::Base.establish_connection(db_config)
391
398
 
@@ -398,7 +405,7 @@ module ActiveRecord
398
405
  def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
399
406
  file ||= schema_dump_path(db_config, format)
400
407
 
401
- check_schema_file(file)
408
+ check_schema_file(file) if file
402
409
 
403
410
  ActiveRecord::Base.establish_connection(db_config)
404
411
 
@@ -416,6 +423,8 @@ module ActiveRecord
416
423
  def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
417
424
  require "active_record/schema_dumper"
418
425
  filename = schema_dump_path(db_config, format)
426
+ return unless filename
427
+
419
428
  connection = ActiveRecord::Base.connection
420
429
 
421
430
  FileUtils.mkdir_p(db_dir)
@@ -58,7 +58,6 @@ module ActiveRecord
58
58
  end
59
59
 
60
60
  args = ["--schema-only", "--no-privileges", "--no-owner"]
61
- args << "--no-comment" if connection.database_version >= 110_000
62
61
  args.concat(["--file", filename])
63
62
 
64
63
  args.concat(Array(extra_flags)) if extra_flags
@@ -81,7 +80,7 @@ module ActiveRecord
81
80
  end
82
81
 
83
82
  def structure_load(filename, extra_flags)
84
- args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--file", filename]
83
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
85
84
  args.concat(Array(extra_flags)) if extra_flags
86
85
  args << db_config.database
87
86
  run_cmd("psql", args, "loading")
@@ -86,6 +86,9 @@ module ActiveRecord
86
86
  include methods
87
87
  end
88
88
 
89
+ # Prevents automatically wrapping each specified test in a transaction,
90
+ # to allow application logic transactions to be tested in a top-level
91
+ # (non-nested) context.
89
92
  def uses_transaction(*methods)
90
93
  @uses_transaction = [] unless defined?(@uses_transaction)
91
94
  @uses_transaction.concat methods.map(&:to_s)
@@ -134,7 +137,7 @@ module ActiveRecord
134
137
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
135
138
  spec_name = payload[:spec_name] if payload.key?(:spec_name)
136
139
  shard = payload[:shard] if payload.key?(:shard)
137
- setup_shared_connection_pool
140
+ setup_shared_connection_pool if ActiveRecord.legacy_connection_handling
138
141
 
139
142
  if spec_name
140
143
  begin
@@ -143,10 +146,14 @@ module ActiveRecord
143
146
  connection = nil
144
147
  end
145
148
 
146
- if connection && !@fixture_connections.include?(connection)
147
- connection.begin_transaction joinable: false, _lazy: false
148
- connection.pool.lock_thread = true if lock_threads
149
- @fixture_connections << connection
149
+ if connection
150
+ setup_shared_connection_pool unless ActiveRecord.legacy_connection_handling
151
+
152
+ if !@fixture_connections.include?(connection)
153
+ connection.begin_transaction joinable: false, _lazy: false
154
+ connection.pool.lock_thread = true if lock_threads
155
+ @fixture_connections << connection
156
+ end
150
157
  end
151
158
  end
152
159
  end
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  classes
17
17
  end
18
18
 
19
- # Set the i18n scope to overwrite ActiveModel.
19
+ # Set the i18n scope to override ActiveModel.
20
20
  def i18n_scope # :nodoc:
21
21
  :activerecord
22
22
  end
@@ -42,14 +42,14 @@ module ActiveRecord
42
42
  # or an array of symbols. (e.g. <tt>on: :create</tt> or
43
43
  # <tt>on: :custom_validation_context</tt> or
44
44
  # <tt>on: [:create, :custom_validation_context]</tt>)
45
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
45
+ # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
46
46
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
47
47
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
48
48
  # proc or string should return or evaluate to a +true+ or +false+ value.
49
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
49
+ # * <tt>:unless</tt> - Specifies a method, proc, or string to call to
50
50
  # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
51
51
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
52
- # method, proc or string should return or evaluate to a +true+ or +false+
52
+ # method, proc, or string should return or evaluate to a +true+ or +false+
53
53
  # value.
54
54
  def validates_associated(*attr_names)
55
55
  validates_with AssociatedValidator, _merge_attributes(attr_names)
@@ -50,11 +50,11 @@ module ActiveRecord
50
50
  # or an array of symbols. (e.g. <tt>on: :create</tt> or
51
51
  # <tt>on: :custom_validation_context</tt> or
52
52
  # <tt>on: [:create, :custom_validation_context]</tt>)
53
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
53
+ # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine if
54
54
  # the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
55
55
  # <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
56
56
  # or string should return or evaluate to a +true+ or +false+ value.
57
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
57
+ # * <tt>:unless</tt> - Specifies a method, proc, or string to call to determine
58
58
  # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
59
59
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
60
60
  # proc or string should return or evaluate to a +true+ or +false+ value.
@@ -166,14 +166,14 @@ module ActiveRecord
166
166
  # attribute is +nil+ (default is +false+).
167
167
  # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
168
168
  # attribute is blank (default is +false+).
169
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
169
+ # * <tt>:if</tt> - Specifies a method, proc, or string to call to determine
170
170
  # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
171
171
  # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
172
172
  # proc or string should return or evaluate to a +true+ or +false+ value.
173
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
173
+ # * <tt>:unless</tt> - Specifies a method, proc, or string to call to
174
174
  # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
175
175
  # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
176
- # method, proc or string should return or evaluate to a +true+ or +false+
176
+ # method, proc, or string should return or evaluate to a +true+ or +false+
177
177
  # value.
178
178
  #
179
179
  # === Concurrency and integrity