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,1287 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
4
+
5
+ module ActiveRecord
6
+ # = Active Record Reflection
7
+ module Reflection # :nodoc:
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_reflections, instance_writer: false, default: {}
12
+ class_attribute :aggregate_reflections, instance_writer: false, default: {}
13
+ class_attribute :automatic_scope_inversing, instance_writer: false, default: false
14
+ class_attribute :automatically_invert_plural_associations, instance_writer: false, default: false
15
+ end
16
+
17
+ class << self
18
+ def create(macro, name, scope, options, ar)
19
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
20
+ options[:through] ? ThroughReflection.new(reflection) : reflection
21
+ end
22
+
23
+ def add_reflection(ar, name, reflection)
24
+ ar.clear_reflections_cache
25
+ name = name.to_sym
26
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
27
+ end
28
+
29
+ def add_aggregate_reflection(ar, name, reflection)
30
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
31
+ end
32
+
33
+ private
34
+ def reflection_class_for(macro)
35
+ case macro
36
+ when :composed_of
37
+ AggregateReflection
38
+ when :has_many
39
+ HasManyReflection
40
+ when :has_one
41
+ HasOneReflection
42
+ when :belongs_to
43
+ BelongsToReflection
44
+ else
45
+ raise "Unsupported Macro: #{macro}"
46
+ end
47
+ end
48
+ end
49
+
50
+ # = Active Record Reflection
51
+ #
52
+ # \Reflection enables the ability to examine the associations and aggregations of
53
+ # Active Record classes and objects. This information, for example,
54
+ # can be used in a form builder that takes an Active Record object
55
+ # and creates input fields for all of the attributes depending on their type
56
+ # and displays the associations to other objects.
57
+ #
58
+ # MacroReflection class has info for AggregateReflection and AssociationReflection
59
+ # classes.
60
+ module ClassMethods
61
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
62
+ def reflect_on_all_aggregations
63
+ aggregate_reflections.values
64
+ end
65
+
66
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
67
+ #
68
+ # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
69
+ #
70
+ def reflect_on_aggregation(aggregation)
71
+ aggregate_reflections[aggregation.to_sym]
72
+ end
73
+
74
+ # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
75
+ #
76
+ # Account.reflections # => {"balance" => AggregateReflection}
77
+ #
78
+ def reflections
79
+ normalized_reflections.stringify_keys
80
+ end
81
+
82
+ def normalized_reflections # :nodoc:
83
+ @__reflections ||= begin
84
+ ref = {}
85
+
86
+ _reflections.each do |name, reflection|
87
+ parent_reflection = reflection.parent_reflection
88
+
89
+ if parent_reflection
90
+ parent_name = parent_reflection.name
91
+ ref[parent_name] = parent_reflection
92
+ else
93
+ ref[name] = reflection
94
+ end
95
+ end
96
+
97
+ ref.freeze
98
+ end
99
+ end
100
+
101
+ # Returns an array of AssociationReflection objects for all the
102
+ # associations in the class. If you only want to reflect on a certain
103
+ # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
104
+ # <tt>:belongs_to</tt>) as the first parameter.
105
+ #
106
+ # Example:
107
+ #
108
+ # Account.reflect_on_all_associations # returns an array of all associations
109
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
110
+ #
111
+ def reflect_on_all_associations(macro = nil)
112
+ association_reflections = normalized_reflections.values
113
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
114
+ association_reflections
115
+ end
116
+
117
+ # Returns the AssociationReflection object for the +association+ (use the symbol).
118
+ #
119
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
120
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
121
+ #
122
+ def reflect_on_association(association)
123
+ normalized_reflections[association.to_sym]
124
+ end
125
+
126
+ def _reflect_on_association(association) # :nodoc:
127
+ _reflections[association.to_sym]
128
+ end
129
+
130
+ # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
131
+ def reflect_on_all_autosave_associations
132
+ reflections = normalized_reflections.values
133
+ reflections.select! { |reflection| reflection.options[:autosave] }
134
+ reflections
135
+ end
136
+
137
+ def clear_reflections_cache # :nodoc:
138
+ @__reflections = nil
139
+ end
140
+
141
+ private
142
+ def inherited(subclass)
143
+ super
144
+ subclass.class_eval do
145
+ @__reflections = nil
146
+ end
147
+ end
148
+ end
149
+
150
+ # Holds all the methods that are shared between MacroReflection and ThroughReflection.
151
+ #
152
+ # AbstractReflection
153
+ # MacroReflection
154
+ # AggregateReflection
155
+ # AssociationReflection
156
+ # HasManyReflection
157
+ # HasOneReflection
158
+ # BelongsToReflection
159
+ # HasAndBelongsToManyReflection
160
+ # ThroughReflection
161
+ # PolymorphicReflection
162
+ # RuntimeReflection
163
+ class AbstractReflection # :nodoc:
164
+ def initialize
165
+ @class_name = nil
166
+ @counter_cache_column = nil
167
+ @inverse_of = nil
168
+ @inverse_which_updates_counter_cache_defined = false
169
+ @inverse_which_updates_counter_cache = nil
170
+ end
171
+
172
+ def through_reflection?
173
+ false
174
+ end
175
+
176
+ def table_name
177
+ klass.table_name
178
+ end
179
+
180
+ # Returns a new, unsaved instance of the associated class. +attributes+ will
181
+ # be passed to the class's constructor.
182
+ def build_association(attributes, &block)
183
+ klass.new(attributes, &block)
184
+ end
185
+
186
+ # Returns the class name for the macro.
187
+ #
188
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
189
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
190
+ def class_name
191
+ @class_name ||= -(options[:class_name] || derive_class_name).to_s
192
+ end
193
+
194
+ # Returns a list of scopes that should be applied for this Reflection
195
+ # object when querying the database.
196
+ def scopes
197
+ scope ? [scope] : []
198
+ end
199
+
200
+ def join_scope(table, foreign_table, foreign_klass)
201
+ predicate_builder = predicate_builder(table)
202
+ scope_chain_items = join_scopes(table, predicate_builder)
203
+ klass_scope = klass_join_scope(table, predicate_builder)
204
+
205
+ if type
206
+ klass_scope.where!(type => foreign_klass.polymorphic_name)
207
+ end
208
+
209
+ scope_chain_items.inject(klass_scope, &:merge!)
210
+
211
+ primary_key_column_names = Array(join_primary_key)
212
+ foreign_key_column_names = Array(join_foreign_key)
213
+
214
+ primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
215
+
216
+ primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
217
+ klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
218
+ end
219
+
220
+ if klass.finder_needs_type_condition?
221
+ klass_scope.where!(klass.send(:type_condition, table))
222
+ end
223
+
224
+ klass_scope
225
+ end
226
+
227
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
228
+ if scope
229
+ [scope_for(build_scope(table, predicate_builder, klass), record)]
230
+ else
231
+ []
232
+ end
233
+ end
234
+
235
+ def klass_join_scope(table, predicate_builder) # :nodoc:
236
+ relation = build_scope(table, predicate_builder)
237
+ klass.scope_for_association(relation)
238
+ end
239
+
240
+ def constraints
241
+ chain.flat_map(&:scopes)
242
+ end
243
+
244
+ def counter_cache_column
245
+ @counter_cache_column ||= begin
246
+ counter_cache = options[:counter_cache]
247
+
248
+ if belongs_to?
249
+ if counter_cache
250
+ counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
251
+ end
252
+ else
253
+ -((counter_cache && -counter_cache[:column]) || "#{name}_count")
254
+ end
255
+ end
256
+ end
257
+
258
+ def inverse_of
259
+ return unless inverse_name
260
+
261
+ @inverse_of ||= klass._reflect_on_association inverse_name
262
+ end
263
+
264
+ def check_validity_of_inverse!
265
+ if !polymorphic? && has_inverse?
266
+ if inverse_of.nil?
267
+ raise InverseOfAssociationNotFoundError.new(self)
268
+ end
269
+ if inverse_of == self
270
+ raise InverseOfAssociationRecursiveError.new(self)
271
+ end
272
+ end
273
+ end
274
+
275
+ # We need to avoid the following situation:
276
+ #
277
+ # * An associated record is deleted via record.destroy
278
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
279
+ # :counter_cache options which points back at our owner. So they update the
280
+ # counter cache.
281
+ # * In which case, we must make sure to *not* update the counter cache, or else
282
+ # it will be decremented twice.
283
+ #
284
+ # Hence this method.
285
+ def inverse_which_updates_counter_cache
286
+ unless @inverse_which_updates_counter_cache_defined
287
+ if counter_cache_column
288
+ inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
289
+ @inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
290
+ inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
291
+ end
292
+ end
293
+ @inverse_which_updates_counter_cache_defined = true
294
+ end
295
+ @inverse_which_updates_counter_cache
296
+ end
297
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
298
+
299
+ def inverse_updates_counter_in_memory?
300
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
301
+ end
302
+
303
+ # Returns whether this association has a counter cache.
304
+ #
305
+ # The counter_cache option must be given on either the owner or inverse
306
+ # association, and the column must be present on the owner.
307
+ def has_cached_counter?
308
+ options[:counter_cache] ||
309
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
310
+ active_record.has_attribute?(counter_cache_column)
311
+ end
312
+
313
+ # Returns whether this association has a counter cache and its column values were backfilled
314
+ # (and so it is used internally by methods like +size+/+any?+/etc).
315
+ def has_active_cached_counter?
316
+ return false unless has_cached_counter?
317
+
318
+ counter_cache = options[:counter_cache] ||
319
+ (inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache])
320
+
321
+ counter_cache[:active] != false
322
+ end
323
+
324
+ def counter_must_be_updated_by_has_many?
325
+ !inverse_updates_counter_in_memory? && has_cached_counter?
326
+ end
327
+
328
+ def alias_candidate(name)
329
+ "#{plural_name}_#{name}"
330
+ end
331
+
332
+ def chain
333
+ collect_join_chain
334
+ end
335
+
336
+ def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
337
+ Relation.create(
338
+ klass,
339
+ table: table,
340
+ predicate_builder: predicate_builder
341
+ )
342
+ end
343
+
344
+ def strict_loading?
345
+ options[:strict_loading]
346
+ end
347
+
348
+ def strict_loading_violation_message(owner)
349
+ message = +"`#{owner}` is marked for strict_loading."
350
+ message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
351
+ message << " named `:#{name}` cannot be lazily loaded."
352
+ end
353
+
354
+ protected
355
+ def actual_source_reflection # FIXME: this is a horrible name
356
+ self
357
+ end
358
+
359
+ private
360
+ def predicate_builder(table)
361
+ PredicateBuilder.new(TableMetadata.new(klass, table))
362
+ end
363
+
364
+ def primary_key(klass)
365
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
366
+ end
367
+
368
+ def ensure_option_not_given_as_class!(option_name)
369
+ if options[option_name] && options[option_name].class == Class
370
+ raise ArgumentError, "A class was passed to `:#{option_name}` but we are expecting a string."
371
+ end
372
+ end
373
+ end
374
+
375
+ # Base class for AggregateReflection and AssociationReflection. Objects of
376
+ # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
377
+ class MacroReflection < AbstractReflection
378
+ # Returns the name of the macro.
379
+ #
380
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
381
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
382
+ attr_reader :name
383
+
384
+ attr_reader :scope
385
+
386
+ # Returns the hash of options used for the macro.
387
+ #
388
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
389
+ # <tt>has_many :clients</tt> returns <tt>{}</tt>
390
+ attr_reader :options
391
+
392
+ attr_reader :active_record
393
+
394
+ attr_reader :plural_name # :nodoc:
395
+
396
+ def initialize(name, scope, options, active_record)
397
+ super()
398
+ @name = name
399
+ @scope = scope
400
+ @options = normalize_options(options)
401
+ @active_record = active_record
402
+ @klass = options[:anonymous_class]
403
+ @plural_name = active_record.pluralize_table_names ?
404
+ name.to_s.pluralize : name.to_s
405
+ end
406
+
407
+ def autosave=(autosave)
408
+ @options[:autosave] = autosave
409
+ parent_reflection = self.parent_reflection
410
+ if parent_reflection
411
+ parent_reflection.autosave = autosave
412
+ end
413
+ end
414
+
415
+ # Returns the class for the macro.
416
+ #
417
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
418
+ # <tt>has_many :clients</tt> returns the Client class
419
+ #
420
+ # class Company < ActiveRecord::Base
421
+ # has_many :clients
422
+ # end
423
+ #
424
+ # Company.reflect_on_association(:clients).klass
425
+ # # => Client
426
+ #
427
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
428
+ # a new association object. Use +build_association+ or +create_association+
429
+ # instead. This allows plugins to hook into association object creation.
430
+ def klass
431
+ @klass ||= _klass(class_name)
432
+ end
433
+
434
+ def _klass(class_name) # :nodoc:
435
+ if active_record.name.demodulize == class_name
436
+ return compute_class("::#{class_name}") rescue NameError
437
+ end
438
+
439
+ compute_class(class_name)
440
+ end
441
+
442
+ def compute_class(name)
443
+ name.constantize
444
+ end
445
+
446
+ # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
447
+ # and +other_aggregation+ has an options hash assigned to it.
448
+ def ==(other_aggregation)
449
+ super ||
450
+ other_aggregation.kind_of?(self.class) &&
451
+ name == other_aggregation.name &&
452
+ !other_aggregation.options.nil? &&
453
+ active_record == other_aggregation.active_record
454
+ end
455
+
456
+ def scope_for(relation, owner = nil)
457
+ relation.instance_exec(owner, &scope) || relation
458
+ end
459
+
460
+ private
461
+ def derive_class_name
462
+ name.to_s.camelize
463
+ end
464
+
465
+ def normalize_options(options)
466
+ counter_cache = options.delete(:counter_cache)
467
+
468
+ if counter_cache
469
+ active = true
470
+
471
+ case counter_cache
472
+ when String, Symbol
473
+ column = -counter_cache.to_s
474
+ when Hash
475
+ active = counter_cache.fetch(:active, true)
476
+ column = counter_cache[:column]&.to_s
477
+ end
478
+
479
+ options[:counter_cache] = { active: active, column: column }
480
+ end
481
+
482
+ options
483
+ end
484
+ end
485
+
486
+ # Holds all the metadata about an aggregation as it was specified in the
487
+ # Active Record class.
488
+ class AggregateReflection < MacroReflection # :nodoc:
489
+ def mapping
490
+ mapping = options[:mapping] || [name, name]
491
+ mapping.first.is_a?(Array) ? mapping : [mapping]
492
+ end
493
+ end
494
+
495
+ # Holds all the metadata about an association as it was specified in the
496
+ # Active Record class.
497
+ class AssociationReflection < MacroReflection # :nodoc:
498
+ def compute_class(name)
499
+ if polymorphic?
500
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
501
+ end
502
+
503
+ begin
504
+ klass = active_record.send(:compute_type, name)
505
+ rescue NameError => error
506
+ if error.name.match?(/(?:\A|::)#{name}\z/)
507
+ message = "Missing model class #{name} for the #{active_record}##{self.name} association."
508
+ message += " You can specify a different model class with the :class_name option." unless options[:class_name]
509
+ raise NameError.new(message, name)
510
+ else
511
+ raise
512
+ end
513
+ end
514
+
515
+ unless klass < ActiveRecord::Base
516
+ raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
517
+ end
518
+
519
+ klass
520
+ end
521
+
522
+ attr_reader :type, :foreign_type
523
+ attr_accessor :parent_reflection # Reflection
524
+
525
+ def initialize(name, scope, options, active_record)
526
+ super
527
+ @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
528
+ @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
529
+ @join_table = nil
530
+ @foreign_key = nil
531
+ @association_foreign_key = nil
532
+ @association_primary_key = nil
533
+ if options[:query_constraints]
534
+ ActiveRecord.deprecator.warn <<~MSG.squish
535
+ Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
536
+ To maintain current behavior, use the `foreign_key` option instead.
537
+ MSG
538
+ end
539
+
540
+ # If the foreign key is an array, set query constraints options and don't use the foreign key
541
+ if options[:foreign_key].is_a?(Array)
542
+ options[:query_constraints] = options.delete(:foreign_key)
543
+ end
544
+
545
+ ensure_option_not_given_as_class!(:class_name)
546
+ end
547
+
548
+ def association_scope_cache(klass, owner, &block)
549
+ key = self
550
+ if polymorphic?
551
+ key = [key, owner._read_attribute(@foreign_type)]
552
+ end
553
+ klass.with_connection do |connection|
554
+ klass.cached_find_by_statement(connection, key, &block)
555
+ end
556
+ end
557
+
558
+ def join_table
559
+ @join_table ||= -(options[:join_table]&.to_s || derive_join_table)
560
+ end
561
+
562
+ def foreign_key(infer_from_inverse_of: true)
563
+ @foreign_key ||= if options[:foreign_key]
564
+ if options[:foreign_key].is_a?(Array)
565
+ options[:foreign_key].map { |fk| -fk.to_s.freeze }.freeze
566
+ else
567
+ options[:foreign_key].to_s.freeze
568
+ end
569
+ elsif options[:query_constraints]
570
+ options[:query_constraints].map { |fk| -fk.to_s.freeze }.freeze
571
+ else
572
+ derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
573
+
574
+ if active_record.has_query_constraints?
575
+ derived_fk = derive_fk_query_constraints(derived_fk)
576
+ end
577
+
578
+ if derived_fk.is_a?(Array)
579
+ derived_fk.map! { |fk| -fk.freeze }
580
+ derived_fk.freeze
581
+ else
582
+ -derived_fk.freeze
583
+ end
584
+ end
585
+ end
586
+
587
+ def association_foreign_key
588
+ @association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
589
+ end
590
+
591
+ def association_primary_key(klass = nil)
592
+ primary_key(klass || self.klass)
593
+ end
594
+
595
+ def active_record_primary_key
596
+ custom_primary_key = options[:primary_key]
597
+ @active_record_primary_key ||= if custom_primary_key
598
+ if custom_primary_key.is_a?(Array)
599
+ custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
600
+ else
601
+ custom_primary_key.to_s.freeze
602
+ end
603
+ elsif active_record.has_query_constraints? || options[:query_constraints]
604
+ active_record.query_constraints_list
605
+ elsif active_record.composite_primary_key?
606
+ # If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
607
+ primary_key = primary_key(active_record)
608
+ primary_key.include?("id") ? "id" : primary_key.freeze
609
+ else
610
+ primary_key(active_record).freeze
611
+ end
612
+ end
613
+
614
+ def join_primary_key(klass = nil)
615
+ foreign_key
616
+ end
617
+
618
+ def join_primary_type
619
+ type
620
+ end
621
+
622
+ def join_foreign_key
623
+ active_record_primary_key
624
+ end
625
+
626
+ def check_validity!
627
+ check_validity_of_inverse!
628
+
629
+ if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
630
+ if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
631
+ raise CompositePrimaryKeyMismatchError.new(self)
632
+ elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
633
+ raise CompositePrimaryKeyMismatchError.new(self)
634
+ end
635
+ end
636
+ end
637
+
638
+ def check_eager_loadable!
639
+ return unless scope
640
+
641
+ unless scope.arity == 0
642
+ raise ArgumentError, <<-MSG.squish
643
+ The association scope '#{name}' is instance dependent (the scope
644
+ block takes an argument). Eager loading instance dependent scopes
645
+ is not supported.
646
+ MSG
647
+ end
648
+ end
649
+
650
+ def join_id_for(owner) # :nodoc:
651
+ Array(join_foreign_key).map { |key| owner._read_attribute(key) }
652
+ end
653
+
654
+ def through_reflection
655
+ nil
656
+ end
657
+
658
+ def source_reflection
659
+ self
660
+ end
661
+
662
+ # A chain of reflections from this one back to the owner. For more see the explanation in
663
+ # ThroughReflection.
664
+ def collect_join_chain
665
+ [self]
666
+ end
667
+
668
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
669
+ # SQL queries on associations.
670
+ def clear_association_scope_cache # :nodoc:
671
+ klass.initialize_find_by_cache
672
+ end
673
+
674
+ def nested?
675
+ false
676
+ end
677
+
678
+ def has_scope?
679
+ scope
680
+ end
681
+
682
+ def has_inverse?
683
+ inverse_name
684
+ end
685
+
686
+ def polymorphic_inverse_of(associated_class)
687
+ if has_inverse?
688
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
689
+ inverse_relationship
690
+ else
691
+ raise InverseOfAssociationNotFoundError.new(self, associated_class)
692
+ end
693
+ end
694
+ end
695
+
696
+ # Returns the macro type.
697
+ #
698
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
699
+ def macro; raise NotImplementedError; end
700
+
701
+ # Returns whether or not this association reflection is for a collection
702
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
703
+ # +has_and_belongs_to_many+, +false+ otherwise.
704
+ def collection?
705
+ false
706
+ end
707
+
708
+ # Returns whether or not the association should be validated as part of
709
+ # the parent's validation.
710
+ #
711
+ # Unless you explicitly disable validation with
712
+ # <tt>validate: false</tt>, validation will take place when:
713
+ #
714
+ # * you explicitly enable validation; <tt>validate: true</tt>
715
+ # * you use autosave; <tt>autosave: true</tt>
716
+ # * the association is a +has_many+ association
717
+ def validate?
718
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
719
+ end
720
+
721
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
722
+ def belongs_to?; false; end
723
+
724
+ # Returns +true+ if +self+ is a +has_one+ reflection.
725
+ def has_one?; false; end
726
+
727
+ def association_class; raise NotImplementedError; end
728
+
729
+ def polymorphic?
730
+ options[:polymorphic]
731
+ end
732
+
733
+ def polymorphic_name
734
+ active_record.polymorphic_name
735
+ end
736
+
737
+ def add_as_source(seed)
738
+ seed
739
+ end
740
+
741
+ def add_as_polymorphic_through(reflection, seed)
742
+ seed + [PolymorphicReflection.new(self, reflection)]
743
+ end
744
+
745
+ def add_as_through(seed)
746
+ seed + [self]
747
+ end
748
+
749
+ def extensions
750
+ Array(options[:extend])
751
+ end
752
+
753
+ private
754
+ # Attempts to find the inverse association name automatically.
755
+ # If it cannot find a suitable inverse association name, it returns
756
+ # +nil+.
757
+ def inverse_name
758
+ unless defined?(@inverse_name)
759
+ @inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
760
+ end
761
+
762
+ @inverse_name
763
+ end
764
+
765
+ # returns either +nil+ or the inverse association name that it finds.
766
+ def automatic_inverse_of
767
+ if can_find_inverse_of_automatically?(self)
768
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
769
+
770
+ begin
771
+ reflection = klass._reflect_on_association(inverse_name)
772
+ if !reflection && active_record.automatically_invert_plural_associations
773
+ plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
774
+ reflection = klass._reflect_on_association(plural_inverse_name)
775
+ end
776
+ rescue NameError => error
777
+ raise unless error.name.to_s == class_name
778
+
779
+ # Give up: we couldn't compute the klass type so we won't be able
780
+ # to find any associations either.
781
+ reflection = false
782
+ end
783
+
784
+ if valid_inverse_reflection?(reflection)
785
+ reflection.name
786
+ end
787
+ end
788
+ end
789
+
790
+ # Checks if the inverse reflection that is returned from the
791
+ # +automatic_inverse_of+ method is a valid reflection. We must
792
+ # make sure that the reflection's active_record name matches up
793
+ # with the current reflection's klass name.
794
+ def valid_inverse_reflection?(reflection)
795
+ reflection &&
796
+ reflection != self &&
797
+ foreign_key == reflection.foreign_key &&
798
+ klass <= reflection.active_record &&
799
+ can_find_inverse_of_automatically?(reflection, true)
800
+ end
801
+
802
+ # Checks to see if the reflection doesn't have any options that prevent
803
+ # us from being able to guess the inverse automatically. First, the
804
+ # <tt>inverse_of</tt> option cannot be set to false. Second, we must
805
+ # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
806
+ # Third, we must not have options such as <tt>:foreign_key</tt>
807
+ # which prevent us from correctly guessing the inverse association.
808
+ def can_find_inverse_of_automatically?(reflection, inverse_reflection = false)
809
+ reflection.options[:inverse_of] != false &&
810
+ !reflection.options[:through] &&
811
+ !reflection.options[:foreign_key] &&
812
+ scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
813
+ end
814
+
815
+ # Scopes on the potential inverse reflection prevent automatic
816
+ # <tt>inverse_of</tt>, since the scope could exclude the owner record
817
+ # we would inverse from. Scopes on the reflection itself allow for
818
+ # automatic <tt>inverse_of</tt> as long as
819
+ # <tt>config.active_record.automatic_scope_inversing<tt> is set to
820
+ # +true+ (the default for new applications).
821
+ def scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
822
+ if inverse_reflection
823
+ !reflection.scope
824
+ else
825
+ !reflection.scope || reflection.klass.automatic_scope_inversing
826
+ end
827
+ end
828
+
829
+ def derive_class_name
830
+ class_name = name.to_s
831
+ class_name = class_name.singularize if collection?
832
+ class_name.camelize
833
+ end
834
+
835
+ def derive_foreign_key(infer_from_inverse_of: true)
836
+ if belongs_to?
837
+ "#{name}_id"
838
+ elsif options[:as]
839
+ "#{options[:as]}_id"
840
+ elsif options[:inverse_of] && infer_from_inverse_of
841
+ inverse_of.foreign_key(infer_from_inverse_of: false)
842
+ else
843
+ active_record.model_name.to_s.foreign_key
844
+ end
845
+ end
846
+
847
+ def derive_fk_query_constraints(foreign_key)
848
+ primary_query_constraints = active_record.query_constraints_list
849
+ owner_pk = active_record.primary_key
850
+
851
+ if primary_query_constraints.size > 2
852
+ raise ArgumentError, <<~MSG.squish
853
+ The query constraints list on the `#{active_record}` model has more than 2
854
+ attributes. Active Record is unable to derive the query constraints
855
+ for the association. You need to explicitly define the query constraints
856
+ for this association.
857
+ MSG
858
+ end
859
+
860
+ if !primary_query_constraints.include?(owner_pk)
861
+ raise ArgumentError, <<~MSG.squish
862
+ The query constraints on the `#{active_record}` model does not include the primary
863
+ key so Active Record is unable to derive the foreign key constraints for
864
+ the association. You need to explicitly define the query constraints for this
865
+ association.
866
+ MSG
867
+ end
868
+
869
+ return foreign_key if primary_query_constraints.include?(foreign_key)
870
+
871
+ first_key, last_key = primary_query_constraints
872
+
873
+ if first_key == owner_pk
874
+ [foreign_key, last_key.to_s]
875
+ elsif last_key == owner_pk
876
+ [first_key.to_s, foreign_key]
877
+ else
878
+ raise ArgumentError, <<~MSG.squish
879
+ Active Record couldn't correctly interpret the query constraints
880
+ for the `#{active_record}` model. The query constraints on `#{active_record}` are
881
+ `#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
882
+ You need to explicitly set the query constraints for this association.
883
+ MSG
884
+ end
885
+ end
886
+
887
+ def derive_join_table
888
+ ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
889
+ end
890
+ end
891
+
892
+ class HasManyReflection < AssociationReflection # :nodoc:
893
+ def macro; :has_many; end
894
+
895
+ def collection?; true; end
896
+
897
+ def association_class
898
+ if options[:through]
899
+ Associations::HasManyThroughAssociation
900
+ else
901
+ Associations::HasManyAssociation
902
+ end
903
+ end
904
+ end
905
+
906
+ class HasOneReflection < AssociationReflection # :nodoc:
907
+ def macro; :has_one; end
908
+
909
+ def has_one?; true; end
910
+
911
+ def association_class
912
+ if options[:through]
913
+ Associations::HasOneThroughAssociation
914
+ else
915
+ Associations::HasOneAssociation
916
+ end
917
+ end
918
+ end
919
+
920
+ class BelongsToReflection < AssociationReflection # :nodoc:
921
+ def macro; :belongs_to; end
922
+
923
+ def belongs_to?; true; end
924
+
925
+ def association_class
926
+ if polymorphic?
927
+ Associations::BelongsToPolymorphicAssociation
928
+ else
929
+ Associations::BelongsToAssociation
930
+ end
931
+ end
932
+
933
+ # klass option is necessary to support loading polymorphic associations
934
+ def association_primary_key(klass = nil)
935
+ if primary_key = options[:primary_key]
936
+ @association_primary_key ||= if primary_key.is_a?(Array)
937
+ primary_key.map { |pk| pk.to_s.freeze }.freeze
938
+ else
939
+ -primary_key.to_s
940
+ end
941
+ elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
942
+ (klass || self.klass).composite_query_constraints_list
943
+ elsif (klass || self.klass).composite_primary_key?
944
+ # If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
945
+ primary_key = (klass || self.klass).primary_key
946
+ primary_key.include?("id") ? "id" : primary_key
947
+ else
948
+ primary_key(klass || self.klass)
949
+ end
950
+ end
951
+
952
+ def join_primary_key(klass = nil)
953
+ polymorphic? ? association_primary_key(klass) : association_primary_key
954
+ end
955
+
956
+ def join_foreign_key
957
+ foreign_key
958
+ end
959
+
960
+ def join_foreign_type
961
+ foreign_type
962
+ end
963
+
964
+ private
965
+ def can_find_inverse_of_automatically?(*)
966
+ !polymorphic? && super
967
+ end
968
+ end
969
+
970
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
971
+ def macro; :has_and_belongs_to_many; end
972
+
973
+ def collection?
974
+ true
975
+ end
976
+ end
977
+
978
+ # Holds all the metadata about a :through association as it was specified
979
+ # in the Active Record class.
980
+ class ThroughReflection < AbstractReflection # :nodoc:
981
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
982
+ :active_record_primary_key, :join_foreign_key, to: :source_reflection
983
+
984
+ def initialize(delegate_reflection)
985
+ super()
986
+ @delegate_reflection = delegate_reflection
987
+ @klass = delegate_reflection.options[:anonymous_class]
988
+ @source_reflection_name = delegate_reflection.options[:source]
989
+
990
+ ensure_option_not_given_as_class!(:source_type)
991
+ end
992
+
993
+ def through_reflection?
994
+ true
995
+ end
996
+
997
+ def klass
998
+ @klass ||= delegate_reflection._klass(class_name)
999
+ end
1000
+
1001
+ # Returns the source of the through reflection. It checks both a singularized
1002
+ # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
1003
+ #
1004
+ # class Post < ActiveRecord::Base
1005
+ # has_many :taggings
1006
+ # has_many :tags, through: :taggings
1007
+ # end
1008
+ #
1009
+ # class Tagging < ActiveRecord::Base
1010
+ # belongs_to :post
1011
+ # belongs_to :tag
1012
+ # end
1013
+ #
1014
+ # tags_reflection = Post.reflect_on_association(:tags)
1015
+ # tags_reflection.source_reflection
1016
+ # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
1017
+ #
1018
+ def source_reflection
1019
+ return unless source_reflection_name
1020
+
1021
+ through_reflection.klass._reflect_on_association(source_reflection_name)
1022
+ end
1023
+
1024
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
1025
+ # of a HasManyThrough or HasOneThrough association.
1026
+ #
1027
+ # class Post < ActiveRecord::Base
1028
+ # has_many :taggings
1029
+ # has_many :tags, through: :taggings
1030
+ # end
1031
+ #
1032
+ # tags_reflection = Post.reflect_on_association(:tags)
1033
+ # tags_reflection.through_reflection
1034
+ # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
1035
+ #
1036
+ def through_reflection
1037
+ active_record._reflect_on_association(options[:through])
1038
+ end
1039
+
1040
+ # Returns an array of reflections which are involved in this association. Each item in the
1041
+ # array corresponds to a table which will be part of the query for this association.
1042
+ #
1043
+ # The chain is built by recursively calling #chain on the source reflection and the through
1044
+ # reflection. The base case for the recursion is a normal association, which just returns
1045
+ # [self] as its #chain.
1046
+ #
1047
+ # class Post < ActiveRecord::Base
1048
+ # has_many :taggings
1049
+ # has_many :tags, through: :taggings
1050
+ # end
1051
+ #
1052
+ # tags_reflection = Post.reflect_on_association(:tags)
1053
+ # tags_reflection.chain
1054
+ # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
1055
+ # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
1056
+ #
1057
+ def collect_join_chain
1058
+ collect_join_reflections [self]
1059
+ end
1060
+
1061
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
1062
+ # SQL queries on associations.
1063
+ def clear_association_scope_cache # :nodoc:
1064
+ delegate_reflection.clear_association_scope_cache
1065
+ source_reflection.clear_association_scope_cache
1066
+ through_reflection.clear_association_scope_cache
1067
+ end
1068
+
1069
+ def scopes
1070
+ source_reflection.scopes + super
1071
+ end
1072
+
1073
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1074
+ source_reflection.join_scopes(table, predicate_builder, klass, record) + super
1075
+ end
1076
+
1077
+ def has_scope?
1078
+ scope || options[:source_type] ||
1079
+ source_reflection.has_scope? ||
1080
+ through_reflection.has_scope?
1081
+ end
1082
+
1083
+ # A through association is nested if there would be more than one join table
1084
+ def nested?
1085
+ source_reflection.through_reflection? || through_reflection.through_reflection?
1086
+ end
1087
+
1088
+ # We want to use the klass from this reflection, rather than just delegate straight to
1089
+ # the source_reflection, because the source_reflection may be polymorphic. We still
1090
+ # need to respect the source_reflection's :primary_key option, though.
1091
+ def association_primary_key(klass = nil)
1092
+ # Get the "actual" source reflection if the immediate source reflection has a
1093
+ # source reflection itself
1094
+ if primary_key = actual_source_reflection.options[:primary_key]
1095
+ @association_primary_key ||= -primary_key.to_s
1096
+ else
1097
+ primary_key(klass || self.klass)
1098
+ end
1099
+ end
1100
+
1101
+ def join_primary_key(klass = self.klass)
1102
+ source_reflection.join_primary_key(klass)
1103
+ end
1104
+
1105
+ # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
1106
+ #
1107
+ # class Post < ActiveRecord::Base
1108
+ # has_many :taggings
1109
+ # has_many :tags, through: :taggings
1110
+ # end
1111
+ #
1112
+ # tags_reflection = Post.reflect_on_association(:tags)
1113
+ # tags_reflection.source_reflection_names
1114
+ # # => [:tag, :tags]
1115
+ #
1116
+ def source_reflection_names
1117
+ options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
1118
+ end
1119
+
1120
+ def source_reflection_name # :nodoc:
1121
+ @source_reflection_name ||= begin
1122
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
1123
+ names = names.find_all { |n|
1124
+ through_reflection.klass._reflect_on_association(n)
1125
+ }
1126
+
1127
+ if names.length > 1
1128
+ raise AmbiguousSourceReflectionForThroughAssociation.new(
1129
+ active_record.name,
1130
+ macro,
1131
+ name,
1132
+ options,
1133
+ source_reflection_names
1134
+ )
1135
+ end
1136
+ names.first
1137
+ end
1138
+ end
1139
+
1140
+ def source_options
1141
+ source_reflection.options
1142
+ end
1143
+
1144
+ def through_options
1145
+ through_reflection.options
1146
+ end
1147
+
1148
+ def check_validity!
1149
+ if through_reflection.nil?
1150
+ raise HasManyThroughAssociationNotFoundError.new(active_record, self)
1151
+ end
1152
+
1153
+ if through_reflection.polymorphic?
1154
+ if has_one?
1155
+ raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
1156
+ else
1157
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
1158
+ end
1159
+ end
1160
+
1161
+ if source_reflection.nil?
1162
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
1163
+ end
1164
+
1165
+ if options[:source_type] && !source_reflection.polymorphic?
1166
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
1167
+ end
1168
+
1169
+ if source_reflection.polymorphic? && options[:source_type].nil?
1170
+ raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
1171
+ end
1172
+
1173
+ if has_one? && through_reflection.collection?
1174
+ raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
1175
+ end
1176
+
1177
+ if parent_reflection.nil?
1178
+ reflections = active_record.normalized_reflections.keys
1179
+
1180
+ if reflections.index(through_reflection.name) > reflections.index(name)
1181
+ raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
1182
+ end
1183
+ end
1184
+
1185
+ check_validity_of_inverse!
1186
+ end
1187
+
1188
+ def constraints
1189
+ scope_chain = source_reflection.constraints
1190
+ scope_chain << scope if scope
1191
+ scope_chain
1192
+ end
1193
+
1194
+ def add_as_source(seed)
1195
+ collect_join_reflections seed
1196
+ end
1197
+
1198
+ def add_as_polymorphic_through(reflection, seed)
1199
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
1200
+ end
1201
+
1202
+ def add_as_through(seed)
1203
+ collect_join_reflections(seed + [self])
1204
+ end
1205
+
1206
+ protected
1207
+ def actual_source_reflection # FIXME: this is a horrible name
1208
+ source_reflection.actual_source_reflection
1209
+ end
1210
+
1211
+ private
1212
+ attr_reader :delegate_reflection
1213
+
1214
+ def collect_join_reflections(seed)
1215
+ a = source_reflection.add_as_source seed
1216
+ if options[:source_type]
1217
+ through_reflection.add_as_polymorphic_through self, a
1218
+ else
1219
+ through_reflection.add_as_through a
1220
+ end
1221
+ end
1222
+
1223
+ def inverse_name; delegate_reflection.send(:inverse_name); end
1224
+
1225
+ def derive_class_name
1226
+ # get the class_name of the belongs_to association of the through reflection
1227
+ options[:source_type] || source_reflection.class_name
1228
+ end
1229
+
1230
+ delegate_methods = AssociationReflection.public_instance_methods -
1231
+ public_instance_methods
1232
+
1233
+ delegate(*delegate_methods, to: :delegate_reflection)
1234
+ end
1235
+
1236
+ class PolymorphicReflection < AbstractReflection # :nodoc:
1237
+ delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
1238
+ :name, :scope_for, to: :@reflection
1239
+
1240
+ def initialize(reflection, previous_reflection)
1241
+ super()
1242
+ @reflection = reflection
1243
+ @previous_reflection = previous_reflection
1244
+ end
1245
+
1246
+ def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1247
+ scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1248
+ scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1249
+ end
1250
+
1251
+ def constraints
1252
+ @reflection.constraints + [source_type_scope]
1253
+ end
1254
+
1255
+ private
1256
+ def source_type_scope
1257
+ type = @previous_reflection.foreign_type
1258
+ source_type = @previous_reflection.options[:source_type]
1259
+ lambda { |object| where(type => source_type) }
1260
+ end
1261
+ end
1262
+
1263
+ class RuntimeReflection < AbstractReflection # :nodoc:
1264
+ delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
1265
+
1266
+ def initialize(reflection, association)
1267
+ super()
1268
+ @reflection = reflection
1269
+ @association = association
1270
+ end
1271
+
1272
+ def klass
1273
+ @association.klass
1274
+ end
1275
+
1276
+ def aliased_table
1277
+ klass.arel_table
1278
+ end
1279
+
1280
+ def join_primary_key(klass = self.klass)
1281
+ @reflection.join_primary_key(klass)
1282
+ end
1283
+
1284
+ def all_includes; yield; end
1285
+ end
1286
+ end
1287
+ end