activerecord 7.0.6 → 7.0.8.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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -0
  3. data/lib/active_record/associations/collection_association.rb +1 -1
  4. data/lib/active_record/associations/collection_proxy.rb +5 -0
  5. data/lib/active_record/associations/has_one_association.rb +0 -4
  6. data/lib/active_record/associations/singular_association.rb +5 -7
  7. data/lib/active_record/autosave_association.rb +5 -9
  8. data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -17
  9. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -6
  10. data/lib/active_record/connection_adapters/mysql/database_statements.rb +1 -1
  11. data/lib/active_record/connection_adapters/mysql/quoting.rb +5 -2
  12. data/lib/active_record/connection_adapters/postgresql/quoting.rb +5 -2
  13. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -2
  14. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
  15. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  16. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -4
  17. data/lib/active_record/gem_version.rb +2 -2
  18. data/lib/active_record/migration/compatibility.rb +12 -54
  19. data/lib/active_record/migration.rb +52 -3
  20. data/lib/active_record/persistence.rb +3 -1
  21. data/lib/active_record/railties/controller_runtime.rb +3 -4
  22. data/lib/active_record/relation/predicate_builder.rb +1 -2
  23. data/lib/active_record/relation/query_attribute.rb +23 -0
  24. data/lib/active_record/relation/query_methods.rb +26 -7
  25. data/lib/active_record/table_metadata.rb +1 -1
  26. data/lib/active_record/transactions.rb +3 -3
  27. data/lib/active_record/type/serialized.rb +4 -0
  28. data/lib/arel/nodes/and.rb +4 -0
  29. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fc8d9f64d2ef73d31d77f9bb3ab1792606a33e541bd28a08566304fc81d5d07
4
- data.tar.gz: 7e4b61e90c14d831d3b888859ba713dd5cbb35d1554225aec1c483ee8857aee9
3
+ metadata.gz: e000d217346c1019c2f482509ab635db50acfd196f69616c86fff4146e25a555
4
+ data.tar.gz: f8c7e6d69f291a96633a58d71862cee5f7f032b58475909ee446ebad1ac40963
5
5
  SHA512:
