activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  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 +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  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 +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  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 +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  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 +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  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 +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  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 +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  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 +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  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 +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  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 +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  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 +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  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 +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,12 +1,12 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  # = Active Record Autosave Association
5
5
  #
6
- # +AutosaveAssociation+ is a module that takes care of automatically saving
6
+ # AutosaveAssociation is a module that takes care of automatically saving
7
7
  # associated records when their parent is saved. In addition to saving, it
8
8
  # also destroys any associated records that were marked for destruction.
9
- # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
9
+ # (See #mark_for_destruction and #marked_for_destruction?).
10
10
  #
11
11
  # Saving of the parent, its associations, and the destruction of marked
12
12
  # associations, all happen inside a transaction. This should never leave the
@@ -18,28 +18,27 @@ module ActiveRecord
18
18
  # Note that it also means that associations marked for destruction won't
19
19
  # be destroyed directly. They will however still be marked for destruction.
20
20
  #
21
- # Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
22
- # When the <tt>:autosave</tt> option is not present new associations are saved.
21
+ # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
22
+ # When the <tt>:autosave</tt> option is not present then new association records are
23
+ # saved but the updated association records are not saved.
23
24
  #
24
25
  # == Validation
25
26
  #
26
- # Children records are validated unless <tt>:validate</tt> is +false+.
27
+ # Child records are validated unless <tt>:validate</tt> is +false+.
27
28
  #
28
29
  # == Callbacks
29
30
  #
30
31
  # Association with autosave option defines several callbacks on your
31
32
  # model (before_save, after_create, after_update). Please note that
32
33
  # callbacks are executed in the order they were defined in
33
- # model. You should avoid modyfing the association content, before
34
+ # model. You should avoid modifying the association content, before
34
35
  # autosave callbacks are executed. Placing your callbacks after
35
36
  # associations is usually a good practice.
36
37
  #
37
- # == Examples
38
- #
39
38
  # === One-to-one Example
40
39
  #
41
- # class Post
42
- # has_one :author, :autosave => true
40
+ # class Post < ActiveRecord::Base
41
+ # has_one :author, autosave: true
43
42
  # end
44
43
  #
45
44
  # Saving changes to the parent and its associated model can now be performed
@@ -66,167 +65,173 @@ module ActiveRecord
66
65
  # Note that the model is _not_ yet removed from the database:
67
66
  #
68
67
  # id = post.author.id
69
- # Author.find_by_id(id).nil? # => false
68
+ # Author.find_by(id: id).nil? # => false
70
69
  #
71
70
  # post.save
72
71
  # post.reload.author # => nil
73
72
  #
74
73
  # Now it _is_ removed from the database:
75
74
  #
76
- # Author.find_by_id(id).nil? # => true
75
+ # Author.find_by(id: id).nil? # => true
77
76
  #
78
77
  # === One-to-many Example
79
78
  #
80
79
  # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
81
80
  #
82
- # class Post
83
- # has_many :comments # :autosave option is no declared
81
+ # class Post < ActiveRecord::Base
82
+ # has_many :comments # :autosave option is not declared
84
83
  # end
85
84
  #
86
- # post = Post.new(:title => 'ruby rocks')
87
- # post.comments.build(:body => 'hello world')
85
+ # post = Post.new(title: 'ruby rocks')
86
+ # post.comments.build(body: 'hello world')
88
87
  # post.save # => saves both post and comment
89
88
  #
90
- # post = Post.create(:title => 'ruby rocks')
91
- # post.comments.build(:body => 'hello world')
89
+ # post = Post.create(title: 'ruby rocks')
90
+ # post.comments.build(body: 'hello world')
92
91
  # post.save # => saves both post and comment
93
92
  #
94
- # post = Post.create(:title => 'ruby rocks')
95
- # post.comments.create(:body => 'hello world')
93
+ # post = Post.create(title: 'ruby rocks')
94
+ # post.comments.create(body: 'hello world')
96
95
  # post.save # => saves both post and comment
97
96
  #
