activerecord 4.2.11.1 → 6.0.3

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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +711 -1547
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +266 -251
  9. data/lib/active_record/association_relation.rb +20 -13
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +128 -57
  12. data/lib/active_record/associations/association_scope.rb +103 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -66
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +136 -288
  23. data/lib/active_record/associations/collection_proxy.rb +241 -147
  24. data/lib/active_record/associations/foreign_association.rb +10 -1
  25. data/lib/active_record/associations/has_many_association.rb +34 -98
  26. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  27. data/lib/active_record/associations/has_one_association.rb +61 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +149 -166
  33. data/lib/active_record/associations/preloader/association.rb +90 -123
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +90 -93
  36. data/lib/active_record/associations/singular_association.rb +18 -39
  37. data/lib/active_record/associations/through_association.rb +38 -18
  38. data/lib/active_record/associations.rb +1737 -1597
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +57 -37
  48. data/lib/active_record/attribute_methods/write.rb +32 -55
  49. data/lib/active_record/attribute_methods.rb +120 -135
  50. data/lib/active_record/attributes.rb +213 -82
  51. data/lib/active_record/autosave_association.rb +97 -41
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +23 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +804 -297
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +240 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +371 -242
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +694 -256
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +473 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +507 -639
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -181
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -114
  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 +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  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 +4 -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 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  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 +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +51 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  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 +144 -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 +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -296
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -349
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +252 -230
  130. data/lib/active_record/counter_cache.rb +70 -49
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +227 -501
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +86 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +623 -305
  165. data/lib/active_record/model_schema.rb +313 -112
  166. data/lib/active_record/nested_attributes.rb +263 -223
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +557 -126
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +430 -281
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +268 -254
  182. data/lib/active_record/relation/delegation.rb +75 -84
  183. data/lib/active_record/relation/finder_methods.rb +285 -241
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +78 -88
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +110 -119
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +603 -397
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +11 -14
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +530 -341
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping/default.rb +98 -83
  208. data/lib/active_record/scoping/named.rb +86 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -100
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +80 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +223 -157
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -45
  228. data/lib/active_record/type/date_time.rb +4 -49
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +23 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +11 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +42 -55
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +42 -22
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. data/lib/rails/generators/active_record.rb +7 -5
  341. metadata +168 -59
  342. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  343. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  344. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  345. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  346. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  347. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  348. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  349. data/lib/active_record/attribute.rb +0 -163
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  353. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  354. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  355. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  356. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  357. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  358. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  359. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  360. data/lib/active_record/type/big_integer.rb +0 -13
  361. data/lib/active_record/type/binary.rb +0 -50
  362. data/lib/active_record/type/boolean.rb +0 -31
  363. data/lib/active_record/type/decimal.rb +0 -64
  364. data/lib/active_record/type/decorator.rb +0 -14
  365. data/lib/active_record/type/float.rb +0 -19
  366. data/lib/active_record/type/integer.rb +0 -59
  367. data/lib/active_record/type/mutable.rb +0 -16
  368. data/lib/active_record/type/numeric.rb +0 -36
  369. data/lib/active_record/type/string.rb +0 -40
  370. data/lib/active_record/type/time_value.rb +0 -38
  371. data/lib/active_record/type/value.rb +0 -110
  372. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  373. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,269 +1,284 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Aggregations
3
- module Aggregations # :nodoc:
4
- extend ActiveSupport::Concern
4
+ # See ActiveRecord::Aggregations::ClassMethods for documentation
5
+ module Aggregations
6
+ def initialize_dup(*) # :nodoc:
7
+ @aggregation_cache = {}
8
+ super
9
+ end
5
10
 
6
- def clear_aggregation_cache #:nodoc:
7
- @aggregation_cache.clear if persisted?
11
+ def reload(*) # :nodoc:
12
+ clear_aggregation_cache
13
+ super
8
14
  end
9
15
 
