activerecord 6.0.0.rc1 → 6.0.3.rc1

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.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +251 -3
  3. data/README.rdoc +1 -1
  4. data/lib/active_record.rb +1 -0
  5. data/lib/active_record/advisory_lock_base.rb +18 -0
  6. data/lib/active_record/aggregations.rb +0 -1
  7. data/lib/active_record/association_relation.rb +10 -8
  8. data/lib/active_record/associations.rb +2 -2
  9. data/lib/active_record/associations/alias_tracker.rb +0 -1
  10. data/lib/active_record/associations/association.rb +5 -1
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -3
  13. data/lib/active_record/associations/collection_association.rb +6 -2
  14. data/lib/active_record/associations/collection_proxy.rb +2 -3
  15. data/lib/active_record/associations/has_many_association.rb +0 -1
  16. data/lib/active_record/associations/join_dependency.rb +23 -9
  17. data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
  18. data/lib/active_record/associations/preloader.rb +2 -3
  19. data/lib/active_record/associations/preloader/association.rb +3 -1
  20. data/lib/active_record/attribute_assignment.rb +0 -1
  21. data/lib/active_record/attribute_decorators.rb +0 -2
  22. data/lib/active_record/attribute_methods.rb +0 -51
  23. data/lib/active_record/attribute_methods/before_type_cast.rb +0 -1
  24. data/lib/active_record/attribute_methods/dirty.rb +8 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +0 -2
  26. data/lib/active_record/attribute_methods/read.rb +0 -1
  27. data/lib/active_record/attribute_methods/serialization.rb +0 -1
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +0 -2
  29. data/lib/active_record/attribute_methods/write.rb +0 -1
  30. data/lib/active_record/attributes.rb +0 -1
  31. data/lib/active_record/autosave_association.rb +11 -7
  32. data/lib/active_record/callbacks.rb +1 -2
  33. data/lib/active_record/coders/yaml_column.rb +0 -1
  34. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +107 -13
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +21 -15
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -5
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +53 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +1 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +27 -27
  40. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -37
  42. data/lib/active_record/connection_adapters/abstract/transaction.rb +14 -7
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +62 -25
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +25 -32
  45. data/lib/active_record/connection_adapters/connection_specification.rb +3 -4
  46. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
  47. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  48. data/lib/active_record/connection_adapters/mysql/database_statements.rb +8 -12
  49. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  51. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +1 -2
  52. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +3 -1
  53. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -8
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -4
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +9 -3
  56. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  59. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +0 -1
  60. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  61. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +0 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +0 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +0 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +2 -2
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -29
  72. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +17 -3
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +8 -7
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -3
  76. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +3 -3
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +23 -8
  78. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  79. data/lib/active_record/connection_handling.rb +17 -22
  80. data/lib/active_record/core.rb +8 -6
  81. data/lib/active_record/counter_cache.rb +4 -1
  82. data/lib/active_record/database_configurations.rb +60 -31
  83. data/lib/active_record/database_configurations/url_config.rb +0 -1
  84. data/lib/active_record/dynamic_matchers.rb +2 -3
  85. data/lib/active_record/enum.rb +9 -0
  86. data/lib/active_record/explain.rb +0 -1
  87. data/lib/active_record/fixture_set/table_row.rb +0 -1
  88. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  89. data/lib/active_record/fixtures.rb +11 -9
  90. data/lib/active_record/gem_version.rb +1 -1
  91. data/lib/active_record/inheritance.rb +0 -3
  92. data/lib/active_record/insert_all.rb +5 -6
  93. data/lib/active_record/internal_metadata.rb +1 -1
  94. data/lib/active_record/locking/optimistic.rb +0 -1
  95. data/lib/active_record/log_subscriber.rb +1 -1
  96. data/lib/active_record/middleware/database_selector.rb +3 -4
  97. data/lib/active_record/middleware/database_selector/resolver.rb +5 -8
  98. data/lib/active_record/migration.rb +43 -32
  99. data/lib/active_record/migration/command_recorder.rb +6 -18
  100. data/lib/active_record/migration/compatibility.rb +3 -3
  101. data/lib/active_record/migration/join_table.rb +0 -1
  102. data/lib/active_record/model_schema.rb +3 -2
  103. data/lib/active_record/nested_attributes.rb +0 -2
  104. data/lib/active_record/no_touching.rb +2 -2
  105. data/lib/active_record/null_relation.rb +0 -1
  106. data/lib/active_record/persistence.rb +4 -5
  107. data/lib/active_record/querying.rb +1 -1
  108. data/lib/active_record/railtie.rb +1 -2
  109. data/lib/active_record/railties/collection_cache_association_loading.rb +1 -1
  110. data/lib/active_record/railties/databases.rake +63 -23
  111. data/lib/active_record/reflection.rb +9 -9
  112. data/lib/active_record/relation.rb +13 -1
  113. data/lib/active_record/relation/batches.rb +0 -1
  114. data/lib/active_record/relation/calculations.rb +3 -5
  115. data/lib/active_record/relation/delegation.rb +7 -6
  116. data/lib/active_record/relation/finder_methods.rb +14 -4
  117. data/lib/active_record/relation/from_clause.rb +4 -0
  118. data/lib/active_record/relation/merger.rb +6 -3
  119. data/lib/active_record/relation/predicate_builder.rb +1 -5
  120. data/lib/active_record/relation/query_methods.rb +94 -55
  121. data/lib/active_record/relation/spawn_methods.rb +0 -1
  122. data/lib/active_record/relation/where_clause.rb +0 -1
  123. data/lib/active_record/result.rb +0 -1
  124. data/lib/active_record/sanitization.rb +30 -2
  125. data/lib/active_record/schema.rb +1 -1
  126. data/lib/active_record/schema_dumper.rb +5 -1
  127. data/lib/active_record/schema_migration.rb +1 -1
  128. data/lib/active_record/scoping.rb +0 -1
  129. data/lib/active_record/scoping/default.rb +0 -1
  130. data/lib/active_record/scoping/named.rb +3 -3
  131. data/lib/active_record/store.rb +1 -1
  132. data/lib/active_record/suppressor.rb +2 -2
  133. data/lib/active_record/table_metadata.rb +21 -10
  134. data/lib/active_record/tasks/database_tasks.rb +76 -8
  135. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  136. data/lib/active_record/tasks/postgresql_database_tasks.rb +0 -1
  137. data/lib/active_record/tasks/sqlite_database_tasks.rb +0 -1
  138. data/lib/active_record/test_databases.rb +1 -16
  139. data/lib/active_record/test_fixtures.rb +2 -1
  140. data/lib/active_record/timestamp.rb +26 -17
  141. data/lib/active_record/touch_later.rb +3 -2
  142. data/lib/active_record/transactions.rb +18 -19
  143. data/lib/active_record/type.rb +0 -1
  144. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  145. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  146. data/lib/active_record/type/serialized.rb +0 -1
  147. data/lib/active_record/type/type_map.rb +0 -1
  148. data/lib/active_record/type/unsigned_integer.rb +0 -1
  149. data/lib/active_record/type_caster/connection.rb +16 -10
  150. data/lib/active_record/validations.rb +3 -3
  151. data/lib/active_record/validations/associated.rb +1 -2
  152. data/lib/arel.rb +17 -6
  153. data/lib/arel/predications.rb +5 -6
  154. data/lib/arel/visitors/depth_first.rb +1 -2
  155. data/lib/arel/visitors/dot.rb +0 -1
  156. data/lib/arel/visitors/mssql.rb +0 -1
  157. data/lib/arel/visitors/oracle.rb +1 -2
  158. data/lib/arel/visitors/oracle12.rb +0 -1
  159. data/lib/arel/visitors/postgresql.rb +0 -1
  160. data/lib/arel/visitors/sqlite.rb +0 -1
  161. data/lib/arel/visitors/to_sql.rb +23 -27
  162. data/lib/arel/visitors/visitor.rb +9 -6
  163. data/lib/arel/visitors/where_sql.rb +0 -1
  164. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  165. data/lib/rails/generators/active_record/migration.rb +0 -1
  166. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +1 -1
  167. data/lib/rails/generators/active_record/model/model_generator.rb +0 -1
  168. metadata +13 -9