6
- metadata.gz: faf9806425e9a75ecde61e1b84bc6eec459cd18895684200701394315b5e7bff69487a4baab67482a80baa1f5e2fcf81021781ceb81cba39f7fbc75bfe7200d4
7
- data.tar.gz: b940c37c5f10f000f37ba68eb9be3aa2ae5dabc329a16979587dd1cd75ba464976cefccc437ba9958eb444a35f9303214d7e5f0d06d37b4e45809337977d8829
6
+ metadata.gz: da9bec970adfbb45cf2cc7e63726775d33bf49ee63b852414db9a777800faf95259b1589fb7512e0401be63e4e653caf65df37af64cec72636184a6394134eb5
7
+ data.tar.gz: e99c01e13ce496d927e6c28211f3780e97c5706bc735bb1371356c55ba9c695a7509b1613d45c127a2fbabdc96cf966168ba6f7155b7679f37566cf852cc74ca
data/CHANGELOG.md CHANGED
@@ -1,3 +1,148 @@
1
+ ## Rails 7.0.8.4 (June 04, 2024) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.0.8.3 (May 17, 2024) ##
7
+
8
+ * No changes.
9
+
10
+
11
+ ## Rails 7.0.8.2 (May 16, 2024) ##
12
+
13
+ * No changes.
14
+
15
+
16
+ ## Rails 7.0.8.1 (February 21, 2024) ##
17
+
18
+ * No changes.
19
+
20
+
21
+ ## Rails 7.0.8 (September 09, 2023) ##
22
+
23
+ * Fix `change_column` not setting `precision: 6` on `datetime` columns when
24
+ using 7.0+ Migrations and SQLite.
25
+
26
+ *Hartley McGuire*
27
+
28
+ * Fix unscope is not working in specific case
29
+
30
+ Before:
31
+ ```ruby
32
+ Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts` WHERE `posts`.`id` >= 1 AND `posts`.`id` < 3"
33
+
34
+ ```
35
+
36
+ After:
37
+ ```ruby
38
+ Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts`"
39
+ ```
40
+
41
+ Fixes #48094.
42
+
43
+ *Kazuya Hatanaka*
44
+
45
+ * Fix associations to a STI model including a `class_name` parameter
46
+
47
+ ```ruby
48
+ class Product < ApplicationRecord
49
+ has_many :requests, as: :requestable, class_name: "ProductRequest", dependent: :destroy
50
+ end
51
+
52
+ # STI tables
53
+ class Request < ApplicationRecord
54
+ belongs_to :requestable, polymorphic: true
55
+
56
+ validate :request_type, presence: true
57
+ end
58
+
59
+ class ProductRequest < Request
60
+ belongs_to :user
61
+ end
62
+ ```
63
+
64
+ Accessing such association would lead to:
65
+
66
+ ```
67
+ table_metadata.rb:22:in `has_column?': undefined method `key?' for nil:NilClass (NoMethodError)
68
+ ```
69
+
70
+ *Romain Filinto*
71
+
72
+ * Fix `change_table` setting datetime precision for 6.1 Migrations
73
+
74
+ *Hartley McGuire*
75
+
76
+ * Fix change_column setting datetime precision for 6.1 Migrations
77
+
78
+ *Hartley McGuire*
79
+
80
+ ## Rails 7.0.7.2 (August 22, 2023) ##
81
+
82
+ * No changes.
83
+
84
+
85
+ ## Rails 7.0.7.1 (August 22, 2023) ##
86
+
87
+ * No changes.
88
+
89
+
90
+ ## Rails 7.0.7 (August 09, 2023) ##
91
+
92
+ * Restores functionality to the missing method when using enums and fixes.
93
+
94
+ *paulreece*
95
+
96
+ * Fix `StatementCache::Substitute` with serialized type.
97
+
98
+ *ywenc*
99
+
100
+ * Fix `:db_runtime` on notification payload when application have multiple databases.
101
+
102
+ *Eileen M. Uchitelle*
103
+
104
+ * Correctly dump check constraints for MySQL 8.0.16+.
105
+
106
+ *Steve Hill*
107
+
108
+ * Fix `ActiveRecord::QueryMethods#in_order_of` to include `nil`s, to match the
109
+ behavior of `Enumerable#in_order_of`.
110
+
111
+ For example, `Post.in_order_of(:title, [nil, "foo"])` will now include posts
112
+ with `nil` titles, the same as `Post.all.to_a.in_order_of(:title, [nil, "foo"])`.
113
+
114
+ *fatkodima*
115
+
116
+ * Revert "Fix autosave associations with validations added on `:base` of the associated objects."
117
+
118
+ This change intended to remove the :base attribute from the message,
119
+ but broke many assumptions which key these errors were stored.
120
+
121
+ *zzak*
122
+
123
+ * Fix `#previously_new_record?` to return true for destroyed records.
124
+
125
+ Before, if a record was created and then destroyed, `#previously_new_record?` would return true.
126
+ Now, any UPDATE or DELETE to a record is considered a change, and will result in `#previously_new_record?`
127
+ returning false.
128
+
129
+ *Adrianna Chang*
130
+
131
+ * Revert breaking changes to `has_one` relationship deleting the old record before the new one is validated.
132
+
133
+ *zzak*
134
+
135
+ * Fix support for Active Record instances being uses in queries.
136
+
137
+ As of `7.0.5`, query arguments were deep duped to avoid mutations impacting
138
+ the query cache, but this had the adverse effect to clearing the primary key when
139
+ the query argument contained an `ActiveRecord::Base` instance.
140
+
141
+ This broke the `noticed` gem.
142
+
143
+ *Jean Boussier*
144
+
145
+
1
146
  ## Rails 7.0.6 (June 29, 2023) ##
2
147
 
3
148
  * Fix autosave associations with validations added on `:base` of the associated objects.
@@ -12,6 +157,10 @@
12
157
 
13
158
  *fatkodima*