10
- # Active Record implements aggregation through a macro-like class method called +composed_of+
11
- # for representing attributes as value objects. It expresses relationships like "Account [is]
12
- # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
13
- # to the macro adds a description of how the value objects are created from the attributes of
14
- # the entity object (when the entity is initialized either as a new object or from finding an
15
- # existing object) and how it can be turned back into attributes (when the entity is saved to
16
- # the database).
17
- #
18
- # class Customer < ActiveRecord::Base
19
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
20
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
21
- # end
22
- #
23
- # The customer class now has the following methods to manipulate the value objects:
24
- # * <tt>Customer#balance, Customer#balance=(money)</tt>
25
- # * <tt>Customer#address, Customer#address=(address)</tt>
26
- #
27
- # These methods will operate with value objects like the ones described below:
28
- #
29
- # class Money
30
- # include Comparable
31
- # attr_reader :amount, :currency
32
- # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
33
- #
34
- # def initialize(amount, currency = "USD")
35
- # @amount, @currency = amount, currency
36
- # end
37
- #
38
- # def exchange_to(other_currency)
39
- # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
40
- # Money.new(exchanged_amount, other_currency)
41
- # end
42
- #
43
- # def ==(other_money)
44
- # amount == other_money.amount && currency == other_money.currency
45
- # end
46
- #
47
- # def <=>(other_money)
48
- # if currency == other_money.currency
49
- # amount <=> other_money.amount
50
- # else
51
- # amount <=> other_money.exchange_to(currency).amount
52
- # end
53
- # end
54
- # end
55
- #
56
- # class Address
57
- # attr_reader :street, :city
58
- # def initialize(street, city)
59
- # @street, @city = street, city
60
- # end
61
- #
62
- # def close_to?(other_address)
63
- # city == other_address.city
64
- # end
65
- #
66
- # def ==(other_address)
67
- # city == other_address.city && street == other_address.street
68
- # end
69
- # end
70
- #
71
- # Now it's possible to access attributes from the database through the value objects instead. If
72
- # you choose to name the composition the same as the attribute's name, it will be the only way to
73
- # access that attribute. That's the case with our +balance+ attribute. You interact with the value
74
- # objects just like you would with any other attribute:
75
- #
76
- # customer.balance = Money.new(20) # sets the Money value object and the attribute
77
- # customer.balance # => Money value object
78
- # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
79
- # customer.balance > Money.new(10) # => true
80
- # customer.balance == Money.new(20) # => true
81
- # customer.balance < Money.new(5) # => false
82
- #
83
- # Value objects can also be composed of multiple attributes, such as the case of Address. The order
84
- # of the mappings will determine the order of the parameters.
85
- #
86
- # customer.address_street = "Hyancintvej"
87
- # customer.address_city = "Copenhagen"
88
- # customer.address # => Address.new("Hyancintvej", "Copenhagen")
89
- #
90
- # customer.address_street = "Vesterbrogade"
91
- # customer.address # => Address.new("Hyancintvej", "Copenhagen")
92
- # customer.clear_aggregation_cache
93
- # customer.address # => Address.new("Vesterbrogade", "Copenhagen")
94
- #
95
- # customer.address = Address.new("May Street", "Chicago")
96
- # customer.address_street # => "May Street"
97
- # customer.address_city # => "Chicago"
98
- #
99
- # == Writing value objects
100
- #
101
- # Value objects are immutable and interchangeable objects that represent a given value, such as
102
- # a Money object representing $5. Two Money objects both representing $5 should be equal (through
103
- # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
104
- # unlike entity objects where equality is determined by identity. An entity class such as Customer can
105
- # easily have two different objects that both have an address on Hyancintvej. Entity identity is
106
- # determined by object or relational unique identifiers (such as primary keys). Normal
107
- # ActiveRecord::Base classes are entity objects.
108
- #
109
- # It's also important to treat the value objects as immutable. Don't allow the Money object to have
110
- # its amount changed after creation. Create a new Money object with the new value instead. The
111
- # Money#exchange_to method is an example of this. It returns a new value object instead of changing
112
- # its own values. Active Record won't persist value objects that have been changed through means
113
- # other than the writer method.
114
- #
115
- # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
116
- # object. Attempting to change it afterwards will result in a RuntimeError.
117
- #
118
- # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
119
- # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
120
- #
121
- # == Custom constructors and converters
122
- #
123
- # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
124
- # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
125
- # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
126
- # a custom constructor to be specified.
127
- #
128
- # When a new value is assigned to the value object, the default assumption is that the new value
129
- # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
130
- # converted to an instance of value class if necessary.
131
- #
132
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
133
- # aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
134
- # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
135
- # New values can be assigned to the value object using either another NetAddr::CIDR object, a string
136
- # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
137
- # these requirements:
138
- #
139
- # class NetworkResource < ActiveRecord::Base
140
- # composed_of :cidr,
141
- # class_name: 'NetAddr::CIDR',
142
- # mapping: [ %w(network_address network), %w(cidr_range bits) ],
143
- # allow_nil: true,
144
- # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
145
- # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
146
- # end
147
- #
148
- # # This calls the :constructor
149
- # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
150
- #
151
- # # These assignments will both use the :converter
152
- # network_resource.cidr = [ '192.168.2.1', 8 ]
153
- # network_resource.cidr = '192.168.0.1/24'
154
- #
155
- # # This assignment won't use the :converter as the value is already an instance of the value class
156
- # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
157
- #
158
- # # Saving and then reloading will use the :constructor on reload
159
- # network_resource.save
160
- # network_resource.reload
161
- #
162
- # == Finding records by a value object
163
- #
164
- # Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
165
- # by specifying an instance of the value object in the conditions hash. The following example
166
- # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
167
- #
168
- # Customer.where(balance: Money.new(20, "USD"))
169
- #
170
- module ClassMethods
171
- # Adds reader and writer methods for manipulating a value object:
172
- # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
173
- #
174
- # Options are:
175
- # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
176
- # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
177
- # to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
178
- # with this option.
179
- # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
180
- # object. Each mapping is represented as an array where the first item is the name of the
181
- # entity attribute and the second item is the name of the attribute in the value object. The
182
- # order in which mappings are defined determines the order in which attributes are sent to the
183
- # value class constructor.
184
- # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
185
- # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
186
- # mapped attributes.
187
- # This defaults to +false+.
188
- # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
189
- # is called to initialize the value object. The constructor is passed all of the mapped attributes,
190
- # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
191
- # to instantiate a <tt>:class_name</tt> object.
192
- # The default is <tt>:new</tt>.
193
- # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
194
- # or a Proc that is called when a new value is assigned to the value object. The converter is
195
- # passed the single value that is used in the assignment and is only called if the new value is
196
- # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
197
- # can return nil to skip the assignment.
198
- #
199
- # Option examples:
200
- # composed_of :temperature, mapping: %w(reading celsius)
201
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount),
202
- # converter: Proc.new { |balance| balance.to_money }
203
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
204
- # composed_of :gps_location
205
- # composed_of :gps_location, allow_nil: true
206
- # composed_of :ip_address,
207
- # class_name: 'IPAddr',
208
- # mapping: %w(ip to_i),
209
- # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
210
- # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
211
- #
212
- def composed_of(part_id, options = {})
213
- options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
16
+ private
17
+ def clear_aggregation_cache
18
+ @aggregation_cache.clear if persisted?
19
+ end
20
+
21
+ def init_internals
22
+ @aggregation_cache = {}
23
+ super
24
+ end
25
+
26
+ # Active Record implements aggregation through a macro-like class method called #composed_of
27
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
28
+ # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
29
+ # to the macro adds a description of how the value objects are created from the attributes of
30
+ # the entity object (when the entity is initialized either as a new object or from finding an
31
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
32
+ # the database).
33
+ #
34
+ # class Customer < ActiveRecord::Base
35
+ # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
36
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
37
+ # end
38
+ #
39
+ # The customer class now has the following methods to manipulate the value objects:
40
+ # * <tt>Customer#balance, Customer#balance=(money)</tt>
41
+ # * <tt>Customer#address, Customer#address=(address)</tt>
42
+ #
43
+ # These methods will operate with value objects like the ones described below:
44
+ #
45
+ # class Money
46
+ # include Comparable
47
+ # attr_reader :amount, :currency
48
+ # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
49
+ #
50
+ # def initialize(amount, currency = "USD")
51
+ # @amount, @currency = amount, currency
52
+ # end
53
+ #
54
+ # def exchange_to(other_currency)
55
+ # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
56
+ # Money.new(exchanged_amount, other_currency)
57
+ # end
58
+ #
59
+ # def ==(other_money)
60
+ # amount == other_money.amount && currency == other_money.currency
61
+ # end
62
+ #
63
+ # def <=>(other_money)
64
+ # if currency == other_money.currency
65
+ # amount <=> other_money.amount
66
+ # else
67
+ # amount <=> other_money.exchange_to(currency).amount
68
+ # end
69
+ # end
70
+ # end
71
+ #
72
+ # class Address
73
+ # attr_reader :street, :city
74
+ # def initialize(street, city)
75
+ # @street, @city = street, city
76
+ # end
77
+ #
78
+ # def close_to?(other_address)
79
+ # city == other_address.city
80
+ # end
81
+ #
82
+ # def ==(other_address)
83
+ # city == other_address.city && street == other_address.street
84
+ # end
85
+ # end
86
+ #
87
+ # Now it's possible to access attributes from the database through the value objects instead. If
88
+ # you choose to name the composition the same as the attribute's name, it will be the only way to
89
+ # access that attribute. That's the case with our +balance+ attribute. You interact with the value
90
+ # objects just like you would with any other attribute:
91
+ #
92
+ # customer.balance = Money.new(20) # sets the Money value object and the attribute
93
+ # customer.balance # => Money value object
94
+ # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
95
+ # customer.balance > Money.new(10) # => true
96
+ # customer.balance == Money.new(20) # => true
97
+ # customer.balance < Money.new(5) # => false
98
+ #
99
+ # Value objects can also be composed of multiple attributes, such as the case of Address. The order
100
+ # of the mappings will determine the order of the parameters.
101
+ #
102
+ # customer.address_street = "Hyancintvej"
103
+ # customer.address_city = "Copenhagen"
104
+ # customer.address # => Address.new("Hyancintvej", "Copenhagen")
105
+ #
106
+ # customer.address = Address.new("May Street", "Chicago")
107
+ # customer.address_street # => "May Street"
108
+ # customer.address_city # => "Chicago"
109
+ #
110
+ # == Writing value objects
111
+ #
112
+ # Value objects are immutable and interchangeable objects that represent a given value, such as
113
+ # a Money object representing $5. Two Money objects both representing $5 should be equal (through
114
+ # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
115
+ # unlike entity objects where equality is determined by identity. An entity class such as Customer can
116
+ # easily have two different objects that both have an address on Hyancintvej. Entity identity is
117
+ # determined by object or relational unique identifiers (such as primary keys). Normal
118
+ # ActiveRecord::Base classes are entity objects.
119
+ #
120
+ # It's also important to treat the value objects as immutable. Don't allow the Money object to have
121
+ # its amount changed after creation. Create a new Money object with the new value instead. The
122
+ # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
123
+ # its own values. Active Record won't persist value objects that have been changed through means
124
+ # other than the writer method.
125
+ #
126
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
127
+ # object. Attempting to change it afterwards will result in a +RuntimeError+.
128
+ #
129
+ # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
130
+ # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
131
+ #
132
+ # == Custom constructors and converters
133
+ #
134
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
135
+ # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
136
+ # option, as arguments. If the value class doesn't support this convention then #composed_of allows
137
+ # a custom constructor to be specified.
138
+ #
139
+ # When a new value is assigned to the value object, the default assumption is that the new value
140
+ # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
141
+ # converted to an instance of value class if necessary.
142
+ #
143
+ # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
144
+ # aggregated using the +NetAddr::CIDR+ value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
145
+ # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
146
+ # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
147
+ # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
148
+ # these requirements:
149
+ #
150
+ # class NetworkResource < ActiveRecord::Base
151
+ # composed_of :cidr,
152
+ # class_name: 'NetAddr::CIDR',
153
+ # mapping: [ %w(network_address network), %w(cidr_range bits) ],
154
+ # allow_nil: true,
155
+ # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
156
+ # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
157
+ # end
158
+ #
159
+ # # This calls the :constructor
160
+ # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
161
+ #
162
+ # # These assignments will both use the :converter
163
+ # network_resource.cidr = [ '192.168.2.1', 8 ]
164
+ # network_resource.cidr = '192.168.0.1/24'
165
+ #
166
+ # # This assignment won't use the :converter as the value is already an instance of the value class
167
+ # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
168
+ #
169
+ # # Saving and then reloading will use the :constructor on reload
170
+ # network_resource.save
171
+ # network_resource.reload
172
+ #
173
+ # == Finding records by a value object
174
+ #
175
+ # Once a #composed_of relationship is specified for a model, records can be loaded from the database
176
+ # by specifying an instance of the value object in the conditions hash. The following example
177
+ # finds all customers with +address_street+ equal to "May Street" and +address_city+ equal to "Chicago":
178
+ #
179
+ # Customer.where(address: Address.new("May Street", "Chicago"))
180
+ #
181
+ module ClassMethods
182
+ # Adds reader and writer methods for manipulating a value object:
183
+ # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
184
+ #
185
+ # Options are:
186
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
187
+ # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
188
+ # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
189
+ # with this option.
190
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
191
+ # object. Each mapping is represented as an array where the first item is the name of the
192
+ # entity attribute and the second item is the name of the attribute in the value object. The
193
+ # order in which mappings are defined determines the order in which attributes are sent to the
194
+ # value class constructor.
195
+ # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
196
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
197
+ # mapped attributes.
198
+ # This defaults to +false+.
199
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
200
+ # is called to initialize the value object. The constructor is passed all of the mapped attributes,
201
+ # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
202
+ # to instantiate a <tt>:class_name</tt> object.
203
+ # The default is <tt>:new</tt>.
204
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
205
+ # or a Proc that is called when a new value is assigned to the value object. The converter is
206
+ # passed the single value that is used in the assignment and is only called if the new value is
207
+ # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
208
+ # can return +nil+ to skip the assignment.
209
+ #
210
+ # Option examples:
211
+ # composed_of :temperature, mapping: %w(reading celsius)
212
+ # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
213
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
214
+ # composed_of :gps_location
215
+ # composed_of :gps_location, allow_nil: true
216
+ # composed_of :ip_address,
217
+ # class_name: 'IPAddr',
218
+ # mapping: %w(ip to_i),
219
+ # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
220
+ # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
221
+ #
222
+ def composed_of(part_id, options = {})
223
+ options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
224
+
225
+ unless self < Aggregations
226
+ include Aggregations
227
+ end
214
228
 