@@ -378,7 +378,9 @@ module ActiveRecord
378
378
  end
379
379
 
380
380
  def remove_records(existing_records, records, method)
381
- records.each { |record| callback(:before_remove, record) }
381
+ catch(:abort) do
382
+ records.each { |record| callback(:before_remove, record) }
383
+ end || return
382
384
 
383
385
  delete_records(existing_records, method) if existing_records.any?
384
386
  @target -= records
@@ -434,7 +436,9 @@ module ActiveRecord
434
436
  end
435
437
 
436
438
  def replace_on_target(record, index, skip_callbacks)
437
- callback(:before_add, record) unless skip_callbacks
439
+ catch(:abort) do
440
+ callback(:before_add, record)
441
+ end || return unless skip_callbacks
438
442
 
439
443
  set_inverse_instance(record)
440
444
 
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  # is computed directly through SQL and does not trigger by itself the
28
28
  # instantiation of the actual post records.
29
29
  class CollectionProxy < Relation
30
- def initialize(klass, association) #:nodoc:
30
+ def initialize(klass, association, **) #:nodoc:
31
31
  @association = association
32
32
  super klass
33
33
 
@@ -1029,7 +1029,7 @@ module ActiveRecord
1029
1029
  alias_method :append, :<<
1030
1030
  alias_method :concat, :<<
