activerecord 4.2.0 → 5.2.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Autosave Association
3
5
  #
4
- # +AutosaveAssociation+ is a module that takes care of automatically saving
6
+ # AutosaveAssociation is a module that takes care of automatically saving
5
7
  # associated records when their parent is saved. In addition to saving, it
6
8
  # also destroys any associated records that were marked for destruction.
7
- # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
9
+ # (See #mark_for_destruction and #marked_for_destruction?).
8
10
  #
9
11
  # Saving of the parent, its associations, and the destruction of marked
10
12
  # associations, all happen inside a transaction. This should never leave the
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  #
23
25
  # == Validation
24
26
  #
25
- # Children records are validated unless <tt>:validate</tt> is +false+.
27
+ # Child records are validated unless <tt>:validate</tt> is +false+.
26
28
  #
27
29
  # == Callbacks
28
30
  #
@@ -125,7 +127,6 @@ module ActiveRecord
125
127
  # Now it _is_ removed from the database:
126
128
  #
127
129
  # Comment.find_by(id: id).nil? # => true
128
-
129
130
  module AutosaveAssociation
130
131
  extend ActiveSupport::Concern
131
132
 
@@ -141,9 +142,10 @@ module ActiveRecord
141
142
 
142
143
  included do
143
144
  Associations::Builder::Association.extensions << AssociationBuilderExtension
145
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
144
146
  end
145
147
 
146
- module ClassMethods
148
+ module ClassMethods # :nodoc:
147
149
  private
148
150
 
149
151
  def define_non_cyclic_method(name, &block)
@@ -153,10 +155,10 @@ module ActiveRecord
153
155
  # Loop prevention for validation of associations
154
156
  unless @_already_called[name]
155
157
  begin
156
- @_already_called[name]=true
158
+ @_already_called[name] = true
157
159
  result = instance_eval(&block)
158
160
  ensure
159
- @_already_called[name]=false
161
+ @_already_called[name] = false
160
162
  end
161
163
  end
162
164
 
@@ -177,11 +179,10 @@ module ActiveRecord
177
179
  # before actually defining them.
178
180
  def add_autosave_association_callbacks(reflection)
179
181
  save_method = :"autosave_associated_records_for_#{reflection.name}"
180
- validation_method = :"validate_associated_records_for_#{reflection.name}"
181
- collection = reflection.collection?
182
182
 
183
- if collection
183
+ if reflection.collection?
184
184
  before_save :before_save_collection_association
185
+ after_save :after_save_collection_association
185
186
 
186
187
  define_non_cyclic_method(save_method) { save_collection_association(reflection) }
187
188
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
@@ -200,14 +201,25 @@ module ActiveRecord
200
201
  after_create save_method
201
202
  after_update save_method
202
203
  else
203
- define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
204
+ define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
204
205
  before_save save_method
205
206
  end
206
207
 
208
+ define_autosave_validation_callbacks(reflection)
209
+ end
210
+
211
+ def define_autosave_validation_callbacks(reflection)
212
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
207
213
  if reflection.validate? && !method_defined?(validation_method)
208
- method = (collection ? :validate_collection_association : :validate_single_association)
214
+ if reflection.collection?
215
+ method = :validate_collection_association
216
+ else
217
+ method = :validate_single_association
218
+ end
219
+
209
220
  define_non_cyclic_method(validation_method) { send(method, reflection) }
210
221
  validate validation_method
222
+ after_validation :_ensure_no_duplicate_errors
211
223
  end
212
224
  end
213
225
  end
@@ -219,7 +231,7 @@ module ActiveRecord
219
231
  super
220
232
  end
221
233
 
222
- # Marks this record to be destroyed as part of the parents save transaction.
234
+ # Marks this record to be destroyed as part of the parent's save transaction.
223
235
  # This does _not_ actually destroy the record instantly, rather child record will be destroyed
224
236
  # when <tt>parent.save</tt> is called.
225
237
  #
@@ -228,7 +240,7 @@ module ActiveRecord
228
240
  @marked_for_destruction = true
229
241
  end
230
242
 
231
- # Returns whether or not this record will be destroyed as part of the parents save transaction.
243
+ # Returns whether or not this record will be destroyed as part of the parent's save transaction.
232
244
  #
233
245
  # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
234
246
  def marked_for_destruction?
@@ -251,7 +263,7 @@ module ActiveRecord
251
263
  # Returns whether or not this record has been changed in any way (including whether
252
264
  # any of its nested autosave associations are likewise changed)