215
- name = part_id.id2name
216
- class_name = options[:class_name] || name.camelize
217
- mapping = options[:mapping] || [ name, name ]
218
- mapping = [ mapping ] unless mapping.first.is_a?(Array)
219
- allow_nil = options[:allow_nil] || false
220
- constructor = options[:constructor] || :new
221
- converter = options[:converter]
229
+ name = part_id.id2name
230
+ class_name = options[:class_name] || name.camelize
231
+ mapping = options[:mapping] || [ name, name ]
232
+ mapping = [ mapping ] unless mapping.first.is_a?(Array)
233
+ allow_nil = options[:allow_nil] || false
234
+ constructor = options[:constructor] || :new
235
+ converter = options[:converter]
222
236
 
223
- reader_method(name, class_name, mapping, allow_nil, constructor)
224
- writer_method(name, class_name, mapping, allow_nil, converter)
237
+ reader_method(name, class_name, mapping, allow_nil, constructor)
238
+ writer_method(name, class_name, mapping, allow_nil, converter)
225
239
 
226
- reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
227
- Reflection.add_aggregate_reflection self, part_id, reflection
228
- end
240
+ reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
241
+ Reflection.add_aggregate_reflection self, part_id, reflection
242
+ end
229
243
 
230
- private
231
- def reader_method(name, class_name, mapping, allow_nil, constructor)
232
- define_method(name) do
233
- if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
234
- attrs = mapping.collect {|key, _| _read_attribute(key)}
235
- object = constructor.respond_to?(:call) ?
236
- constructor.call(*attrs) :
237
- class_name.constantize.send(constructor, *attrs)
238
- @aggregation_cache[name] = object
244
+ private
245
+ def reader_method(name, class_name, mapping, allow_nil, constructor)
246
+ define_method(name) do
247
+ if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !_read_attribute(key).nil? })
248
+ attrs = mapping.collect { |key, _| _read_attribute(key) }
249
+ object = constructor.respond_to?(:call) ?
250
+ constructor.call(*attrs) :
251
+ class_name.constantize.send(constructor, *attrs)
252
+ @aggregation_cache[name] = object
253
+ end
254
+ @aggregation_cache[name]
239
255
  end
