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.
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)