14
159
 
160
+ * Fix assignment into an `has_one` relationship deleting the old record before the new one is validated.
161
+
162
+ *Jean Boussier*
163
+
15
164
  * Fix where on association with has_one/has_many polymorphic relations.
16
165
 
17
166
  Before:
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
  def reset
80
80
  super
81
81
  @target = []
82
- @replaced_or_added_targets = Set.new
82
+ @replaced_or_added_targets = Set.new.compare_by_identity
83
83
  @association_ids = nil
84
84
  end
85
85
 
@@ -1102,6 +1102,11 @@ module ActiveRecord
1102
1102
  super
1103
1103
  end
1104
1104
 
1105
+ def pretty_print(pp) # :nodoc:
1106
+ load_target if find_from_target?
1107
+ super
1108
+ end
1109
+
1105
1110
  delegate_methods = [
1106
1111
  QueryMethods,
1107
1112
  SpawnMethods,
@@ -87,10 +87,6 @@ module ActiveRecord
87
87
  replace(record, false)
88
88
  end
89
89
 
90
- def replace_keys(record, force: false)
91
- # Has one association doesn't have foreign keys to replace.
92
- end
93
-
94
90
  def remove_target!(method)
95
91
  case method
96
92
  when :delete
@@ -54,13 +54,11 @@ module ActiveRecord
54
54
  end
55
55
 
56
56
  def _create_record(attributes, raise_error = false, &block)
57
- reflection.klass.transaction do
58
- record = build(attributes, &block)
59
- saved = record.save
60
- replace_keys(record, force: true)
61
- raise RecordInvalid.new(record) if !saved && raise_error
62
- record
63
- end
57
+ record = build_record(attributes, &block)
58
+ saved = record.save
59
+ set_new_record(record)
60
+ raise RecordInvalid.new(record) if !saved && raise_error
61
+ record
64
62
  end
65
63
  end
66
64
  end
@@ -354,15 +354,11 @@ module ActiveRecord
354
354
  end
355
355
 
356
356
  def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
357
- normalized_attribute =
358
- if indexed_attribute
359
- "#{reflection.name}[#{index}]"
360
- else
361
- reflection.name
362
- end
363
-
364
- normalized_attribute = "#{normalized_attribute}.#{attribute}" if attribute != :base
365
- normalized_attribute
357
+ if indexed_attribute
358
+ "#{reflection.name}[#{index}].#{attribute}"
359
+ else
360
+ "#{reflection.name}.#{attribute}"
361
+ end
366
362
  end
367
363
 
368
364
  # Is used as an around_save callback to check while saving a collection
@@ -71,14 +71,6 @@ module ActiveRecord
71
71
  /\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
72
72
  end
73
73
 
74
- def self.quoted_column_names # :nodoc:
75
- @quoted_column_names ||= {}
76
- end
77
-
78
- def self.quoted_table_names # :nodoc:
79
- @quoted_table_names ||= {}
80
- end
81
-
82
74
  def initialize(connection, logger = nil, config = {}) # :nodoc:
83
75
  super()
84
76
 
@@ -665,15 +657,6 @@ module ActiveRecord
665
657
  migration_context.current_version
666
658
  end
667
659
 
668
- def field_ordered_value(column, values) # :nodoc:
669
- node = Arel::Nodes::Case.new(column)
670
- values.each.with_index(1) do |value, order|
671
- node.when(value).then(order)
672
- end
673
-
674
- Arel::Nodes::Ascending.new(node.else(values.length + 1))
675
- end
676
-
677
660
  class << self
678
661
  private
679
662
  def initialize_type_map(m)
@@ -138,11 +138,6 @@ module ActiveRecord
138
138
  true
139
139
  end
140
140
 
141
- def field_ordered_value(column, values) # :nodoc:
142
- field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse.map { |value| Arel::Nodes.build_quoted(value) }])
143
- Arel::Nodes::Descending.new(field)
144
- end
145
-
146
141
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
147
142
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
148
143
  end
@@ -438,7 +433,8 @@ module ActiveRecord
438
433
  name: row["name"]