240
- @aggregation_cache[name]
241
256
  end
242
- end
243
257
 
244
- def writer_method(name, class_name, mapping, allow_nil, converter)
245
- define_method("#{name}=") do |part|
246
- klass = class_name.constantize
258
+ def writer_method(name, class_name, mapping, allow_nil, converter)
259
+ define_method("#{name}=") do |part|
260
+ klass = class_name.constantize
247
261
 
248
- unless part.is_a?(klass) || converter.nil? || part.nil?
249
- part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
250
- end
262
+ unless part.is_a?(klass) || converter.nil? || part.nil?
263
+ part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
264
+ end
251
265
 
252
- hash_from_multiparameter_assignment = part.is_a?(Hash) &&
253
- part.each_key.all? { |k| k.is_a?(Integer) }
254
- if hash_from_multiparameter_assignment
255
- part = klass.new(*part.values)
256
- end
266
+ hash_from_multiparameter_assignment = part.is_a?(Hash) &&
267
+ part.each_key.all? { |k| k.is_a?(Integer) }
268
+ if hash_from_multiparameter_assignment
269
+ raise ArgumentError unless part.size == part.each_key.max
270
+ part = klass.new(*part.sort.map(&:last))
271
+ end
257
272
 
258
- if part.nil? && allow_nil
259
- mapping.each { |key, _| self[key] = nil }
260
- @aggregation_cache[name] = nil
261
- else
262
- mapping.each { |key, value| self[key] = part.send(value) }
263
- @aggregation_cache[name] = part.freeze
273
+ if part.nil? && allow_nil
274
+ mapping.each { |key, _| self[key] = nil }
275
+ @aggregation_cache[name] = nil
276
+ else
277
+ mapping.each { |key, value| self[key] = part.send(value) }
278
+ @aggregation_cache[name] = part.freeze
279
+ end
264
280
  end