1031
1031
 
1032
- def prepend(*args)
1032
+ def prepend(*args) # :nodoc:
1033
1033
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1034
1034
  end
1035
1035
 
@@ -1101,7 +1101,6 @@ module ActiveRecord
1101
1101
  delegate(*delegate_methods, to: :scope)
1102
1102
 
1103
1103
  private
1104
-
1105
1104
  def find_nth_with_limit(index, limit)
1106
1105
  load_target if find_from_target?
1107
1106
  super
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  private
40
-
41
40
  # Returns the number of records in this collection.
42
41
  #
43
42
  # If the association has a counter cache it gets that value. Otherwise
@@ -64,16 +64,21 @@ module ActiveRecord
64
64
  end
65
65
  end
66
66
 
67
- def initialize(base, table, associations)
67
+ def initialize(base, table, associations, join_type)
68
68
  tree = self.class.make_tree associations
69
69
  @join_root = JoinBase.new(base, table, build(tree, base))
70
+ @join_type = join_type
71
+ end
72
+
73
+ def base_klass
74
+ join_root.base_klass
70
75
  end
71
76
 
72
77
  def reflections
73
78
  join_root.drop(1).map!(&:reflection)
74
79
  end
75
80
 
76
- def join_constraints(joins_to_add, join_type, alias_tracker)
81
+ def join_constraints(joins_to_add, alias_tracker)
77
82
  @alias_tracker = alias_tracker
78
83
 
79
84
  construct_tables!(join_root)
@@ -82,9 +87,9 @@ module ActiveRecord
82
87
  joins.concat joins_to_add.flat_map { |oj|
83
88
  construct_tables!(oj.join_root)
84
89
  if join_root.match? oj.join_root
85
- walk join_root, oj.join_root
90
+ walk(join_root, oj.join_root, oj.join_type)
86
91
  else
87
- make_join_constraints(oj.join_root, join_type)
92
+ make_join_constraints(oj.join_root, oj.join_type)
88
93
  end
89
94
  }
90
95
  end
@@ -100,7 +105,9 @@ module ActiveRecord
100
105
 
101
106
  model_cache = Hash.new { |h, klass| h[klass] = {} }
102
107
  parents = model_cache[join_root]
108
+
103
109
  column_aliases = aliases.column_aliases join_root
110
+ column_aliases += explicit_selections(column_aliases, result_set)
104
111
 
105
112
  message_bus = ActiveSupport::Notifications.instrumenter
106
113
 
@@ -125,11 +132,18 @@ module ActiveRecord
125
132
  end
126
133
 
127
134
  protected
128
- attr_reader :join_root
135
+ attr_reader :join_root, :join_type
129
136
 
130
137
  private
131
138
  attr_reader :alias_tracker
132
139
 
140
+ def explicit_selections(root_column_aliases, result_set)
141
+ root_names = root_column_aliases.map(&:name).to_set
142
+ result_set.columns
143
+ .reject { |n| root_names.include?(n) || n =~ /\At\d+_r\d+\z/ }
144
+ .map { |n| Aliases::Column.new(n, n) }
145
+ end
146
+
133
147
  def aliases
134
148
  @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