439
434
  }
440
435
  expression = row["expression"]
441
- expression = expression[1..-2] unless mariadb? # remove parentheses added by mysql
436
+ expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
437
+ expression = strip_whitespace_characters(expression)
442
438
  CheckConstraintDefinition.new(table_name, expression, options)
443
439
  end
444
440
  else
@@ -619,6 +615,12 @@ module ActiveRecord
619
615
  end
620
616
 
621
617
  private
618
+ def strip_whitespace_characters(expression)
619
+ expression = expression.gsub(/\\n|\\\\/, "")
620
+ expression = expression.gsub(/\s{2,}/, " ")
621
+ expression
622
+ end
623
+
622
624
  def text_type?(type)
623
625
  TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
624
626
  end
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  end
21
21
 
22
22
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
23
- :desc, :describe, :set, :show, :use
23
+ :desc, :describe, :set, :show, :use, :kill
24
24
  ) # :nodoc:
25
25
  private_constant :READ_QUERY
26
26
 
@@ -6,6 +6,9 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module MySQL
8
8
  module Quoting # :nodoc:
9
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
10
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
11
+
9
12
  def quote_bound_value(value)
10
13
  case value
11
14
  when Rational
@@ -24,11 +27,11 @@ module ActiveRecord
24
27
  end
25
28
 
26
29
  def quote_column_name(name)
27
- self.class.quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
30
+ QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
28
31
  end
29
32
 
30
33
  def quote_table_name(name)
31
- self.class.quoted_table_names[name] ||= super.gsub(".", "`.`").freeze
34
+ QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
32
35
  end
33
36
 
34
37
  def unquoted_true
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module PostgreSQL
6
6
  module Quoting
7
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
9
+
7
10
  class IntegerOutOf64BitRange < StandardError
8
11
  def initialize(msg)
9
12
  super(msg)
@@ -81,7 +84,7 @@ module ActiveRecord
81
84
  # - "schema.name".table_name
82
85
  # - "schema.name"."table.name"
83
86
  def quote_table_name(name) # :nodoc:
84
- self.class.quoted_table_names[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
87
+ QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
85
88
  end
86
89
 
87
90
  # Quotes schema names for use in SQL queries.
@@ -95,7 +98,7 @@ module ActiveRecord
95
98
 
96
99
  # Quotes column names for use in SQL queries.
97
100
  def quote_column_name(name) # :nodoc:
98
- self.class.quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
101
+ QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(super).freeze
99
102
  end
100
103
 
101
104
  # Quote date/time values for use in SQL input.
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  module Quoting # :nodoc:
7
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
8
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
9
+
7
10
  def quote_string(s)
8
11
  @connection.class.quote(s)
9
12
  end
@@ -13,11 +16,11 @@ module ActiveRecord
13
16
  end
14
17
 
15
18
  def quote_table_name(name)
16
- self.class.quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
19
+ QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "\".\"").freeze
17
20
  end
18
21
 
19
22
  def quote_column_name(name)
