activerecord 3.2.6 → 6.0.0

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