135
149
  columns = join_part.column_names.each_with_index.map { |column_name, j|
@@ -151,7 +165,7 @@ module ActiveRecord
151
165
  end
152
166
  end
153
167
 
154
- def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
168
+ def make_constraints(parent, child, join_type)
155
169
  foreign_table = parent.table
156
170
  foreign_klass = parent.base_klass
157
171
  joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
@@ -173,13 +187,13 @@ module ActiveRecord
173
187
  join ? "#{name}_join" : name
174
188
  end
175
189
 
176
- def walk(left, right)
190
+ def walk(left, right, join_type)
177
191
  intersection, missing = right.children.map { |node1|
178
192
  [left.children.find { |node2| node1.match? node2 }, node1]
179
193
  }.partition(&:first)
180
194
 
181
- joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
182
- joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
195
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
196
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
183
197
  end
184
198
 
185
199
  def find_reflection(klass, name)
@@ -37,15 +37,14 @@ module ActiveRecord
37
37
  nodes = arel.constraints.first
38
38
 
39
39
  others = nodes.children.extract! do |node|
40
- Arel.fetch_attribute(node) { |attr| attr.relation.name != table.name }
40
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
41
41
  end
42
42
 
43
43
  joins << table.create_join(table, table.create_on(nodes), join_type)
44
44
 
45
45
  unless others.empty?
46
46
  joins.concat arel.join_sources
47
- right = joins.last.right
48
- right.expr.children.concat(others)
47
+ append_constraints(joins.last, others)
49
48
  end
50
49
 
51
50
  # The current table in this iteration becomes the foreign table in the next
@@ -65,6 +64,16 @@ module ActiveRecord
65
64
 
66
65
  @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
67
66
  end
67
+
68
+ private
69
+ def append_constraints(join, constraints)
70
+ if join.is_a?(Arel::Nodes::StringJoin)
71
+ join_string = table.create_and(constraints.unshift(join.left))
72
+ join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
73
+ else
74
+ join.right.expr.children.concat(constraints)
75
+ end
76
+ end
68
77
  end
69
78
  end
70
79
  end
@@ -95,7 +95,6 @@ module ActiveRecord
95
95
  end
96
96
 
97
97
  private
98
-
99
98
  # Loads all the given data into +records+ for the +association+.
100
99
  def preloaders_on(association, records, scope, polymorphic_parent = false)
101
100
  case association
@@ -112,7 +111,7 @@ module ActiveRecord
112
111
  association.flat_map { |parent, child|
113
112
  grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
113
  loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
- recs = loaders.flat_map(&:preloaded_records)
114
+ recs = loaders.flat_map(&:preloaded_records).uniq
116
115
  child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
116
  loaders.concat Array.wrap(child).flat_map { |assoc|
118
117
  preloaders_on assoc, recs, scope, child_polymorphic_parent
@@ -185,7 +184,7 @@ module ActiveRecord
185
184
  # and attach it to a relation. The class returned implements a `run` method
186
185
  # that accepts a preloader.
187
186
  def preloader_for(reflection, owners)
188
- if owners.first.association(reflection.name).loaded?
187
+ if owners.all? { |o| o.association(reflection.name).loaded? }
189
188
  return AlreadyLoaded
190
189
  end
191
190
  reflection.check_preloadable!
@@ -27,7 +27,9 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  def records_by_owner
30
- @records_by_owner ||= preloaded_records.each_with_object({}) do |record, result|
30
+ # owners can be duplicated when a relation has a collection association join
31
+ # #compare_by_identity makes such owners different hash keys
32
+ @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
31
33
  owners_by_key[convert_key(record[association_key_name])].each do |owner|
32
34
  (result[owner] ||= []) << record
33
35
  end
@@ -7,7 +7,6 @@ module ActiveRecord
7
7
  include ActiveModel::AttributeAssignment
8
8
 
9
9
  private
10
-
11
10
  def _assign_attributes(attributes)
12
11
  multi_parameter_attributes = {}
13
12
  nested_parameter_attributes = {}
@@ -46,7 +46,6 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  private
49
-
50
49
  def load_schema!
51
50
  super
52
51
  attribute_types.each do |name, type|
@@ -75,7 +74,6 @@ module ActiveRecord
75
74
  end
76
75
 
77
76
  private
78
-
79
77
  def decorators_for(name, type)
80
78
  matching(name, type).map(&:last)
81
79
  end
@@ -159,57 +159,6 @@ module ActiveRecord
159
159
  end
160
160
  end
161
161
 
162
- # Regexp for column names (with or without a table name prefix). Matches
163
- # the following:
164
- # "#{table_name}.#{column_name}"
165
- # "#{column_name}"
166
- COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
167
-
168
- # Regexp for column names with order (with or without a table name
169
- # prefix, with or without various order modifiers). Matches the following:
170
- # "#{table_name}.#{column_name}"
171
- # "#{table_name}.#{column_name} #{direction}"
172
- # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
173
- # "#{table_name}.#{column_name} NULLS LAST"
174
- # "#{column_name}"
175
- # "#{column_name} #{direction}"
176
- # "#{column_name} #{direction} NULLS FIRST"
177
- # "#{column_name} NULLS LAST"
178
- COLUMN_NAME_WITH_ORDER = /
179
- \A
180
- (?:\w+\.)?
181
- \w+
182
- (?:\s+asc|\s+desc)?
183
- (?:\s+nulls\s+(?:first|last))?
184
- \z
185
- /ix
186
-
187
- def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
188
- unexpected = args.reject do |arg|
189
- Arel.arel_node?(arg) ||
190
- arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
191
- end
192
-
193
- return if unexpected.none?
194
-
195
- if allow_unsafe_raw_sql == :deprecated
196
- ActiveSupport::Deprecation.warn(
197
- "Dangerous query method (method whose arguments are used as raw " \
198
- "SQL) called with non-attribute argument(s): " \
199
- "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
200
- "arguments will be disallowed in Rails 6.1. This method should " \
201
- "not be called with user-provided values, such as request " \
202
- "parameters or model attributes. Known-safe values can be passed " \
203
- "by wrapping them in Arel.sql()."
204
- )
205
- else
206
- raise(ActiveRecord::UnknownAttributeReference,
207
- "Query method called with non-attribute argument(s): " +
208
- unexpected.map(&:inspect).join(", ")
209
- )
210
- end
211
- end
212
-
213
162
  # Returns true if the given attribute exists, otherwise false.
214
163
  #
215
164
  # class Person < ActiveRecord::Base
@@ -66,7 +66,6 @@ module ActiveRecord
66
66
  end
67
67
 
68
68
  private
69
-
70
69
  # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
71
70
  def attribute_before_type_cast(attribute_name)
72
71
  read_attribute_before_type_cast(attribute_name)
@@ -49,7 +49,7 @@ module ActiveRecord
49
49
  # +to+ When passed, this method will return false unless the value was
50
50
  # changed to the given value
51
51
  def saved_change_to_attribute?(attr_name, **options)
52
- mutations_before_last_save.changed?(attr_name.to_s, options)
52
+ mutations_before_last_save.changed?(attr_name.to_s, **options)
53
53
  end
54
54
 
55
55
  # Returns the change to an attribute during the last save. If the
@@ -99,7 +99,7 @@ module ActiveRecord
99
99
  # +to+ When passed, this method will return false unless the value will be
100
100
  # changed to the given value
101
101
  def will_save_change_to_attribute?(attr_name, **options)
102
- mutations_from_database.changed?(attr_name.to_s, options)
102
+ mutations_from_database.changed?(attr_name.to_s, **options)
103
103
  end
104
104
 
105
105
  # Returns the change to an attribute that will be persisted during the
@@ -177,6 +177,11 @@ module ActiveRecord
177
177
 
178
178
  affected_rows = super
179
179
 
180
+ if @_skip_dirty_tracking ||= false
181
+ clear_attribute_changes(@_touch_attr_names)
182
+ return affected_rows
183
+ end
184
+
180
185
  changes = {}
181
186
  @attributes.keys.each do |attr_name|
182
187
  next if @_touch_attr_names.include?(attr_name)
@@ -193,7 +198,7 @@ module ActiveRecord
193
198
 
194
199
  affected_rows
195
200
  ensure
196
- @_touch_attr_names = nil
201
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
197
202
  end
198
203
 
199
204
  def _update_record(attribute_names = attribute_names_for_partial_writes)
@@ -45,7 +45,6 @@ module ActiveRecord
45
45
  end
46
46
 
47
47
  private
48
-
49
48
  def attribute_method?(attr_name)
50
49
  attr_name == "id" || super
51
50
  end
@@ -120,7 +119,6 @@ module ActiveRecord
120
119
  end
121
120
 
122
121
  private
123
-
124
122
  def suppress_composite_primary_key(pk)
125
123
  return pk unless pk.is_a?(Array)
126
124
 
@@ -7,7 +7,6 @@ module ActiveRecord
7
7
 
8
8
  module ClassMethods # :nodoc:
9
9
  private
10
-
11
10
  def define_method_attribute(name)
12
11
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
13
12
  generated_attribute_methods, name
@@ -79,7 +79,6 @@ module ActiveRecord
79
79
  end
80
80
 
81
81
  private
82
-
83
82
  def type_incompatible_with_serialize?(type, class_name)
84
83
  type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
85
84
  type.respond_to?(:type_cast_array, true) && class_name == ::Array
@@ -25,7 +25,6 @@ module ActiveRecord
25
25
  end
26
26
 
27
27
  private
28
-
29
28
  def convert_time_to_time_zone(value)
30
29
  return if value.nil?
31
30
 
@@ -64,7 +63,6 @@ module ActiveRecord
64
63
 
65
64
  module ClassMethods # :nodoc:
66
65
  private
67
-
68
66
  def inherited(subclass)
69
67
  super
70
68
  # We need to apply this decorator here, rather than on module inclusion. The closure
@@ -11,7 +11,6 @@ module ActiveRecord
11
11
 
12
12
  module ClassMethods # :nodoc:
13
13
  private
14
-
15
14
  def define_method_attribute=(name)
16
15
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
17
16
  generated_attribute_methods, name, writer: true,
@@ -255,7 +255,6 @@ module ActiveRecord
255
255
  end
256
256
 
257
257
  private
258
-
259
258
  NO_DEFAULT_PROVIDED = Object.new # :nodoc:
260
259
  private_constant :NO_DEFAULT_PROVIDED
261
260
 
@@ -147,7 +147,6 @@ module ActiveRecord
147
147
 
148
148
  module ClassMethods # :nodoc:
149
149
  private
150
-
151
150
  def define_non_cyclic_method(name, &block)
152
151
  return if instance_methods(false).include?(name)
153
152
  define_method(name) do |*args|
@@ -267,12 +266,11 @@ module ActiveRecord
267
266
  end
268
267
 
269
268
  private
270
-
271
269
  # Returns the record for an association collection that should be validated
272
270
  # or saved. If +autosave+ is +false+ only new records will be returned,
273
271
  # unless the parent is/was a new record itself.
274
272
  def associated_records_to_validate_or_save(association, new_record, autosave)
275
- if new_record
273
+ if new_record || custom_validation_context?
276
274
  association && association.target
277
275
  elsif autosave
278
276
  association.target.find_all(&:changed_for_autosave?)
@@ -304,7 +302,7 @@ module ActiveRecord
304
302
  def validate_single_association(reflection)
305
303
  association = association_instance_get(reflection.name)
306
304
  record = association && association.reader
307
- association_valid?(reflection, record) if record
305
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
308
306
  end
309
307
 
310
308
  # Validate the associated records if <tt>:validate</tt> or
@@ -324,7 +322,7 @@ module ActiveRecord
324
322
  def association_valid?(reflection, record, index = nil)
325
323
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
326
324
 
327
- context = validation_context unless [:create, :update].include?(validation_context)
325
+ context = validation_context if custom_validation_context?
328
326
 
329
327
  unless valid = record.valid?(context)
330
328
  if reflection.options[:autosave]
@@ -363,7 +361,9 @@ module ActiveRecord
363
361
  # Is used as a before_save callback to check while saving a collection
364
362
  # association whether or not the parent was a new record before saving.
365
363
  def before_save_collection_association
366
- @new_record_before_save = new_record?
364
+ unless defined?(@new_record_before_save)
365
+ @new_record_before_save = new_record?
366
+ end
367
367
  end
368
368
 
369
369
  def after_save_collection_association
@@ -416,7 +416,7 @@ module ActiveRecord
416
416
  saved = record.save(validate: false)
417
417
  end
418
418
 
419
- raise ActiveRecord::Rollback unless saved
419
+ raise(RecordInvalid.new(association.owner)) unless saved
420
420
  end
421
421
  end
422
422
  end
@@ -499,6 +499,10 @@ module ActiveRecord
499
499
  end
500
500
  end
501
501
 
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
505
+
502
506
  def _ensure_no_duplicate_errors
503
507
  errors.messages.each_key do |attribute|
504
508
  errors[attribute].uniq!