265
281
  end
266
- end
267
- end
282
+ end
268
283
  end
269
284
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class AssociationRelation < Relation
3
- def initialize(klass, table, association)
4
- super(klass, table)
5
+ def initialize(klass, association, **)
6
+ super(klass)
5
7
  @association = association
6
8
  end
7
9
 
@@ -10,26 +12,31 @@ module ActiveRecord
10
12
  end
11
13
 
12
14
  def ==(other)
13
- other == to_a
15
+ other == records
14
16
  end
15
17
 
16
- def build(*args, &block)
17
- scoping { @association.build(*args, &block) }
18
+ def build(attributes = nil, &block)
19
+ block = _deprecated_scope_block("new", &block)
20
+ scoping { @association.build(attributes, &block) }
18
21
  end
19
22
  alias new build
20
23
 
21
- def create(*args, &block)
22
- scoping { @association.create(*args, &block) }
24
+ def create(attributes = nil, &block)
25
+ block = _deprecated_scope_block("create", &block)
26
+ scoping { @association.create(attributes, &block) }
23
27
  end
24
28
 
25
- def create!(*args, &block)
26
- scoping { @association.create!(*args, &block) }
29
+ def create!(attributes = nil, &block)
30
+ block = _deprecated_scope_block("create!", &block)
31
+ scoping { @association.create!(attributes, &block) }
27
32
  end
28
33
 
29
34
  private
30
-
31
- def exec_queries
32
- super.each { |r| @association.set_inverse_instance r }
33
- end
35
+ def exec_queries
36
+ super do |record|
37
+ @association.set_inverse_instance_from_queries(record)
38
+ yield record if block_given?
39
+ end
40
+ end
34
41
  end
35
42
  end