253
265
  def changed_for_autosave?
254
- new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
266
+ new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
255
267
  end
256
268
 
257
269
  private
@@ -260,23 +272,30 @@ module ActiveRecord
260
272
  # or saved. If +autosave+ is +false+ only new records will be returned,
261
273
  # unless the parent is/was a new record itself.
262
274
  def associated_records_to_validate_or_save(association, new_record, autosave)
263
- if new_record
275
+ if new_record || custom_validation_context?
264
276
  association && association.target
265
277
  elsif autosave
266
- association.target.find_all { |record| record.changed_for_autosave? }
278
+ association.target.find_all(&:changed_for_autosave?)
267
279
  else
268
- association.target.find_all { |record| record.new_record? }
280
+ association.target.find_all(&:new_record?)
269
281
  end
270
282
  end
271
283
 
272
284
  # go through nested autosave associations that are loaded in memory (without loading
273
285
  # any new ones), and return true if is changed for autosave
274
286
  def nested_records_changed_for_autosave?
275
- self.class._reflections.values.any? do |reflection|
276
- if reflection.options[:autosave]
277
- association = association_instance_get(reflection.name)
278
- association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
287
+ @_nested_records_changed_for_autosave_already_called ||= false
288
+ return false if @_nested_records_changed_for_autosave_already_called
289
+ begin
290
+ @_nested_records_changed_for_autosave_already_called = true
291
+ self.class._reflections.values.any? do |reflection|
292
+ if reflection.options[:autosave]
293
+ association = association_instance_get(reflection.name)
294
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
295
+ end
279
296
  end
297
+ ensure
298
+ @_nested_records_changed_for_autosave_already_called = false
280
299
  end
281
300
  end
282
301
 
@@ -285,7 +304,7 @@ module ActiveRecord
285
304
  def validate_single_association(reflection)
286
305
  association = association_instance_get(reflection.name)
287
306
  record = association && association.reader
288
- association_valid?(reflection, record) if record
307
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
289
308
  end
290
309
 
291
310
  # Validate the associated records if <tt>:validate</tt> or
@@ -294,7 +313,7 @@ module ActiveRecord
294
313
  def validate_collection_association(reflection)
295
314
  if association = association_instance_get(reflection.name)
296
315
  if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
297
- records.each { |record| association_valid?(reflection, record) }
316
+ records.each_with_index { |record, index| association_valid?(reflection, record, index) }
298
317
  end
299
318
  end
300
319
  end
@@ -302,17 +321,30 @@ module ActiveRecord
302
321
  # Returns whether or not the association is valid and applies any errors to
303
322
  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
304
323
  # enabled records if they're marked_for_destruction? or destroyed.
305
- def association_valid?(reflection, record)
306
- return true if record.destroyed? || record.marked_for_destruction?
324
+ def association_valid?(reflection, record, index = nil)
325
+ return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
307
326
 
308
- validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
309
- unless valid = record.valid?(validation_context)
327
+ context = validation_context if custom_validation_context?
328
+
329
+ unless valid = record.valid?(context)
310
330
  if reflection.options[:autosave]
331
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
332
+
311
333
  record.errors.each do |attribute, message|
312
- attribute = "#{reflection.name}.#{attribute}"
334
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
313
335
  errors[attribute] << message
314
336
  errors[attribute].uniq!
315
337
  end
338
+
339
+ record.errors.details.each_key do |attribute|
340
+ reflection_attribute =
341
+ normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
342
+
343
+ record.errors.details[attribute].each do |error|
344
+ errors.details[reflection_attribute] << error
345
+ errors.details[reflection_attribute].uniq!
346
+ end
347
+ end
316
348
  else
317
349
  errors.add(reflection.name)
318
350
  end
@@ -320,18 +352,29 @@ module ActiveRecord
320
352
  valid
321
353
  end
322
354
 
355
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
356
+ if indexed_attribute
357
+ "#{reflection.name}[#{index}].#{attribute}"
358
+ else
359
+ "#{reflection.name}.#{attribute}"
360
+ end
361
+ end
362
+
323
363
  # Is used as a before_save callback to check while saving a collection
324
364
  # association whether or not the parent was a new record before saving.
325
365
  def before_save_collection_association
326
366
  @new_record_before_save = new_record?
327
- true
367
+ end
368
+
369
+ def after_save_collection_association
370
+ @new_record_before_save = false
328
371
  end
329
372
 
330
373
  # Saves any new associated records, or all loaded autosave associations if