98
- # When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
97
+ # When <tt>:autosave</tt> is true all children are saved, no matter whether they
98
+ # are new records or not:
99
99
  #
100
- # class Post
101
- # has_many :comments, :autosave => true
100
+ # class Post < ActiveRecord::Base
101
+ # has_many :comments, autosave: true
102
102
  # end
103
103
  #
104
- # post = Post.create(:title => 'ruby rocks')
105
- # post.comments.create(:body => 'hello world')
104
+ # post = Post.create(title: 'ruby rocks')
105
+ # post.comments.create(body: 'hello world')
106
106
  # post.comments[0].body = 'hi everyone'
107
- # post.save # => saves both post and comment, with 'hi everyone' as body
107
+ # post.comments.build(body: "good morning.")
108
+ # post.title += "!"
109
+ # post.save # => saves both post and comments.
108
110
  #
109
111
  # Destroying one of the associated models as part of the parent's save action
110
112
  # is as simple as marking it for destruction:
111
113
  #
112
- # post.comments.last.mark_for_destruction
113
- # post.comments.last.marked_for_destruction? # => true
114
+ # post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
115
+ # post.comments[1].mark_for_destruction
116
+ # post.comments[1].marked_for_destruction? # => true
114
117
  # post.comments.length # => 2
115
118
  #
116
119
  # Note that the model is _not_ yet removed from the database:
117
120
  #
118
121
  # id = post.comments.last.id
119
- # Comment.find_by_id(id).nil? # => false
122
+ # Comment.find_by(id: id).nil? # => false
120
123
  #
121
124
  # post.save
122
125
  # post.reload.comments.length # => 1
123
126
  #
124
127
  # Now it _is_ removed from the database:
125
128
  #
126
- # Comment.find_by_id(id).nil? # => true
127
-
129
+ # Comment.find_by(id: id).nil? # => true
128
130
  module AutosaveAssociation
129
131
  extend ActiveSupport::Concern
130
132
 
131
- ASSOCIATION_TYPES = %w{ HasOne HasMany BelongsTo HasAndBelongsToMany }
132
-
133
133
  module AssociationBuilderExtension #:nodoc:
134
- def self.included(base)
135
- base.valid_options << :autosave
134
+ def self.build(model, reflection)
135
+ model.send(:add_autosave_association_callbacks, reflection)
136
136
  end
137
137
 
138
- def build
139
- reflection = super
140
- model.send(:add_autosave_association_callbacks, reflection)
141
- reflection
138
+ def self.valid_options
139
+ [ :autosave ]
142
140
  end
143
141
  end
144
142
 
145
143
  included do
146
- ASSOCIATION_TYPES.each do |type|
147
- Associations::Builder.const_get(type).send(:include, AssociationBuilderExtension)
148
- end
144
+ Associations::Builder::Association.extensions << AssociationBuilderExtension
145
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
149
146
  end
150
147
 
151
- module ClassMethods
148
+ module ClassMethods # :nodoc:
152
149
  private
153
150
 
154
- def define_non_cyclic_method(name, reflection, &block)
155
- define_method(name) do |*args|
156
- result = true; @_already_called ||= {}
157
- # Loop prevention for validation of associations
158
- unless @_already_called[[name, reflection.name]]
159
- begin
160
- @_already_called[[name, reflection.name]]=true
161
- result = instance_eval(&block)
162
- ensure
163
- @_already_called[[name, reflection.name]]=false
151
+ def define_non_cyclic_method(name, &block)
152
+ return if method_defined?(name)
153
+ define_method(name) do |*args|
154
+ result = true; @_already_called ||= {}
155
+ # Loop prevention for validation of associations
156
+ unless @_already_called[name]
157
+ begin
158
+ @_already_called[name] = true
159
+ result = instance_eval(&block)
160
+ ensure
161
+ @_already_called[name] = false
162
+ end
164
163
  end
165
- end
166
164
 
167
- result
165
+ result
166
+ end
168
167
  end
169
- end
170
168
 
