omg-activerecord 8.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "benchmark/ips"
5
+
6
+ TIME = (ENV["BENCHMARK_TIME"] || 20).to_i
7
+ RECORDS = (ENV["BENCHMARK_RECORDS"] || TIME * 1000).to_i
8
+
9
+ conn = { adapter: "sqlite3", database: ":memory:" }
10
+
11
+ ActiveRecord::Base.establish_connection(conn)
12
+
13
+ class User < ActiveRecord::Base
14
+ connection.create_table :users, force: true do |t|
15
+ t.string :name, :email
16
+ t.timestamps
17
+ end
18
+
19
+ has_many :exhibits
20
+ end
21
+
22
+ class Exhibit < ActiveRecord::Base
23
+ connection.create_table :exhibits, force: true do |t|
24
+ t.belongs_to :user
25
+ t.string :name
26
+ t.text :notes
27
+ t.timestamps
28
+ end
29
+
30
+ belongs_to :user
31
+
32
+ def look; attributes end
33
+ def feel; look; user.name end
34
+
35
+ def self.with_name
36
+ where("name IS NOT NULL")
37
+ end
38
+
39
+ def self.with_notes
40
+ where("notes IS NOT NULL")
41
+ end
42
+
43
+ def self.look(exhibits) exhibits.each(&:look) end
44
+ def self.feel(exhibits) exhibits.each(&:feel) end
45
+ end
46
+
47
+ def progress_bar(int); print "." if (int % 100).zero? ; end
48
+
49
+ puts "Generating data..."
50
+
51
+ module ActiveRecord
52
+ class Faker
53
+ LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
54
+ Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
55
+ Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
56
+ tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
57
+ varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
58
+ tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
59
+ Praesent varius tincidunt commodo".split
60
+
61
+ def self.name
62
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join " "
63
+ end
64
+
65
+ def self.email
66
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join("@") + ".com"
67
+ end
68
+ end
69
+ end
70
+
71
+ # pre-compute the insert statements and fake data compilation,
72
+ # so the benchmarks below show the actual runtime for the execute
73
+ # method, minus the setup steps
74
+
75
+ # Using the same paragraph for all exhibits because it is very slow
76
+ # to generate unique paragraphs for all exhibits.
77
+ notes = ActiveRecord::Faker::LOREM.join " "
78
+ today = Date.today
79
+
80
+ puts "Inserting #{RECORDS} users and exhibits..."
81
+ RECORDS.times do |record|
82
+ user = User.create(
83
+ created_at: today,
84
+ name: ActiveRecord::Faker.name,
85
+ email: ActiveRecord::Faker.email
86
+ )
87
+
88
+ Exhibit.create(
89
+ created_at: today,
90
+ name: ActiveRecord::Faker.name,
91
+ user: user,
92
+ notes: notes
93
+ )
94
+ progress_bar(record)
95
+ end
96
+ puts "Done!\n"
97
+
98
+ Benchmark.ips(TIME) do |x|
99
+ ar_obj = Exhibit.find(1)
100
+ attrs = { name: "sam" }
101
+ attrs_first = { name: "sam" }
102
+ attrs_second = { name: "tom" }
103
+ exhibit = {
104
+ name: ActiveRecord::Faker.name,
105
+ notes: notes,
106
+ created_at: Date.today
107
+ }
108
+
109
+ x.report("Model#id") do
110
+ ar_obj.id
111
+ end
112
+
113
+ x.report "Model.new (instantiation)" do
114
+ Exhibit.new
115
+ end
116
+
117
+ x.report "Model.new (setting attributes)" do
118
+ Exhibit.new(attrs)
119
+ end
120
+
121
+ x.report "Model.first" do
122
+ Exhibit.first.look
123
+ end
124
+
125
+ x.report "Model.take" do
126
+ Exhibit.take
127
+ end
128
+
129
+ x.report("Model.all limit(100)") do
130
+ Exhibit.look Exhibit.limit(100)
131
+ end
132
+
133
+ x.report("Model.all take(100)") do
134
+ Exhibit.look Exhibit.take(100)
135
+ end
136
+
137
+ x.report "Model.all limit(100) with relationship" do
138
+ Exhibit.feel Exhibit.limit(100).includes(:user)
139
+ end
140
+
141
+ x.report "Model.all limit(10,000)" do
142
+ Exhibit.look Exhibit.limit(10000)
143
+ end
144
+
145
+ x.report "Model.named_scope" do
146
+ Exhibit.limit(10).with_name.with_notes
147
+ end
148
+
149
+ x.report "Model.create" do
150
+ Exhibit.create(exhibit)
151
+ end
152
+
153
+ x.report "Resource#attributes=" do
154
+ e = Exhibit.new(attrs_first)
155
+ e.attributes = attrs_second
156
+ end
157
+
158
+ x.report "Resource#update" do
159
+ Exhibit.first.update(name: "bob")
160
+ end
161
+
162
+ x.report "Resource#destroy" do
163
+ Exhibit.first.destroy
164
+ end
165
+
166
+ x.report "Model.transaction" do
167
+ Exhibit.transaction { Exhibit.new }
168
+ end
169
+
170
+ x.report "Model.find(id)" do
171
+ User.find(1)
172
+ end
173
+
174
+ x.report "Model.find_by_sql" do
175
+ Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
176
+ end
177
+
178
+ x.report "Model.log" do
179
+ Exhibit.lease_connection.send(:log, "hello", "world") { }
180
+ end
181
+
182
+ x.report "AR.execute(query)" do
183
+ ActiveRecord::Base.lease_connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
184
+ end
185
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ class Person < ActiveRecord::Base
6
+ establish_connection adapter: "sqlite3", database: "foobar.db"
7
+ connection.create_table table_name, force: true do |t|
8
+ t.string :name
9
+ end
10
+ end
11
+
12
+ bob = Person.create!(name: "bob")
13
+ puts Person.all.inspect
14
+ bob.destroy
15
+ puts Person.all.inspect
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # See ActiveRecord::Aggregations::ClassMethods for documentation
5
+ module Aggregations
6
+ def initialize_dup(*) # :nodoc:
7
+ @aggregation_cache = @aggregation_cache.dup
8
+ super
9
+ end
10
+
11
+ def reload(*) # :nodoc:
12
+ clear_aggregation_cache
13
+ super
14
+ end
15
+
16
+ private
17
+ def clear_aggregation_cache
18
+ @aggregation_cache.clear if persisted?
19
+ end
20
+
21
+ def init_internals
22
+ super
23
+ @aggregation_cache = {}
24
+ end
25
+
26
+ # = Active Record \Aggregations
27
+ #
28
+ # Active Record implements aggregation through a macro-like class method called #composed_of
29
+ # for representing attributes as value objects. It expresses relationships like "Account [is]
30
+ # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
31
+ # to the macro adds a description of how the value objects are created from the attributes of
32
+ # the entity object (when the entity is initialized either as a new object or from finding an
33
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
34
+ # the database).
35
+ #
36
+ # class Customer < ActiveRecord::Base
37
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
38
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
39
+ # end
40
+ #
41
+ # The customer class now has the following methods to manipulate the value objects:
42
+ # * <tt>Customer#balance, Customer#balance=(money)</tt>
43
+ # * <tt>Customer#address, Customer#address=(address)</tt>
44
+ #
45
+ # These methods will operate with value objects like the ones described below:
46
+ #
47
+ # class Money
48
+ # include Comparable
49
+ # attr_reader :amount, :currency
50
+ # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
51
+ #
52
+ # def initialize(amount, currency = "USD")
53
+ # @amount, @currency = amount, currency
54
+ # end
55
+ #
56
+ # def exchange_to(other_currency)
57
+ # exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
58
+ # Money.new(exchanged_amount, other_currency)
59
+ # end
60
+ #
61
+ # def ==(other_money)
62
+ # amount == other_money.amount && currency == other_money.currency
63
+ # end
64
+ #
65
+ # def <=>(other_money)
66
+ # if currency == other_money.currency
67
+ # amount <=> other_money.amount
68
+ # else
69
+ # amount <=> other_money.exchange_to(currency).amount
70
+ # end
71
+ # end
72
+ # end
73
+ #
74
+ # class Address
75
+ # attr_reader :street, :city
76
+ # def initialize(street, city)
77
+ # @street, @city = street, city
78
+ # end
79
+ #
80
+ # def close_to?(other_address)
81
+ # city == other_address.city
82
+ # end
83
+ #
84
+ # def ==(other_address)
85
+ # city == other_address.city && street == other_address.street
86
+ # end
87
+ # end
88
+ #
89
+ # Now it's possible to access attributes from the database through the value objects instead. If
90
+ # you choose to name the composition the same as the attribute's name, it will be the only way to
91
+ # access that attribute. That's the case with our +balance+ attribute. You interact with the value
92
+ # objects just like you would with any other attribute:
93
+ #
94
+ # customer.balance = Money.new(20) # sets the Money value object and the attribute
95
+ # customer.balance # => Money value object
96
+ # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
97
+ # customer.balance > Money.new(10) # => true
98
+ # customer.balance == Money.new(20) # => true
99
+ # customer.balance < Money.new(5) # => false
100
+ #
101
+ # Value objects can also be composed of multiple attributes, such as the case of Address. The order
102
+ # of the mappings will determine the order of the parameters.
103
+ #
104
+ # customer.address_street = "Hyancintvej"
105
+ # customer.address_city = "Copenhagen"
106
+ # customer.address # => Address.new("Hyancintvej", "Copenhagen")
107
+ #
108
+ # customer.address = Address.new("May Street", "Chicago")
109
+ # customer.address_street # => "May Street"
110
+ # customer.address_city # => "Chicago"
111
+ #
112
+ # == Writing value objects
113
+ #
114
+ # Value objects are immutable and interchangeable objects that represent a given value, such as
115
+ # a Money object representing $5. Two Money objects both representing $5 should be equal (through
116
+ # methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
117
+ # unlike entity objects where equality is determined by identity. An entity class such as Customer can
118
+ # easily have two different objects that both have an address on Hyancintvej. Entity identity is
119
+ # determined by object or relational unique identifiers (such as primary keys). Normal
120
+ # ActiveRecord::Base classes are entity objects.
121
+ #
122
+ # It's also important to treat the value objects as immutable. Don't allow the Money object to have
123
+ # its amount changed after creation. Create a new Money object with the new value instead. The
124
+ # <tt>Money#exchange_to</tt> method is an example of this. It returns a new value object instead of changing
125
+ # its own values. Active Record won't persist value objects that have been changed through means
126
+ # other than the writer method.
127
+ #
128
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
129
+ # object. Attempting to change it afterwards will result in a +RuntimeError+.
130
+ #
131
+ # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
132
+ # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
133
+ #
134
+ # == Custom constructors and converters
135
+ #
136
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value
137
+ # class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
138
+ # option, as arguments. If the value class doesn't support this convention then #composed_of allows
139
+ # a custom constructor to be specified.
140
+ #
141
+ # When a new value is assigned to the value object, the default assumption is that the new value
142
+ # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
143
+ # converted to an instance of value class if necessary.
144
+ #
145
+ # For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
146
+ # aggregated using the +NetAddr::CIDR+ value class (https://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
147
+ # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
148
+ # New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
149
+ # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
150
+ # these requirements:
151
+ #
152
+ # class NetworkResource < ActiveRecord::Base
153
+ # composed_of :cidr,
154
+ # class_name: 'NetAddr::CIDR',
155
+ # mapping: { network_address: :network, cidr_range: :bits },
156
+ # allow_nil: true,
157
+ # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
158
+ # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
159
+ # end
160
+ #
161
+ # # This calls the :constructor
162
+ # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
163
+ #
164
+ # # These assignments will both use the :converter
165
+ # network_resource.cidr = [ '192.168.2.1', 8 ]
166
+ # network_resource.cidr = '192.168.0.1/24'
167
+ #
168
+ # # This assignment won't use the :converter as the value is already an instance of the value class
169
+ # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
170
+ #
171
+ # # Saving and then reloading will use the :constructor on reload
172
+ # network_resource.save
173
+ # network_resource.reload
174
+ #
175
+ # == Finding records by a value object
176
+ #
177
+ # Once a #composed_of relationship is specified for a model, records can be loaded from the database
178
+ # by specifying an instance of the value object in the conditions hash. The following example
179
+ # finds all customers with +address_street+ equal to "May Street" and +address_city+ equal to "Chicago":
180
+ #
181
+ # Customer.where(address: Address.new("May Street", "Chicago"))
182
+ #
183
+ module ClassMethods
184
+ # Adds reader and writer methods for manipulating a value object:
185
+ # <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
186
+ #
187
+ # Options are:
188
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
189
+ # can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
190
+ # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
191
+ # with this option.
192
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
193
+ # object. Each mapping is represented as a key-value pair where the key is the name of the
194
+ # entity attribute and the value is the name of the attribute in the value object. The
195
+ # order in which mappings are defined determines the order in which attributes are sent to the
196
+ # value class constructor. The mapping can be written as a hash or as an array of pairs.
197
+ # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
198
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
199
+ # mapped attributes.
200
+ # This defaults to +false+.
201
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
202
+ # is called to initialize the value object. The constructor is passed all of the mapped attributes,
203
+ # in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
204
+ # to instantiate a <tt>:class_name</tt> object.
205
+ # The default is <tt>:new</tt>.
206
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
207
+ # or a Proc that is called when a new value is assigned to the value object. The converter is
208
+ # passed the single value that is used in the assignment and is only called if the new value is
209
+ # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
210
+ # can return +nil+ to skip the assignment.
211
+ #
212
+ # Option examples:
213
+ # composed_of :temperature, mapping: { reading: :celsius }
214
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
215
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
216
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
217
+ # composed_of :gps_location
218
+ # composed_of :gps_location, allow_nil: true
219
+ # composed_of :ip_address,
220
+ # class_name: 'IPAddr',
221
+ # mapping: { ip: :to_i },
222
+ # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
223
+ # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
224
+ #
225
+ def composed_of(part_id, options = {})
226
+ options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
227
+
228
+ unless self < Aggregations
229
+ include Aggregations
230
+ end
231
+
232
+ name = part_id.id2name
233
+ class_name = options[:class_name] || name.camelize
234
+ mapping = options[:mapping] || [ name, name ]
235
+ mapping = [ mapping ] unless mapping.first.is_a?(Array)
236
+ allow_nil = options[:allow_nil] || false
237
+ constructor = options[:constructor] || :new
238
+ converter = options[:converter]
239
+
240
+ reader_method(name, class_name, mapping, allow_nil, constructor)
241
+ writer_method(name, class_name, mapping, allow_nil, converter)
242
+
243
+ reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
244
+ Reflection.add_aggregate_reflection self, part_id, reflection
245
+ end
246
+
247
+ private
248
+ def reader_method(name, class_name, mapping, allow_nil, constructor)
249
+ define_method(name) do
250
+ if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !read_attribute(key).nil? })
251
+ attrs = mapping.collect { |key, _| read_attribute(key) }
252
+ object = constructor.respond_to?(:call) ?
253
+ constructor.call(*attrs) :
254
+ class_name.constantize.send(constructor, *attrs)
255
+ @aggregation_cache[name] = object.freeze
256
+ end
257
+ @aggregation_cache[name]
258
+ end
259
+ end
260
+
261
+ def writer_method(name, class_name, mapping, allow_nil, converter)
262
+ define_method("#{name}=") do |part|
263
+ klass = class_name.constantize
264
+
265
+ unless part.is_a?(klass) || converter.nil? || part.nil?
266
+ part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
267
+ end
268
+
269
+ hash_from_multiparameter_assignment = part.is_a?(Hash) &&
270
+ part.keys.all?(Integer)
271
+ if hash_from_multiparameter_assignment
272
+ raise ArgumentError unless part.size == part.each_key.max
273
+ part = klass.new(*part.sort.map(&:last))
274
+ end
275
+
276
+ if part.nil? && allow_nil
277
+ mapping.each { |key, _| write_attribute(key, nil) }
278
+ @aggregation_cache[name] = nil
279
+ else
280
+ mapping.each { |key, value| write_attribute(key, part.send(value)) }
281
+ @aggregation_cache[name] = part.dup.freeze
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class AssociationRelation < Relation # :nodoc:
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
+ %w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
19
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
20
+ def #{method}(attributes, **kwargs)
21
+ if @association.reflection.through_reflection?
22
+ raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
23
+ end
24
+
25
+ super
26
+ end
27
+ RUBY
28
+ end
29
+
30
+ private
31
+ def _new(attributes, &block)
32
+ @association.build(attributes, &block)
33
+ end
34
+
35
+ def _create(attributes, &block)
36
+ @association.create(attributes, &block)
37
+ end
38
+
39
+ def _create!(attributes, &block)
40
+ @association.create!(attributes, &block)
41
+ end
42
+
43
+ def exec_queries
44
+ super do |record|
45
+ @association.set_inverse_instance_from_queries(record)
46
+ yield record if block_given?
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/conversions"
4
+
5
+ module ActiveRecord
6
+ module Associations
7
+ # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
+ class AliasTracker # :nodoc:
9
+ def self.create(pool, initial_table, joins, aliases = nil)
10
+ pool.with_connection do |connection|
11
+ if joins.empty?
12
+ aliases ||= Hash.new(0)
13
+ elsif aliases
14
+ default_proc = aliases.default_proc || proc { 0 }
15
+ aliases.default_proc = proc { |h, k|
16
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
17
+ }
18
+ else
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
21
+ }
22
+ end
23
+ aliases[initial_table] = 1
24
+ new(connection.table_alias_length, aliases)
25
+ end
26
+ end
27
+
28
+ def self.initial_count_for(connection, name, table_joins)
29
+ quoted_name = nil
30
+
31
+ counts = table_joins.map do |join|
32
+ if join.is_a?(Arel::Nodes::StringJoin)
33
+ # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
34
+ quoted_name ||= connection.quote_table_name(name)
35
+
36
+ # Table names + table aliases
37
+ join.left.scan(
38
+ /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
39
+ ).size
40
+ elsif join.is_a?(Arel::Nodes::Join)
41
+ join.left.name == name ? 1 : 0
42
+ else
43
+ raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
44
+ end
45
+ end
46
+
47
+ counts.sum
48
+ end
49
+
50
+ # table_joins is an array of arel joins which might conflict with the aliases we assign here
51
+ def initialize(table_alias_length, aliases)
52
+ @aliases = aliases
53
+ @table_alias_length = table_alias_length
54
+ end
55
+
56
+ def aliased_table_for(arel_table, table_name = nil)
57
+ table_name ||= arel_table.name
58
+
59
+ if aliases[table_name] == 0
60
+ # If it's zero, we can have our table_name
61
+ aliases[table_name] = 1
62
+ arel_table = arel_table.alias(table_name) if arel_table.name != table_name
63
+ else
64
+ # Otherwise, we need to use an alias
65
+ aliased_name = table_alias_for(yield)
66
+
67
+ # Update the count
68
+ count = aliases[aliased_name] += 1
69
+
70
+ aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
71
+
72
+ arel_table = arel_table.alias(aliased_name)
73
+ end
74
+
75
+ arel_table
76
+ end
77
+
78
+ attr_reader :aliases
79
+
80
+ private
81
+ def table_alias_for(table_name)
82
+ table_name[0...@table_alias_length].tr(".", "_")
83
+ end
84
+
85
+ def truncate(name)
86
+ name.slice(0, @table_alias_length - 2)
87
+ end
88
+ end
89
+ end
90
+ end