331
374
  # <tt>:autosave</tt> is enabled on the association.
332
375
  #
333
376
  # In addition, it destroys all children that were marked for destruction
334
- # with mark_for_destruction.
377
+ # with #mark_for_destruction.
335
378
  #
336
379
  # This all happens inside a transaction, _if_ the Transactions module is included into
337
380
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
@@ -339,7 +382,14 @@ module ActiveRecord
339
382
  if association = association_instance_get(reflection.name)
340
383
  autosave = reflection.options[:autosave]
341
384
 
342
- if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
385
+ # By saving the instance variable in a local variable,
386
+ # we make the whole callback re-entrant.
387
+ new_record_before_save = @new_record_before_save
388
+
389
+ # reconstruct the scope now that we know the owner's id
390
+ association.reset_scope
391
+
392
+ if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
343
393
  if autosave
344
394
  records_to_destroy = records.select(&:marked_for_destruction?)
345
395
  records_to_destroy.each { |record| association.destroy(record) }
@@ -351,22 +401,24 @@ module ActiveRecord
351
401
 
352
402
  saved = true
353
403
 
354
- if autosave != false && (@new_record_before_save || record.new_record?)
404
+ if autosave != false && (new_record_before_save || record.new_record?)
355
405
  if autosave
356
406
  saved = association.insert_record(record, false)
357
- else
358
- association.insert_record(record) unless reflection.nested?
407
+ elsif !reflection.nested?
408
+ association_saved = association.insert_record(record)
409
+
410
+ if reflection.validate?
411
+ errors.add(reflection.name) unless association_saved
412
+ saved = association_saved
413
+ end
359
414
  end
360
415
  elsif autosave
361
- saved = record.save(:validate => false)
416
+ saved = record.save(validate: false)
362
417
  end
363
418
 
364
419
  raise ActiveRecord::Rollback unless saved
365
420
  end
366
421
  end
367
-
368
- # reconstruct the scope now that we know the owner's id
369
- association.reset_scope if association.respond_to?(:reset_scope)
370
422
  end
371
423
  end
372
424
 
@@ -374,7 +426,7 @@ module ActiveRecord
374
426
  # on the association.
375
427
  #
376
428
  # In addition, it will destroy the association if it was marked for
377
- # destruction with mark_for_destruction.
429
+ # destruction with #mark_for_destruction.
378
430
  #
379
431
  # This all happens inside a transaction, _if_ the Transactions module is included into
380
432
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
@@ -393,9 +445,12 @@ module ActiveRecord
393
445
  if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
394
446
  unless reflection.through_reflection
395
447
  record[reflection.foreign_key] = key
448
+ if inverse_reflection = reflection.inverse_of
449
+ record.association(inverse_reflection.name).loaded!
450
+ end
396
451
  end
397
452
 
398
- saved = record.save(:validate => !autosave)
453
+ saved = record.save(validate: !autosave)
399
454
  raise ActiveRecord::Rollback if !saved && autosave
400
455
  saved
401
456
  end
@@ -406,8 +461,14 @@ module ActiveRecord
406
461
  # If the record is new or it has changed, returns true.
407
462
  def record_changed?(reflection, record, key)
408
463
  record.new_record? ||