20
- self.class.quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
23
+ QUOTED_COLUMN_NAMES[name] ||= %Q("#{super.gsub('"', '""')}")
21
24
  end
22
25
 
23
26
  def quoted_time(value)
@@ -4,6 +4,12 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLite3
6
6
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
7
+ def change_column(column_name, type, **options)
8
+ name = column_name.to_s
9
+ @columns_hash[name] = nil
10
+ column(name, type, **options)
11
+ end
12
+
7
13
  def references(*args, **options)
8
14
  super(*args, type: :integer, **options)
9
15
  end
@@ -84,11 +84,11 @@ module ActiveRecord
84
84
  table_sql = query_value(<<-SQL, "SCHEMA")
85
85
  SELECT sql
86
86
  FROM sqlite_master
87
- WHERE name = #{quote_table_name(table_name)} AND type = 'table'
87
+ WHERE name = #{quote(table_name)} AND type = 'table'
88
88
  UNION ALL
89
89
  SELECT sql
90
90
  FROM sqlite_temp_master
91
- WHERE name = #{quote_table_name(table_name)} AND type = 'table'
91
+ WHERE name = #{quote(table_name)} AND type = 'table'
92
92
  SQL
93
93
 
94
94
  table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
@@ -287,10 +287,7 @@ module ActiveRecord
287
287
 
288
288
  def change_column(table_name, column_name, type, **options) # :nodoc:
289
289
  alter_table(table_name) do |definition|
290
- definition[column_name].instance_eval do
291
- self.type = aliased_types(type.to_s, type)
292
- self.options.merge!(options)
293
- end
290
+ definition.change_column(column_name, type, **options)
294
291
  end
295
292
  end
296
293
 
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 0
12
- TINY = 6
13
- PRE = nil
12
+ TINY = 8
13
+ PRE = "4"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -56,12 +56,13 @@ module ActiveRecord
56
56
  super
57
57
  end
58
58
 
59
- def create_table(table_name, **options)
60
- if block_given?
61
- super { |t| yield compatible_table_definition(t) }
62
- else
63
- super
59
+ def change_column(table_name, column_name, type, **options)
60
+ if type == :datetime
61
+ options[:precision] ||= nil
64
62
  end
63
+
64
+ type = PostgreSQLCompat.compatible_timestamp_type(type, connection)
65
+ super
65
66
  end
66
67
 
67
68
  module TableDefinition
@@ -70,6 +71,11 @@ module ActiveRecord
70
71
  super
71
72
  end
72
73
 
74
+ def change(name, type, index: nil, **options)
75
+ options[:precision] ||= nil
76
+ super
77
+ end
78
+
73
79
  def column(name, type, index: nil, **options)
74
80
  options[:precision] ||= nil
75
81
  super
@@ -81,7 +87,7 @@ module ActiveRecord
81
87
  class << t
82
88
  prepend TableDefinition
83
89
  end
84
- t
90
+ super
85
91
  end
86
92
  end
87
93
 
@@ -105,30 +111,6 @@ module ActiveRecord
105
111
  end
106
112
  end
107
113
 
108
- def create_table(table_name, **options)
109
- if block_given?
110
- super { |t| yield compatible_table_definition(t) }
111
- else
112
- super
113
- end
114
- end
115
-
116
- def change_table(table_name, **options)
117
- if block_given?
118
- super { |t| yield compatible_table_definition(t) }
119
- else
120
- super
121
- end
122
- end
123
-
124
- def create_join_table(table_1, table_2, **options)
125
- if block_given?
126
- super { |t| yield compatible_table_definition(t) }
127
- else
128
- super
129
- end
130
- end
131
-
132
114
  def add_reference(table_name, ref_name, **options)
133
115
  if connection.adapter_name == "SQLite"
134
116
  options[:type] = :integer
@@ -182,30 +164,6 @@ module ActiveRecord
182
164
  end
183
165
  end
184
166
 
185
- def create_table(table_name, **options)
186
- if block_given?
187
- super { |t| yield compatible_table_definition(t) }
188
- else
189
- super
190
- end
191
- end
192
-
193
- def change_table(table_name, **options)
194
- if block_given?
195
- super { |t| yield compatible_table_definition(t) }
196
- else
197
- super
198
- end
199
- end
200
-
201
- def create_join_table(table_1, table_2, **options)
202
- if block_given?
203
- super { |t| yield compatible_table_definition(t) }
204
- else
205
- super
206
- end
207
- end
208
-
209
167
  def add_timestamps(table_name, **options)
210
168
  options[:precision] ||= nil
211
169
  super
@@ -551,6 +551,41 @@ module ActiveRecord
551
551
 
552
552
  # This must be defined before the inherited hook, below
553
553
  class Current < Migration # :nodoc:
554
+ def create_table(table_name, **options)
555
+ if block_given?
556
+ super { |t| yield compatible_table_definition(t) }
557
+ else
558
+ super
559
+ end
560
+ end
561
+
562
+ def change_table(table_name, **options)
563
+ if block_given?
564
+ super { |t| yield compatible_table_definition(t) }
565
+ else
566
+ super
567
+ end
568
+ end
569
+
570
+ def create_join_table(table_1, table_2, **options)
571
+ if block_given?
572
+ super { |t| yield compatible_table_definition(t) }
573
+ else
574
+ super
575
+ end
576
+ end
577
+
578
+ def drop_table(table_name, **options)
579
+ if block_given?
580
+ super { |t| yield compatible_table_definition(t) }
581
+ else
582
+ super
583
+ end
584
+ end
585
+
586
+ def compatible_table_definition(t)
587
+ t
588
+ end
554
589
  end
555
590
 
556
591
  def self.inherited(subclass) # :nodoc:
@@ -916,9 +951,7 @@ module ActiveRecord
916
951
  end
917
952
 
918
953
  def method_missing(method, *arguments, &block)
919
- arg_list = arguments.map(&:inspect) * ", "
920
-
921
- say_with_time "#{method}(#{arg_list})" do
954
+ say_with_time "#{method}(#{format_arguments(arguments)})" do
922
955
  unless connection.respond_to? :revert
923
956
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
924
957
  arguments[0] = proper_table_name(arguments.first, table_name_options)
@@ -1026,6 +1059,22 @@ module ActiveRecord
1026
1059
  end
1027
1060
  end
1028
1061
 
1062
+ def format_arguments(arguments)
1063
+ arg_list = arguments[0...-1].map(&:inspect)
1064
+ last_arg = arguments.last
1065
+ if last_arg.is_a?(Hash)
1066
+ last_arg = last_arg.reject { |k, _v| internal_option?(k) }
1067
+ arg_list << last_arg.inspect unless last_arg.empty?
1068
+ else
1069
+ arg_list << last_arg.inspect
1070
+ end
1071
+ arg_list.join(", ")
1072
+ end
1073
+
1074
+ def internal_option?(option_name)
1075
+ option_name.start_with?("_")
1076
+ end
1077
+
1029
1078
  def command_recorder
1030
1079
  CommandRecorder.new(connection)
1031
1080
  end
@@ -564,7 +564,7 @@ module ActiveRecord
564
564
  end
565
565
 
566
566
  # Returns true if this object was just created -- that is, prior to the last
567
- # save, the object didn't exist in the database and new_record? would have
567
+ # update or delete, the object didn't exist in the database and new_record? would have
568
568
  # returned true.
569
569
  def previously_new_record?
570
570
  @previously_new_record
@@ -663,6 +663,7 @@ module ActiveRecord
663
663
  def delete
664
664
  _delete_row if persisted?
665
665
  @destroyed = true
666
+ @previously_new_record = false
666
667
  freeze
667
668
  end
668
669
 
@@ -682,6 +683,7 @@ module ActiveRecord
682
683
  true
683
684
  end
684
685
  @destroyed = true
686
+ @previously_new_record = false
685
687
  freeze
686
688
  end
687
689
 
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  end
29
29
 
30
30
  def cleanup_view_runtime
31
- if logger && logger.info? && ActiveRecord::Base.connected?
31
+ if logger && logger.info?
32
32
  db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
33
33
  self.db_runtime = (db_runtime || 0) + db_rt_before_render
34
34
  runtime = super
@@ -42,9 +42,8 @@ module ActiveRecord
42
42
 
43
43
  def append_info_to_payload(payload)
44
44
  super
45
- if ActiveRecord::Base.connected?
46
- payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
47
- end
45
+
46
+ payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
48
47
  end
49
48
  end
50
49
  end
@@ -65,8 +65,7 @@ module ActiveRecord
65
65
  end
66
66
 
67
67
  def build_bind_attribute(column_name, value)
68
- type = table.type(column_name)
69
- Relation::QueryAttribute.new(column_name, type.immutable_value(value), type)
68
+ Relation::QueryAttribute.new(column_name, value, table.type(column_name))
70
69
  end
71
70
 
72
71
  def resolve_arel_attribute(table_name, column_name, &block)
@@ -5,6 +5,20 @@ require "active_model/attribute"
5
5
  module ActiveRecord
6
6
  class Relation
7
7
  class QueryAttribute < ActiveModel::Attribute # :nodoc:
8
+ def initialize(...)
9
+ super
10
+
11
+ # The query attribute value may be mutated before we actually "compile" the query.
12
+ # To avoid that if the type uses a serializer we eagerly compute the value for database
13
+ if value_before_type_cast.is_a?(StatementCache::Substitute)
14
+ # we don't need to serialize StatementCache::Substitute
15
+ elsif @type.serialized?
16
+ value_for_database
17
+ elsif @type.mutable? # If the type is simply mutable, we deep_dup it.
18
+ @value_before_type_cast = @value_before_type_cast.deep_dup
19
+ end
20
+ end
21
+
8
22
  def type_cast(value)
9
23
  value
10
24
  end
@@ -35,6 +49,15 @@ module ActiveRecord
35
49
  @_unboundable
36
50
  end
37
51
 
52
+ def ==(other)
53
+ super && value_for_database == other.value_for_database
54
+ end
55
+ alias eql? ==
56
+
57
+ def hash
58
+ [self.class, name, value_for_database, type].hash
59
+ end
60
+
38
61
  private
39
62
  def infinity?(value)
40
63
  value.respond_to?(:infinite?) && value.infinite?
@@ -77,7 +77,7 @@ module ActiveRecord
77
77
  associations.each do |association|
78
78
  reflection = scope_association_reflection(association)
79
79
  @scope.joins!(association)
80
- if @scope.table_name == reflection.table_name
80
+ if reflection.options[:class_name]
81
81
  self.not(association => { reflection.association_primary_key => nil })
82
82
  else
83
83
  self.not(reflection.table_name => { reflection.association_primary_key => nil })
@@ -109,7 +109,7 @@ module ActiveRecord
109
109
  associations.each do |association|
110
110
  reflection = scope_association_reflection(association)
111
111
  @scope.left_outer_joins!(association)
112
- if @scope.table_name == reflection.table_name
112
+ if reflection.options[:class_name]
113
113
  @scope.where!(association => { reflection.association_primary_key => nil })
114
114
  else
115
115
  @scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
@@ -444,13 +444,16 @@ module ActiveRecord
444
444
  self
445
445
  end
446
446
 
447
- # Allows to specify an order by a specific set of values. Depending on your
448
- # adapter this will either use a CASE statement or a built-in function.
447
+ # Allows to specify an order by a specific set of values.
449
448
  #
450
449
  # User.in_order_of(:id, [1, 5, 3])
451
450
  # # SELECT "users".* FROM "users"
452
- # # ORDER BY FIELD("users"."id", 1, 5, 3)
453
451
  # # WHERE "users"."id" IN (1, 5, 3)
452
+ # # ORDER BY CASE
453
+ # # WHEN "users"."id" = 1 THEN 1
454
+ # # WHEN "users"."id" = 5 THEN 2
455
+ # # WHEN "users"."id" = 3 THEN 3
456
+ # # END ASC
454
457
  #
455
458
  def in_order_of(column, values)
456
459
  klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
@@ -462,9 +465,16 @@ module ActiveRecord
462
465
  values = values.map { |value| type_caster.type_cast_for_database(column, value) }
463
466
  arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
464
467
 
468
+ where_clause =
469
+ if values.include?(nil)
470
+ arel_column.in(values.compact).or(arel_column.eq(nil))
471
+ else
472
+ arel_column.in(values)
473
+ end
474
+
465
475
  spawn
466
- .order!(connection.field_ordered_value(arel_column, values))
467
- .where!(arel_column.in(values))
476
+ .order!(build_case_for_value_position(arel_column, values))
477
+ .where!(where_clause)
468
478
  end
469
479
 
470
480
  # Replaces any existing order defined on the relation with the specified order.
@@ -1669,6 +1679,15 @@ module ActiveRecord
1669
1679
  end
1670
1680
  end
1671
1681
 
1682
+ def build_case_for_value_position(column, values)
1683
+ node = Arel::Nodes::Case.new
1684
+ values.each.with_index(1) do |value, order|
1685
+ node.when(column.eq(value)).then(order)
1686
+ end
1687
+
1688
+ Arel::Nodes::Ascending.new(node)
1689
+ end
1690
+
1672
1691
  def resolve_arel_attributes(attrs)
1673
1692
  attrs.flat_map do |attr|
1674
1693
  case attr
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def has_column?(column_name)
22
- klass&.columns_hash.key?(column_name)
22
+ klass&.columns_hash&.key?(column_name)
23
23
  end
24
24
 
25
25
  def associated_with?(table_name)
@@ -336,9 +336,9 @@ module ActiveRecord
336
336
  @_trigger_update_callback = @_trigger_destroy_callback = false if force_restore_state
337
337
  end
338
338
 
339
- # Executes +method+ within a transaction and captures its return value as a
340
- # status flag. If the status is true the transaction is committed, otherwise
341
- # a ROLLBACK is issued. In any case the status flag is returned.
339
+ # Executes a block within a transaction and captures its return value as a
340
+ # status flag. If the status is true, the transaction is committed,
341
+ # otherwise a ROLLBACK is issued. In any case, the status flag is returned.
342
342
  #
343
343
  # This method is available within the context of an ActiveRecord::Base
344
344
  # instance.
@@ -55,6 +55,10 @@ module ActiveRecord
55
55
  coder.respond_to?(:object_class) && value.is_a?(coder.object_class)
56
56
  end
57
57
 
58
+ def serialized? # :nodoc:
59
+ true
60
+ end
61
+
58
62
  private
59
63
  def default_value?(value)
60
64
  value == coder.load(nil)
@@ -18,6 +18,10 @@ module Arel # :nodoc: all
18
18
  children[1]
19
19
  end
20
20
 
21
+ def fetch_attribute(&block)
22
+ children.any? && children.all? { |child| child.fetch_attribute(&block) }
23
+ end
24
+
21
25
  def hash
22
26
  children.hash
23
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.6
4
+ version: 7.0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-29 00:00:00.000000000 Z
11
+ date: 2024-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.0.6
19
+ version: 7.0.8.4
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.0.6
26
+ version: 7.0.8.4
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 7.0.6
33
+ version: 7.0.8.4
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 7.0.6
40
+ version: 7.0.8.4
41
41
  description: Databases on Rails. Build a persistent domain model by mapping database
42
42
  tables to Ruby classes. Strong conventions for associations, validations, aggregations,
43
43
  migrations, and testing come baked-in.
@@ -434,12 +434,12 @@ licenses:
434
434
  - MIT
435
435
  metadata:
436
436
  bug_tracker_uri: https://github.com/rails/rails/issues
437
- changelog_uri: https://github.com/rails/rails/blob/v7.0.6/activerecord/CHANGELOG.md
438
- documentation_uri: https://api.rubyonrails.org/v7.0.6/
437
+ changelog_uri: https://github.com/rails/rails/blob/v7.0.8.4/activerecord/CHANGELOG.md
438
+ documentation_uri: https://api.rubyonrails.org/v7.0.8.4/
439
439
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
440
- source_code_uri: https://github.com/rails/rails/tree/v7.0.6/activerecord
440
+ source_code_uri: https://github.com/rails/rails/tree/v7.0.8.4/activerecord
441
441
  rubygems_mfa_required: 'true'
442
- post_install_message:
442
+ post_install_message:
443
443
  rdoc_options:
444
444
  - "--main"
445
445
  - README.rdoc
@@ -456,8 +456,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
456
456
  - !ruby/object:Gem::Version
457
457
  version: '0'
458
458
  requirements: []
459
- rubygems_version: 3.4.13
460
- signing_key:
459
+ rubygems_version: 3.3.27
460
+ signing_key:
461
461
  specification_version: 4
462
462
  summary: Object-relational mapper framework (part of Rails).
463
463
  test_files: []