171
- # Adds validation and save callbacks for the association as specified by
172
- # the +reflection+.
173
- #
174
- # For performance reasons, we don't check whether to validate at runtime.
175
- # However the validation and callback methods are lazy and those methods
176
- # get created when they are invoked for the very first time. However,
177
- # this can change, for instance, when using nested attributes, which is
178
- # called _after_ the association has been defined. Since we don't want
179
- # the callbacks to get defined multiple times, there are guards that
180
- # check if the save or validation methods have already been defined
181
- # before actually defining them.
182
- def add_autosave_association_callbacks(reflection)
183
- save_method = :"autosave_associated_records_for_#{reflection.name}"
184
- validation_method = :"validate_associated_records_for_#{reflection.name}"
185
- collection = reflection.collection?
186
-
187
- unless method_defined?(save_method)
188
- if collection
169
+ # Adds validation and save callbacks for the association as specified by
170
+ # the +reflection+.
171
+ #
172
+ # For performance reasons, we don't check whether to validate at runtime.
173
+ # However the validation and callback methods are lazy and those methods
174
+ # get created when they are invoked for the very first time. However,
175
+ # this can change, for instance, when using nested attributes, which is
176
+ # called _after_ the association has been defined. Since we don't want
177
+ # the callbacks to get defined multiple times, there are guards that
178
+ # check if the save or validation methods have already been defined
179
+ # before actually defining them.
180
+ def add_autosave_association_callbacks(reflection)
181
+ save_method = :"autosave_associated_records_for_#{reflection.name}"
182
+
183
+ if reflection.collection?
189
184
  before_save :before_save_collection_association
185
+ after_save :after_save_collection_association
190
186
 
191
- define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
187
+ define_non_cyclic_method(save_method) { save_collection_association(reflection) }
192
188
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
193
189
  after_create save_method
194
190
  after_update save_method
191
+ elsif reflection.has_one?
192
+ define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
193
+ # Configures two callbacks instead of a single after_save so that
194
+ # the model may rely on their execution order relative to its
195
+ # own callbacks.
196
+ #
197
+ # For example, given that after_creates run before after_saves, if
198
+ # we configured instead an after_save there would be no way to fire
199
+ # a custom after_create callback after the child association gets
200
+ # created.
201
+ after_create save_method
202
+ after_update save_method
195
203
  else
196
- if reflection.macro == :has_one
197
- define_method(save_method) { save_has_one_association(reflection) }
198
- # Configures two callbacks instead of a single after_save so that
199
- # the model may rely on their execution order relative to its
200
- # own callbacks.
201
- #
202
- # For example, given that after_creates run before after_saves, if
203
- # we configured instead an after_save there would be no way to fire
204
- # a custom after_create callback after the child association gets
205
- # created.
206
- after_create save_method
207
- after_update save_method
208
- else
209
- define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
210
- before_save save_method
211
- end
204
+ define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
205
+ before_save save_method
212
206
  end
207
+
208
+ define_autosave_validation_callbacks(reflection)
213
209
  end
214
210
 
215
- if reflection.validate? && !method_defined?(validation_method)
216
- method = (collection ? :validate_collection_association : :validate_single_association)
217
- define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
218
- validate validation_method
211
+ def define_autosave_validation_callbacks(reflection)
212
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
213
+ if reflection.validate? && !method_defined?(validation_method)
214
+ if reflection.collection?
215
+ method = :validate_collection_association
216
+ else
217
+ method = :validate_single_association
218
+ end
219
+
220
+ define_non_cyclic_method(validation_method) { send(method, reflection) }
221
+ validate validation_method
222
+ after_validation :_ensure_no_duplicate_errors
223
+ end
219
224
  end
220
- end
221
225
  end
222
226
 
223
227
  # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
224
228
  def reload(options = nil)
225
229
  @marked_for_destruction = false
230
+ @destroyed_by_association = nil
226
231
  super
227
232
  end
228
233
 
229
- # 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.
230
235
  # This does _not_ actually destroy the record instantly, rather child record will be destroyed
231
236
  # when <tt>parent.save</tt> is called.
232
237
  #