409
- (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
410
- record.attribute_changed?(reflection.foreign_key)
464
+ association_foreign_key_changed?(reflection, record, key) ||
465
+ record.will_save_change_to_attribute?(reflection.foreign_key)
466
+ end
467
+
468
+ def association_foreign_key_changed?(reflection, record, key)
469
+ return false if reflection.through_reflection?
470
+
471
+ record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
411
472
  end
412
473
 
413
474
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -415,7 +476,9 @@ module ActiveRecord
415
476
  # In addition, it will destroy the association if it was marked for destruction.
416
477
  def save_belongs_to_association(reflection)
417
478
  association = association_instance_get(reflection.name)
418
- record = association && association.load_target
479
+ return unless association && association.loaded? && !association.stale_target?
480
+
481
+ record = association.load_target
419
482
  if record && !record.destroyed?
420
483
  autosave = reflection.options[:autosave]
421
484
 
@@ -423,7 +486,7 @@ module ActiveRecord
423
486
  self[reflection.foreign_key] = nil
424
487
  record.destroy
425
488
  elsif autosave != false
426
- saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
489
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
427
490
 
428
491
  if association.updated?
429
492
  association_id = record.send(reflection.options[:primary_key] || :id)
@@ -435,5 +498,15 @@ module ActiveRecord
435
498
  end
436
499
  end
437
500
  end
501
+
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
505
+
506
+ def _ensure_no_duplicate_errors
507
+ errors.messages.each_key do |attribute|
508
+ errors[attribute].uniq!
509
+ end
510
+ end
438
511
  end
439
512
  end
@@ -1,27 +1,28 @@
1
- require 'yaml'
2
- require 'set'
3
- require 'active_support/benchmarkable'
4
- require 'active_support/dependencies'
5
- require 'active_support/descendants_tracker'
6
- require 'active_support/time'
7
- require 'active_support/core_ext/module/attribute_accessors'
8
- require 'active_support/core_ext/class/delegating_attributes'
9
- require 'active_support/core_ext/array/extract_options'
10
- require 'active_support/core_ext/hash/deep_merge'
11
- require 'active_support/core_ext/hash/slice'
12
- require 'active_support/core_ext/hash/transform_values'
13
- require 'active_support/core_ext/string/behavior'
14
- require 'active_support/core_ext/kernel/singleton_class'
15
- require 'active_support/core_ext/module/introspection'
16
- require 'active_support/core_ext/object/duplicable'
17
- require 'active_support/core_ext/class/subclasses'
18
- require 'arel'
19
- require 'active_record/attribute_decorators'
20
- require 'active_record/errors'
21
- require 'active_record/log_subscriber'
22
- require 'active_record/explain_subscriber'
23
- require 'active_record/relation/delegation'
24
- require 'active_record/attributes'
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "active_support/benchmarkable"
5
+ require "active_support/dependencies"
6
+ require "active_support/descendants_tracker"
7
+ require "active_support/time"
8
+ require "active_support/core_ext/module/attribute_accessors"
9
+ require "active_support/core_ext/array/extract_options"
10
+ require "active_support/core_ext/hash/deep_merge"
11
+ require "active_support/core_ext/hash/slice"
12
+ require "active_support/core_ext/hash/transform_values"
13
+ require "active_support/core_ext/string/behavior"
14
+ require "active_support/core_ext/kernel/singleton_class"
15
+ require "active_support/core_ext/module/introspection"
16
+ require "active_support/core_ext/object/duplicable"
17
+ require "active_support/core_ext/class/subclasses"
18
+ require "active_record/attribute_decorators"
19
+ require "active_record/define_callbacks"
20
+ require "active_record/errors"
21
+ require "active_record/log_subscriber"
22
+ require "active_record/explain_subscriber"
23
+ require "active_record/relation/delegation"
24
+ require "active_record/attributes"
25
+ require "active_record/type_caster"
25
26
 
26
27
  module ActiveRecord #:nodoc:
27
28
  # = Active Record
@@ -119,29 +120,25 @@ module ActiveRecord #:nodoc:
119
120
  # All column values are automatically available through basic accessors on the Active Record
120
121
  # object, but sometimes you want to specialize this behavior. This can be done by overwriting
121
122
  # the default accessors (using the same name as the attribute) and calling
122
- # <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
123
- # change things.
123
+ # +super+ to actually change things.
124
124
  #
125
125
  # class Song < ActiveRecord::Base
126
126
  # # Uses an integer of seconds to hold the length of the song
127
127
  #
128
128
  # def length=(minutes)
129
- # write_attribute(:length, minutes.to_i * 60)
129
+ # super(minutes.to_i * 60)
130
130
  # end
131
131
  #
132
132
  # def length
133
- # read_attribute(:length) / 60
133
+ # super / 60
134
134
  # end
135
135
  # end
136
136
  #
137
- # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
138
- # instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
139
- #
140
137
  # == Attribute query methods
141
138
  #
142
139
  # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
143
140
  # Query methods allow you to test whether an attribute value is present.
144
- # For numeric values, present is defined as non-zero.
141
+ # Additionally, when dealing with numeric values, a query method will return false if the value is zero.
145
142
  #
146
143
  # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
147
144
  # to determine whether the user has a name:
@@ -172,10 +169,11 @@ module ActiveRecord #:nodoc:
172
169
  # <tt>Person.find_by_user_name(user_name)</tt>.
173
170
  #
174
171
  # It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
175
- # <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records,
172
+ # ActiveRecord::RecordNotFound error if they do not return any records,
176
173
  # like <tt>Person.find_by_last_name!</tt>.
177
174
  #
178
- # It's also possible to use multiple attributes in the same find by separating them with "_and_".
175
+ # It's also possible to use multiple attributes in the same <tt>find_by_</tt> by separating them with
176
+ # "_and_".
179
177
  #
180
178
  # Person.find_by(user_name: user_name, password: password)
181
179
  # Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
@@ -187,7 +185,8 @@ module ActiveRecord #:nodoc:
187
185
  # == Saving arrays, hashes, and other non-mappable objects in text columns
188
186
  #
189
187
  # Active Record can serialize any object in text columns using YAML. To do so, you must
190
- # specify this with a call to the class method +serialize+.
188
+ # specify this with a call to the class method
189
+ # {serialize}[rdoc-ref:AttributeMethods::Serialization::ClassMethods#serialize].
191
190
  # This makes it possible to store arrays, hashes, and other non-mappable objects without doing
192
191
  # any additional work.
193
192
  #
@@ -227,39 +226,47 @@ module ActiveRecord #:nodoc:
227
226
  #
228
227
  # == Connection to multiple databases in different models
229
228
  #
230
- # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved
229
+ # Connections are usually created through
230
+ # {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
231
231
  # by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
232
232
  # connection. But you can also set a class-specific connection. For example, if Course is an
233
233
  # ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
234
234
  # and Course and all of its subclasses will use this connection instead.
235
235
  #
236
236
  # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
237
- # a Hash indexed by the class. If a connection is requested, the retrieve_connection method
237
+ # a hash indexed by the class. If a connection is requested, the
238
+ # {ActiveRecord::Base.retrieve_connection}[rdoc-ref:ConnectionHandling#retrieve_connection] method
238
239
  # will go up the class-hierarchy until a connection is found in the connection pool.
239
240
  #
240
241
  # == Exceptions
241
242
  #
242
243
  # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
243
- # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
244
- # <tt>:adapter</tt> key.
245
- # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a
246
- # non-existent adapter
244
+ # * AdapterNotSpecified - The configuration hash used in
245
+ # {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
246
+ # didn't include an <tt>:adapter</tt> key.
247
+ # * AdapterNotFound - The <tt>:adapter</tt> key used in
248
+ # {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection]
249
+ # specified a non-existent adapter
247
250
  # (or a bad spelling of an existing one).
248
251
  # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
249
252
  # specified in the association definition.
250
253
  # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
251
- # <tt>attributes=</tt> method.
254
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
252
255
  # You can inspect the +attribute+ property of the exception object to determine which attribute
253
256
  # triggered the error.
254
- # * ConnectionNotEstablished - No connection has been established. Use <tt>establish_connection</tt>
255
- # before querying.
257
+ # * ConnectionNotEstablished - No connection has been established.
258
+ # Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
256
259
  # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
257
- # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
260
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
261
+ # The +errors+ property of this exception contains an array of
258
262
  # AttributeAssignmentError
259
263
  # objects that should be inspected to determine which attributes triggered the errors.
260
- # * RecordInvalid - raised by save! and create! when the record is invalid.
261
- # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
262
- # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
264
+ # * RecordInvalid - raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
265
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
266
+ # when the record is invalid.
267
+ # * RecordNotFound - No record responded to the {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method.
268
+ # Either the row with the given ID doesn't exist or the row didn't meet the additional restrictions.
269
+ # Some {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] calls do not raise this exception to signal
263
270
  # nothing was found, please check its documentation for further details.
264
271
  # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
265
272
  # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
@@ -281,6 +288,7 @@ module ActiveRecord #:nodoc:
281
288
  extend Explain
282
289
  extend Enum
283
290
  extend Delegation::DelegateCache
291
+ extend CollectionCacheKey
284
292
 
285
293
  include Core
286
294
  include Persistence
@@ -298,6 +306,7 @@ module ActiveRecord #:nodoc:
298
306
  include AttributeDecorators
299
307
  include Locking::Optimistic
300
308
  include Locking::Pessimistic
309
+ include DefineCallbacks
301
310
  include AttributeMethods
302
311
  include Callbacks
303
312
  include Timestamp
@@ -307,10 +316,13 @@ module ActiveRecord #:nodoc:
307
316
  include NestedAttributes
308
317
  include Aggregations
309
318
  include Transactions
319
+ include TouchLater
310
320
  include NoTouching
311
321
  include Reflection
312
322
  include Serialization
313
323
  include Store
324
+ include SecureToken
325
+ include Suppressor
314
326
  end
315
327
 
316
328
  ActiveSupport.run_load_hooks(:active_record, Base)