@@ -235,106 +240,159 @@ module ActiveRecord
235
240
  @marked_for_destruction = true
236
241
  end
237
242
 
238
- # 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.
239
244
  #
240
245
  # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
241
246
  def marked_for_destruction?
242
247
  @marked_for_destruction
243
248
  end
244
249
 
250
+ # Records the association that is being destroyed and destroying this
251
+ # record in the process.
252
+ def destroyed_by_association=(reflection)
253
+ @destroyed_by_association = reflection
254
+ end
255
+
256
+ # Returns the association for the parent being destroyed.
257
+ #
258
+ # Used to avoid updating the counter cache unnecessarily.
259
+ def destroyed_by_association
260
+ @destroyed_by_association
261
+ end
262
+
245
263
  # Returns whether or not this record has been changed in any way (including whether
246
264
  # any of its nested autosave associations are likewise changed)
247
265
  def changed_for_autosave?
248
- 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?
249
267
  end
250
268
 
251
269
  private
252
270
 
253
- # Returns the record for an association collection that should be validated
254
- # or saved. If +autosave+ is +false+ only new records will be returned,
255
- # unless the parent is/was a new record itself.
256
- def associated_records_to_validate_or_save(association, new_record, autosave)
257
- if new_record
258
- association && association.target
259
- elsif autosave
260
- association.target.find_all { |record| record.changed_for_autosave? }
261
- else
262
- association.target.find_all { |record| record.new_record? }
271
+ # Returns the record for an association collection that should be validated
272
+ # or saved. If +autosave+ is +false+ only new records will be returned,
273
+ # unless the parent is/was a new record itself.
274
+ def associated_records_to_validate_or_save(association, new_record, autosave)
275
+ if new_record || custom_validation_context?
276
+ association && association.target
277
+ elsif autosave
278
+ association.target.find_all(&:changed_for_autosave?)
279
+ else
280
+ association.target.find_all(&:new_record?)
281
+ end
263
282
  end
264
- end
265
283
 
266
- # go through nested autosave associations that are loaded in memory (without loading
267
- # any new ones), and return true if is changed for autosave
268
- def nested_records_changed_for_autosave?
269
- self.class.reflect_on_all_autosave_associations.any? do |reflection|
270
- association = association_instance_get(reflection.name)
271
- association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
284
+ # go through nested autosave associations that are loaded in memory (without loading
285
+ # any new ones), and return true if is changed for autosave
286
+ def nested_records_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
296
+ end
297
+ ensure
298
+ @_nested_records_changed_for_autosave_already_called = false
299
+ end
272
300
  end
273
- end
274
301
 
275
- # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
276
- # turned on for the association.
277
- def validate_single_association(reflection)
278
- association = association_instance_get(reflection.name)
279
- record = association && association.reader
280
- association_valid?(reflection, record) if record
281
- end
302
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
303
+ # turned on for the association.
304
+ def validate_single_association(reflection)
305
+ association = association_instance_get(reflection.name)
306
+ record = association && association.reader
307
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
308
+ end
282
309
 
283
- # Validate the associated records if <tt>:validate</tt> or
284
- # <tt>:autosave</tt> is turned on for the association specified by
285
- # +reflection+.
286
- def validate_collection_association(reflection)
287
- if association = association_instance_get(reflection.name)
288
- if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
289
- records.each { |record| association_valid?(reflection, record) }
310
+ # Validate the associated records if <tt>:validate</tt> or
311
+ # <tt>:autosave</tt> is turned on for the association specified by
312
+ # +reflection+.
313
+ def validate_collection_association(reflection)
314
+ if association = association_instance_get(reflection.name)
315
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
316
+ records.each_with_index { |record, index| association_valid?(reflection, record, index) }
317
+ end
290
318
  end
291
319
  end
292
- end
293
320
 
294
- # Returns whether or not the association is valid and applies any errors to
295
- # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
296
- # enabled records if they're marked_for_destruction? or destroyed.
297
- def association_valid?(reflection, record)
298
- return true if record.destroyed? || record.marked_for_destruction?
299
-
300
- unless valid = record.valid?
301
- if reflection.options[:autosave]
302
- record.errors.each do |attribute, message|
303
- attribute = "#{reflection.name}.#{attribute}"
304
- errors[attribute] << message
305
- errors[attribute].uniq!
321
+ # Returns whether or not the association is valid and applies any errors to
322
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
323
+ # enabled records if they're marked_for_destruction? or destroyed.
324
+ def association_valid?(reflection, record, index = nil)
325
+ return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
326
+
327
+ context = validation_context if custom_validation_context?
328
+
329
+ unless valid = record.valid?(context)
330
+ if reflection.options[:autosave]
331
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
332
+
333
+ record.errors.each do |attribute, message|
334
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
335
+ errors[attribute] << message
336
+ errors[attribute].uniq!
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
348
+ else
349
+ errors.add(reflection.name)
306
350
  end
351
+ end
352
+ valid
353
+ end
354
+
355
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
356
+ if indexed_attribute
357
+ "#{reflection.name}[#{index}].#{attribute}"
307
358
  else
308
- errors.add(reflection.name)
359
+ "#{reflection.name}.#{attribute}"
309
360
  end
310
361
  end
311
- valid
312
- end
313
362
 
314
- # Is used as a before_save callback to check while saving a collection
315
- # association whether or not the parent was a new record before saving.
316
- def before_save_collection_association
317
- @new_record_before_save = new_record?
318
- true
319
- end
363
+ # Is used as a before_save callback to check while saving a collection
364
+ # association whether or not the parent was a new record before saving.
365
+ def before_save_collection_association
366
+ @new_record_before_save = new_record?
367
+ end
320
368
 
321
- # Saves any new associated records, or all loaded autosave associations if
322
- # <tt>:autosave</tt> is enabled on the association.
323
- #
324
- # In addition, it destroys all children that were marked for destruction
325
- # with mark_for_destruction.
326
- #
327
- # This all happens inside a transaction, _if_ the Transactions module is included into
328
- # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
329
- def save_collection_association(reflection)
330
- if association = association_instance_get(reflection.name)
331
- autosave = reflection.options[:autosave]
332
-
333
- if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
334
- begin
369
+ def after_save_collection_association
370
+ @new_record_before_save = false
371
+ end
372
+
373
+ # Saves any new associated records, or all loaded autosave associations if
374
+ # <tt>:autosave</tt> is enabled on the association.
375
+ #
376
+ # In addition, it destroys all children that were marked for destruction
377
+ # with #mark_for_destruction.
378
+ #
379
+ # This all happens inside a transaction, _if_ the Transactions module is included into
380
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
381
+ def save_collection_association(reflection)
382
+ if association = association_instance_get(reflection.name)
383
+ autosave = reflection.options[:autosave]
384
+
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)
335
393
  if autosave
336
394
  records_to_destroy = records.select(&:marked_for_destruction?)
337
- records_to_destroy.each { |record| association.proxy.destroy(record) }
395
+ records_to_destroy.each { |record| association.destroy(record) }
338
396
  records -= records_to_destroy
339
397
  end
340
398
 
@@ -343,83 +401,112 @@ module ActiveRecord
343
401
 
344
402
  saved = true
345
403
 
346
- if autosave != false && (@new_record_before_save || record.new_record?)
404
+ if autosave != false && (new_record_before_save || record.new_record?)
347
405
  if autosave
348
406
  saved = association.insert_record(record, false)
349
- else
350
- 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
351
414
  end
352
415
  elsif autosave
353
- saved = record.save(:validate => false)
416
+ saved = record.save(validate: false)
354
417
  end
355
418
 
356
419
  raise ActiveRecord::Rollback unless saved
357
420
  end
358
- rescue
359
- records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled?
360
- raise
361
421
  end
362
422
  end
423
+ end
424
+
425
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
426
+ # on the association.
427
+ #
428
+ # In addition, it will destroy the association if it was marked for
429
+ # destruction with #mark_for_destruction.
430
+ #
431
+ # This all happens inside a transaction, _if_ the Transactions module is included into
432
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
433
+ def save_has_one_association(reflection)
434
+ association = association_instance_get(reflection.name)
435
+ record = association && association.load_target
436
+
437
+ if record && !record.destroyed?
438
+ autosave = reflection.options[:autosave]
439
+
440
+ if autosave && record.marked_for_destruction?
441
+ record.destroy
442
+ elsif autosave != false
443
+ key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
444
+
445
+ if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
446
+ unless reflection.through_reflection
447
+ record[reflection.foreign_key] = key
448
+ if inverse_reflection = reflection.inverse_of
449
+ record.association(inverse_reflection.name).loaded!
450
+ end
451
+ end
452
+
453
+ saved = record.save(validate: !autosave)
454
+ raise ActiveRecord::Rollback if !saved && autosave
455
+ saved
456
+ end
457
+ end
458
+ end
459
+ end
363
460
 
364
- # reconstruct the scope now that we know the owner's id
365
- association.reset_scope if association.respond_to?(:reset_scope)
461
+ # If the record is new or it has changed, returns true.
462
+ def record_changed?(reflection, record, key)
463
+ record.new_record? ||
464
+ association_foreign_key_changed?(reflection, record, key) ||
465
+ record.will_save_change_to_attribute?(reflection.foreign_key)
366
466
  end
367
- end
368
467
 
369
- # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
370
- # on the association.
371
- #
372
- # In addition, it will destroy the association if it was marked for
373
- # destruction with mark_for_destruction.
374
- #
375
- # This all happens inside a transaction, _if_ the Transactions module is included into
376
- # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
377
- def save_has_one_association(reflection)
378
- association = association_instance_get(reflection.name)
379
- record = association && association.load_target
380
- if record && !record.destroyed?
381
- autosave = reflection.options[:autosave]
382
-
383
- if autosave && record.marked_for_destruction?
384
- record.destroy
385
- else
386
- key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
387
- if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
388
- unless reflection.through_reflection
389
- record[reflection.foreign_key] = key
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
472
+ end
473
+
474
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
475
+ #
476
+ # In addition, it will destroy the association if it was marked for destruction.
477
+ def save_belongs_to_association(reflection)
478
+ association = association_instance_get(reflection.name)
479
+ return unless association && association.loaded? && !association.stale_target?
480
+
481
+ record = association.load_target
482
+ if record && !record.destroyed?
483
+ autosave = reflection.options[:autosave]
484
+
485
+ if autosave && record.marked_for_destruction?
486
+ self[reflection.foreign_key] = nil
487
+ record.destroy
488
+ elsif autosave != false
489
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
490
+
491
+ if association.updated?
492
+ association_id = record.send(reflection.options[:primary_key] || :id)
493
+ self[reflection.foreign_key] = association_id
494
+ association.loaded!
390
495
  end
391
496
 
392
- saved = record.save(:validate => !autosave)
393
- raise ActiveRecord::Rollback if !saved && autosave
394
- saved
497
+ saved if autosave
395
498
  end
396
499
  end
397
500
  end
398
- end
399
501
 
400
- # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
401
- #
402
- # In addition, it will destroy the association if it was marked for destruction.
403
- def save_belongs_to_association(reflection)
404
- association = association_instance_get(reflection.name)
405
- record = association && association.load_target
406
- if record && !record.destroyed?
407
- autosave = reflection.options[:autosave]
408
-
409
- if autosave && record.marked_for_destruction?
410
- record.destroy
411
- elsif autosave != false
412
- saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
413
-
414
- if association.updated?
415
- association_id = record.send(reflection.options[:primary_key] || :id)
416
- self[reflection.foreign_key] = association_id
417
- association.loaded!
418
- end
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
419
505
 
420
- saved if autosave
506
+ def _ensure_no_duplicate_errors
507
+ errors.messages.each_key do |attribute|
508
+ errors[attribute].uniq!
421
509
  end
422
510
  end
423
- end
424
511
  end
425
512
  end