activerecord 6.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1013 -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.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +332 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +258 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +508 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1165 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +512 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +154 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +761 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +821 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +949 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +557 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +267 -0
  129. data/lib/active_record/core.rb +599 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +542 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +859 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +552 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1359 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +51 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +19 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  340. metadata +415 -0
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ReadonlyAttributes
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :_attr_readonly, instance_accessor: false, default: []
9
+ end
10
+
11
+ module ClassMethods
12
+ # Attributes listed as readonly will be used to create a new record but update operations will
13
+ # ignore these fields.
14
+ def attr_readonly(*attributes)
15
+ self._attr_readonly = Set.new(attributes.map(&:to_s)) + (_attr_readonly || [])
16
+ end
17
+
18
+ # Returns an array of all the attributes that have been specified as readonly.
19
+ def readonly_attributes
20
+ _attr_readonly
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,1042 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
4
+ require "concurrent/map"
5
+
6
+ module ActiveRecord
7
+ # = Active Record Reflection
8
+ module Reflection # :nodoc:
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ class_attribute :_reflections, instance_writer: false, default: {}
13
+ class_attribute :aggregate_reflections, instance_writer: false, default: {}
14
+ end
15
+
16
+ class << self
17
+ def create(macro, name, scope, options, ar)
18
+ reflection = reflection_class_for(macro).new(name, scope, options, ar)
19
+ options[:through] ? ThroughReflection.new(reflection) : reflection
20
+ end
21
+
22
+ def add_reflection(ar, name, reflection)
23
+ ar.clear_reflections_cache
24
+ name = -name.to_s
25
+ ar._reflections = ar._reflections.except(name).merge!(name => reflection)
26
+ end
27
+
28
+ def add_aggregate_reflection(ar, name, reflection)
29
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
30
+ end
31
+
32
+ private
33
+ def reflection_class_for(macro)
34
+ case macro
35
+ when :composed_of
36
+ AggregateReflection
37
+ when :has_many
38
+ HasManyReflection
39
+ when :has_one
40
+ HasOneReflection
41
+ when :belongs_to
42
+ BelongsToReflection
43
+ else
44
+ raise "Unsupported Macro: #{macro}"
45
+ end
46
+ end
47
+ end
48
+
49
+ # \Reflection enables the ability to examine the associations and aggregations of
50
+ # Active Record classes and objects. This information, for example,
51
+ # can be used in a form builder that takes an Active Record object
52
+ # and creates input fields for all of the attributes depending on their type
53
+ # and displays the associations to other objects.
54
+ #
55
+ # MacroReflection class has info for AggregateReflection and AssociationReflection
56
+ # classes.
57
+ module ClassMethods
58
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
59
+ def reflect_on_all_aggregations
60
+ aggregate_reflections.values
61
+ end
62
+
63
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
64
+ #
65
+ # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
66
+ #
67
+ def reflect_on_aggregation(aggregation)
68
+ aggregate_reflections[aggregation.to_s]
69
+ end
70
+
71
+ # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
72
+ #
73
+ # Account.reflections # => {"balance" => AggregateReflection}
74
+ #
75
+ def reflections
76
+ @__reflections ||= begin
77
+ ref = {}
78
+
79
+ _reflections.each do |name, reflection|
80
+ parent_reflection = reflection.parent_reflection
81
+
82
+ if parent_reflection
83
+ parent_name = parent_reflection.name
84
+ ref[parent_name.to_s] = parent_reflection
85
+ else
86
+ ref[name] = reflection
87
+ end
88
+ end
89
+
90
+ ref
91
+ end
92
+ end
93
+
94
+ # Returns an array of AssociationReflection objects for all the
95
+ # associations in the class. If you only want to reflect on a certain
96
+ # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
97
+ # <tt>:belongs_to</tt>) as the first parameter.
98
+ #
99
+ # Example:
100
+ #
101
+ # Account.reflect_on_all_associations # returns an array of all associations
102
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
103
+ #
104
+ def reflect_on_all_associations(macro = nil)
105
+ association_reflections = reflections.values
106
+ association_reflections.select! { |reflection| reflection.macro == macro } if macro
107
+ association_reflections
108
+ end
109
+
110
+ # Returns the AssociationReflection object for the +association+ (use the symbol).
111
+ #
112
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
113
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
114
+ #
115
+ def reflect_on_association(association)
116
+ reflections[association.to_s]
117
+ end
118
+
119
+ def _reflect_on_association(association) #:nodoc:
120
+ _reflections[association.to_s]
121
+ end
122
+
123
+ # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
124
+ def reflect_on_all_autosave_associations
125
+ reflections.values.select { |reflection| reflection.options[:autosave] }
126
+ end
127
+
128
+ def clear_reflections_cache # :nodoc:
129
+ @__reflections = nil
130
+ end
131
+ end
132
+
133
+ # Holds all the methods that are shared between MacroReflection and ThroughReflection.
134
+ #
135
+ # AbstractReflection
136
+ # MacroReflection
137
+ # AggregateReflection
138
+ # AssociationReflection
139
+ # HasManyReflection
140
+ # HasOneReflection
141
+ # BelongsToReflection
142
+ # HasAndBelongsToManyReflection
143
+ # ThroughReflection
144
+ # PolymorphicReflection
145
+ # RuntimeReflection
146
+ class AbstractReflection # :nodoc:
147
+ def through_reflection?
148
+ false
149
+ end
150
+
151
+ def table_name
152
+ klass.table_name
153
+ end
154
+
155
+ # Returns a new, unsaved instance of the associated class. +attributes+ will
156
+ # be passed to the class's constructor.
157
+ def build_association(attributes, &block)
158
+ klass.new(attributes, &block)
159
+ end
160
+
161
+ # Returns the class name for the macro.
162
+ #
163
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
164
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
165
+ def class_name
166
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
167
+ end
168
+
169
+ JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
170
+
171
+ def join_keys
172
+ @join_keys ||= get_join_keys(klass)
173
+ end
174
+
175
+ # Returns a list of scopes that should be applied for this Reflection
176
+ # object when querying the database.
177
+ def scopes
178
+ scope ? [scope] : []
179
+ end
180
+
181
+ def join_scope(table, foreign_table, foreign_klass)
182
+ predicate_builder = predicate_builder(table)
183
+ scope_chain_items = join_scopes(table, predicate_builder)
184
+ klass_scope = klass_join_scope(table, predicate_builder)
185
+
186
+ key = join_keys.key
187
+ foreign_key = join_keys.foreign_key
188
+
189
+ klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
190
+
191
+ if type
192
+ klass_scope.where!(type => foreign_klass.polymorphic_name)
193
+ end
194
+
195
+ if klass.finder_needs_type_condition?
196
+ klass_scope.where!(klass.send(:type_condition, table))
197
+ end
198
+
199
+ scope_chain_items.inject(klass_scope, &:merge!)
200
+ end
201
+
202
+ def join_scopes(table, predicate_builder) # :nodoc:
203
+ if scope
204
+ [scope_for(build_scope(table, predicate_builder))]
205
+ else
206
+ []
207
+ end
208
+ end
209
+
210
+ def klass_join_scope(table, predicate_builder) # :nodoc:
211
+ relation = build_scope(table, predicate_builder)
212
+ klass.scope_for_association(relation)
213
+ end
214
+
215
+ def constraints
216
+ chain.flat_map(&:scopes)
217
+ end
218
+
219
+ def counter_cache_column
220
+ if belongs_to?
221
+ if options[:counter_cache] == true
222
+ "#{active_record.name.demodulize.underscore.pluralize}_count"
223
+ elsif options[:counter_cache]
224
+ options[:counter_cache].to_s
225
+ end
226
+ else
227
+ options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
228
+ end
229
+ end
230
+
231
+ def inverse_of
232
+ return unless inverse_name
233
+
234
+ @inverse_of ||= klass._reflect_on_association inverse_name
235
+ end
236
+
237
+ def check_validity_of_inverse!
238
+ unless polymorphic?
239
+ if has_inverse? && inverse_of.nil?
240
+ raise InverseOfAssociationNotFoundError.new(self)
241
+ end
242
+ end
243
+ end
244
+
245
+ # This shit is nasty. We need to avoid the following situation:
246
+ #
247
+ # * An associated record is deleted via record.destroy
248
+ # * Hence the callbacks run, and they find a belongs_to on the record with a
249
+ # :counter_cache options which points back at our owner. So they update the
250
+ # counter cache.
251
+ # * In which case, we must make sure to *not* update the counter cache, or else
252
+ # it will be decremented twice.
253
+ #
254
+ # Hence this method.
255
+ def inverse_which_updates_counter_cache
256
+ return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
257
+ @inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
258
+ inverse.counter_cache_column == counter_cache_column
259
+ end
260
+ end
261
+ alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
262
+
263
+ def inverse_updates_counter_in_memory?
264
+ inverse_of && inverse_which_updates_counter_cache == inverse_of
265
+ end
266
+
267
+ # Returns whether a counter cache should be used for this association.
268
+ #
269
+ # The counter_cache option must be given on either the owner or inverse
270
+ # association, and the column must be present on the owner.
271
+ def has_cached_counter?
272
+ options[:counter_cache] ||
273
+ inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
274
+ !!active_record.columns_hash[counter_cache_column]
275
+ end
276
+
277
+ def counter_must_be_updated_by_has_many?
278
+ !inverse_updates_counter_in_memory? && has_cached_counter?
279
+ end
280
+
281
+ def alias_candidate(name)
282
+ "#{plural_name}_#{name}"
283
+ end
284
+
285
+ def chain
286
+ collect_join_chain
287
+ end
288
+
289
+ def get_join_keys(association_klass)
290
+ JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
291
+ end
292
+
293
+ def build_scope(table, predicate_builder = predicate_builder(table))
294
+ Relation.create(
295
+ klass,
296
+ table: table,
297
+ predicate_builder: predicate_builder
298
+ )
299
+ end
300
+
301
+ def join_primary_key(*)
302
+ foreign_key
303
+ end
304
+
305
+ def join_foreign_key
306
+ active_record_primary_key
307
+ end
308
+
309
+ protected
310
+ def actual_source_reflection # FIXME: this is a horrible name
311
+ self
312
+ end
313
+
314
+ private
315
+ def predicate_builder(table)
316
+ PredicateBuilder.new(TableMetadata.new(klass, table))
317
+ end
318
+
319
+ def primary_key(klass)
320
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
321
+ end
322
+ end
323
+
324
+ # Base class for AggregateReflection and AssociationReflection. Objects of
325
+ # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
326
+ class MacroReflection < AbstractReflection
327
+ # Returns the name of the macro.
328
+ #
329
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
330
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
331
+ attr_reader :name
332
+
333
+ attr_reader :scope
334
+
335
+ # Returns the hash of options used for the macro.
336
+ #
337
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
338
+ # <tt>has_many :clients</tt> returns <tt>{}</tt>
339
+ attr_reader :options
340
+
341
+ attr_reader :active_record
342
+
343
+ attr_reader :plural_name # :nodoc:
344
+
345
+ def initialize(name, scope, options, active_record)
346
+ @name = name
347
+ @scope = scope
348
+ @options = options
349
+ @active_record = active_record
350
+ @klass = options[:anonymous_class]
351
+ @plural_name = active_record.pluralize_table_names ?
352
+ name.to_s.pluralize : name.to_s
353
+ end
354
+
355
+ def autosave=(autosave)
356
+ @options[:autosave] = autosave
357
+ parent_reflection = self.parent_reflection
358
+ if parent_reflection
359
+ parent_reflection.autosave = autosave
360
+ end
361
+ end
362
+
363
+ # Returns the class for the macro.
364
+ #
365
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
366
+ # <tt>has_many :clients</tt> returns the Client class
367
+ #
368
+ # class Company < ActiveRecord::Base
369
+ # has_many :clients
370
+ # end
371
+ #
372
+ # Company.reflect_on_association(:clients).klass
373
+ # # => Client
374
+ #
375
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
376
+ # a new association object. Use +build_association+ or +create_association+
377
+ # instead. This allows plugins to hook into association object creation.
378
+ def klass
379
+ @klass ||= compute_class(class_name)
380
+ end
381
+
382
+ def compute_class(name)
383
+ name.constantize
384
+ end
385
+
386
+ # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
387
+ # and +other_aggregation+ has an options hash assigned to it.
388
+ def ==(other_aggregation)
389
+ super ||
390
+ other_aggregation.kind_of?(self.class) &&
391
+ name == other_aggregation.name &&
392
+ !other_aggregation.options.nil? &&
393
+ active_record == other_aggregation.active_record
394
+ end
395
+
396
+ def scope_for(relation, owner = nil)
397
+ relation.instance_exec(owner, &scope) || relation
398
+ end
399
+
400
+ private
401
+ def derive_class_name
402
+ name.to_s.camelize
403
+ end
404
+ end
405
+
406
+ # Holds all the metadata about an aggregation as it was specified in the
407
+ # Active Record class.
408
+ class AggregateReflection < MacroReflection #:nodoc:
409
+ def mapping
410
+ mapping = options[:mapping] || [name, name]
411
+ mapping.first.is_a?(Array) ? mapping : [mapping]
412
+ end
413
+ end
414
+
415
+ # Holds all the metadata about an association as it was specified in the
416
+ # Active Record class.
417
+ class AssociationReflection < MacroReflection #:nodoc:
418
+ def compute_class(name)
419
+ if polymorphic?
420
+ raise ArgumentError, "Polymorphic associations do not support computing the class."
421
+ end
422
+ active_record.send(:compute_type, name)
423
+ end
424
+
425
+ attr_reader :type, :foreign_type
426
+ attr_accessor :parent_reflection # Reflection
427
+
428
+ def initialize(name, scope, options, active_record)
429
+ super
430
+ @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
431
+ @foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
432
+ @constructable = calculate_constructable(macro, options)
433
+ @association_scope_cache = Concurrent::Map.new
434
+
435
+ if options[:class_name] && options[:class_name].class == Class
436
+ raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
437
+ end
438
+ end
439
+
440
+ def association_scope_cache(conn, owner, &block)
441
+ key = conn.prepared_statements
442
+ if polymorphic?
443
+ key = [key, owner._read_attribute(@foreign_type)]
444
+ end
445
+ @association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
446
+ end
447
+
448
+ def constructable? # :nodoc:
449
+ @constructable
450
+ end
451
+
452
+ def join_table
453
+ @join_table ||= options[:join_table] || derive_join_table
454
+ end
455
+
456
+ def foreign_key
457
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
458
+ end
459
+
460
+ def association_foreign_key
461
+ @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
462
+ end
463
+
464
+ # klass option is necessary to support loading polymorphic associations
465
+ def association_primary_key(klass = nil)
466
+ options[:primary_key] || primary_key(klass || self.klass)
467
+ end
468
+
469
+ def active_record_primary_key
470
+ @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
471
+ end
472
+
473
+ def check_validity!
474
+ check_validity_of_inverse!
475
+ end
476
+
477
+ def check_preloadable!
478
+ return unless scope
479
+
480
+ unless scope.arity == 0
481
+ raise ArgumentError, <<-MSG.squish
482
+ The association scope '#{name}' is instance dependent (the scope
483
+ block takes an argument). Preloading instance dependent scopes is
484
+ not supported.
485
+ MSG
486
+ end
487
+ end
488
+ alias :check_eager_loadable! :check_preloadable!
489
+
490
+ def join_id_for(owner) # :nodoc:
491
+ owner[join_foreign_key]
492
+ end
493
+
494
+ def through_reflection
495
+ nil
496
+ end
497
+
498
+ def source_reflection
499
+ self
500
+ end
501
+
502
+ # A chain of reflections from this one back to the owner. For more see the explanation in
503
+ # ThroughReflection.
504
+ def collect_join_chain
505
+ [self]
506
+ end
507
+
508
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
509
+ # SQL queries on associations.
510
+ def clear_association_scope_cache # :nodoc:
511
+ @association_scope_cache.clear
512
+ end
513
+
514
+ def nested?
515
+ false
516
+ end
517
+
518
+ def has_scope?
519
+ scope
520
+ end
521
+
522
+ def has_inverse?
523
+ inverse_name
524
+ end
525
+
526
+ def polymorphic_inverse_of(associated_class)
527
+ if has_inverse?
528
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
529
+ inverse_relationship
530
+ else
531
+ raise InverseOfAssociationNotFoundError.new(self, associated_class)
532
+ end
533
+ end
534
+ end
535
+
536
+ # Returns the macro type.
537
+ #
538
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
539
+ def macro; raise NotImplementedError; end
540
+
541
+ # Returns whether or not this association reflection is for a collection
542
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
543
+ # +has_and_belongs_to_many+, +false+ otherwise.
544
+ def collection?
545
+ false
546
+ end
547
+
548
+ # Returns whether or not the association should be validated as part of
549
+ # the parent's validation.
550
+ #
551
+ # Unless you explicitly disable validation with
552
+ # <tt>validate: false</tt>, validation will take place when:
553
+ #
554
+ # * you explicitly enable validation; <tt>validate: true</tt>
555
+ # * you use autosave; <tt>autosave: true</tt>
556
+ # * the association is a +has_many+ association
557
+ def validate?
558
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
559
+ end
560
+
561
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
562
+ def belongs_to?; false; end
563
+
564
+ # Returns +true+ if +self+ is a +has_one+ reflection.
565
+ def has_one?; false; end
566
+
567
+ def association_class; raise NotImplementedError; end
568
+
569
+ def polymorphic?
570
+ options[:polymorphic]
571
+ end
572
+
573
+ VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
574
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
575
+
576
+ def add_as_source(seed)
577
+ seed
578
+ end
579
+
580
+ def add_as_polymorphic_through(reflection, seed)
581
+ seed + [PolymorphicReflection.new(self, reflection)]
582
+ end
583
+
584
+ def add_as_through(seed)
585
+ seed + [self]
586
+ end
587
+
588
+ def extensions
589
+ Array(options[:extend])
590
+ end
591
+
592
+ private
593
+
594
+ def calculate_constructable(macro, options)
595
+ true
596
+ end
597
+
598
+ # Attempts to find the inverse association name automatically.
599
+ # If it cannot find a suitable inverse association name, it returns
600
+ # +nil+.
601
+ def inverse_name
602
+ unless defined?(@inverse_name)
603
+ @inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
604
+ end
605
+
606
+ @inverse_name
607
+ end
608
+
609
+ # returns either +nil+ or the inverse association name that it finds.
610
+ def automatic_inverse_of
611
+ if can_find_inverse_of_automatically?(self)
612
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
613
+
614
+ begin
615
+ reflection = klass._reflect_on_association(inverse_name)
616
+ rescue NameError
617
+ # Give up: we couldn't compute the klass type so we won't be able
618
+ # to find any associations either.
619
+ reflection = false
620
+ end
621
+
622
+ if valid_inverse_reflection?(reflection)
623
+ return inverse_name
624
+ end
625
+ end
626
+ end
627
+
628
+ # Checks if the inverse reflection that is returned from the
629
+ # +automatic_inverse_of+ method is a valid reflection. We must
630
+ # make sure that the reflection's active_record name matches up
631
+ # with the current reflection's klass name.
632
+ def valid_inverse_reflection?(reflection)
633
+ reflection &&
634
+ klass <= reflection.active_record &&
635
+ can_find_inverse_of_automatically?(reflection)
636
+ end
637
+
638
+ # Checks to see if the reflection doesn't have any options that prevent
639
+ # us from being able to guess the inverse automatically. First, the
640
+ # <tt>inverse_of</tt> option cannot be set to false. Second, we must
641
+ # have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
642
+ # Third, we must not have options such as <tt>:foreign_key</tt>
643
+ # which prevent us from correctly guessing the inverse association.
644
+ #
645
+ # Anything with a scope can additionally ruin our attempt at finding an
646
+ # inverse, so we exclude reflections with scopes.
647
+ def can_find_inverse_of_automatically?(reflection)
648
+ reflection.options[:inverse_of] != false &&
649
+ VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
650
+ !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
651
+ !reflection.scope
652
+ end
653
+
654
+ def derive_class_name
655
+ class_name = name.to_s
656
+ class_name = class_name.singularize if collection?
657
+ class_name.camelize
658
+ end
659
+
660
+ def derive_foreign_key
661
+ if belongs_to?
662
+ "#{name}_id"
663
+ elsif options[:as]
664
+ "#{options[:as]}_id"
665
+ else
666
+ active_record.name.foreign_key
667
+ end
668
+ end
669
+
670
+ def derive_join_table
671
+ ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
672
+ end
673
+ end
674
+
675
+ class HasManyReflection < AssociationReflection # :nodoc:
676
+ def macro; :has_many; end
677
+
678
+ def collection?; true; end
679
+
680
+ def association_class
681
+ if options[:through]
682
+ Associations::HasManyThroughAssociation
683
+ else
684
+ Associations::HasManyAssociation
685
+ end
686
+ end
687
+
688
+ def association_primary_key(klass = nil)
689
+ primary_key(klass || self.klass)
690
+ end
691
+ end
692
+
693
+ class HasOneReflection < AssociationReflection # :nodoc:
694
+ def macro; :has_one; end
695
+
696
+ def has_one?; true; end
697
+
698
+ def association_class
699
+ if options[:through]
700
+ Associations::HasOneThroughAssociation
701
+ else
702
+ Associations::HasOneAssociation
703
+ end
704
+ end
705
+
706
+ private
707
+
708
+ def calculate_constructable(macro, options)
709
+ !options[:through]
710
+ end
711
+ end
712
+
713
+ class BelongsToReflection < AssociationReflection # :nodoc:
714
+ def macro; :belongs_to; end
715
+
716
+ def belongs_to?; true; end
717
+
718
+ def association_class
719
+ if polymorphic?
720
+ Associations::BelongsToPolymorphicAssociation
721
+ else
722
+ Associations::BelongsToAssociation
723
+ end
724
+ end
725
+
726
+ def join_primary_key(klass = nil)
727
+ polymorphic? ? association_primary_key(klass) : association_primary_key
728
+ end
729
+
730
+ def join_foreign_key
731
+ foreign_key
732
+ end
733
+
734
+ private
735
+ def can_find_inverse_of_automatically?(_)
736
+ !polymorphic? && super
737
+ end
738
+
739
+ def calculate_constructable(macro, options)
740
+ !polymorphic?
741
+ end
742
+ end
743
+
744
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
745
+ def macro; :has_and_belongs_to_many; end
746
+
747
+ def collection?
748
+ true
749
+ end
750
+ end
751
+
752
+ # Holds all the metadata about a :through association as it was specified
753
+ # in the Active Record class.
754
+ class ThroughReflection < AbstractReflection #:nodoc:
755
+ delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
756
+ :active_record_primary_key, :type, :get_join_keys, to: :source_reflection
757
+
758
+ def initialize(delegate_reflection)
759
+ @delegate_reflection = delegate_reflection
760
+ @klass = delegate_reflection.options[:anonymous_class]
761
+ @source_reflection_name = delegate_reflection.options[:source]
762
+ end
763
+
764
+ def through_reflection?
765
+ true
766
+ end
767
+
768
+ def klass
769
+ @klass ||= delegate_reflection.compute_class(class_name)
770
+ end
771
+
772
+ # Returns the source of the through reflection. It checks both a singularized
773
+ # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
774
+ #
775
+ # class Post < ActiveRecord::Base
776
+ # has_many :taggings
777
+ # has_many :tags, through: :taggings
778
+ # end
779
+ #
780
+ # class Tagging < ActiveRecord::Base
781
+ # belongs_to :post
782
+ # belongs_to :tag
783
+ # end
784
+ #
785
+ # tags_reflection = Post.reflect_on_association(:tags)
786
+ # tags_reflection.source_reflection
787
+ # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
788
+ #
789
+ def source_reflection
790
+ through_reflection.klass._reflect_on_association(source_reflection_name)
791
+ end
792
+
793
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
794
+ # of a HasManyThrough or HasOneThrough association.
795
+ #
796
+ # class Post < ActiveRecord::Base
797
+ # has_many :taggings
798
+ # has_many :tags, through: :taggings
799
+ # end
800
+ #
801
+ # tags_reflection = Post.reflect_on_association(:tags)
802
+ # tags_reflection.through_reflection
803
+ # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
804
+ #
805
+ def through_reflection
806
+ active_record._reflect_on_association(options[:through])
807
+ end
808
+
809
+ # Returns an array of reflections which are involved in this association. Each item in the
810
+ # array corresponds to a table which will be part of the query for this association.
811
+ #
812
+ # The chain is built by recursively calling #chain on the source reflection and the through
813
+ # reflection. The base case for the recursion is a normal association, which just returns
814
+ # [self] as its #chain.
815
+ #
816
+ # class Post < ActiveRecord::Base
817
+ # has_many :taggings
818
+ # has_many :tags, through: :taggings
819
+ # end
820
+ #
821
+ # tags_reflection = Post.reflect_on_association(:tags)
822
+ # tags_reflection.chain
823
+ # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
824
+ # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
825
+ #
826
+ def collect_join_chain
827
+ collect_join_reflections [self]
828
+ end
829
+
830
+ # This is for clearing cache on the reflection. Useful for tests that need to compare
831
+ # SQL queries on associations.
832
+ def clear_association_scope_cache # :nodoc:
833
+ delegate_reflection.clear_association_scope_cache
834
+ source_reflection.clear_association_scope_cache
835
+ through_reflection.clear_association_scope_cache
836
+ end
837
+
838
+ def scopes
839
+ source_reflection.scopes + super
840
+ end
841
+
842
+ def join_scopes(table, predicate_builder) # :nodoc:
843
+ source_reflection.join_scopes(table, predicate_builder) + super
844
+ end
845
+
846
+ def has_scope?
847
+ scope || options[:source_type] ||
848
+ source_reflection.has_scope? ||
849
+ through_reflection.has_scope?
850
+ end
851
+
852
+ # A through association is nested if there would be more than one join table
853
+ def nested?
854
+ source_reflection.through_reflection? || through_reflection.through_reflection?
855
+ end
856
+
857
+ # We want to use the klass from this reflection, rather than just delegate straight to
858
+ # the source_reflection, because the source_reflection may be polymorphic. We still
859
+ # need to respect the source_reflection's :primary_key option, though.
860
+ def association_primary_key(klass = nil)
861
+ # Get the "actual" source reflection if the immediate source reflection has a
862
+ # source reflection itself
863
+ actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
864
+ end
865
+
866
+ # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
867
+ #
868
+ # class Post < ActiveRecord::Base
869
+ # has_many :taggings
870
+ # has_many :tags, through: :taggings
871
+ # end
872
+ #
873
+ # tags_reflection = Post.reflect_on_association(:tags)
874
+ # tags_reflection.source_reflection_names
875
+ # # => [:tag, :tags]
876
+ #
877
+ def source_reflection_names
878
+ options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
879
+ end
880
+
881
+ def source_reflection_name # :nodoc:
882
+ return @source_reflection_name if @source_reflection_name
883
+
884
+ names = [name.to_s.singularize, name].collect(&:to_sym).uniq
885
+ names = names.find_all { |n|
886
+ through_reflection.klass._reflect_on_association(n)
887
+ }
888
+
889
+ if names.length > 1
890
+ raise AmbiguousSourceReflectionForThroughAssociation.new(
891
+ active_record.name,
892
+ macro,
893
+ name,
894
+ options,
895
+ source_reflection_names
896
+ )
897
+ end
898
+
899
+ @source_reflection_name = names.first
900
+ end
901
+
902
+ def source_options
903
+ source_reflection.options
904
+ end
905
+
906
+ def through_options
907
+ through_reflection.options
908
+ end
909
+
910
+ def check_validity!
911
+ if through_reflection.nil?
912
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
913
+ end
914
+
915
+ if through_reflection.polymorphic?
916
+ if has_one?
917
+ raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
918
+ else
919
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
920
+ end
921
+ end
922
+
923
+ if source_reflection.nil?
924
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
925
+ end
926
+
927
+ if options[:source_type] && !source_reflection.polymorphic?
928
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
929
+ end
930
+
931
+ if source_reflection.polymorphic? && options[:source_type].nil?
932
+ raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
933
+ end
934
+
935
+ if has_one? && through_reflection.collection?
936
+ raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
937
+ end
938
+
939
+ if parent_reflection.nil?
940
+ reflections = active_record.reflections.keys.map(&:to_sym)
941
+
942
+ if reflections.index(through_reflection.name) > reflections.index(name)
943
+ raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
944
+ end
945
+ end
946
+
947
+ check_validity_of_inverse!
948
+ end
949
+
950
+ def constraints
951
+ scope_chain = source_reflection.constraints
952
+ scope_chain << scope if scope
953
+ scope_chain
954
+ end
955
+
956
+ def add_as_source(seed)
957
+ collect_join_reflections seed
958
+ end
959
+
960
+ def add_as_polymorphic_through(reflection, seed)
961
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
962
+ end
963
+
964
+ def add_as_through(seed)
965
+ collect_join_reflections(seed + [self])
966
+ end
967
+
968
+ protected
969
+ def actual_source_reflection # FIXME: this is a horrible name
970
+ source_reflection.actual_source_reflection
971
+ end
972
+
973
+ private
974
+ attr_reader :delegate_reflection
975
+
976
+ def collect_join_reflections(seed)
977
+ a = source_reflection.add_as_source seed
978
+ if options[:source_type]
979
+ through_reflection.add_as_polymorphic_through self, a
980
+ else
981
+ through_reflection.add_as_through a
982
+ end
983
+ end
984
+
985
+ def inverse_name; delegate_reflection.send(:inverse_name); end
986
+
987
+ def derive_class_name
988
+ # get the class_name of the belongs_to association of the through reflection
989
+ options[:source_type] || source_reflection.class_name
990
+ end
991
+
992
+ delegate_methods = AssociationReflection.public_instance_methods -
993
+ public_instance_methods
994
+
995
+ delegate(*delegate_methods, to: :delegate_reflection)
996
+ end
997
+
998
+ class PolymorphicReflection < AbstractReflection # :nodoc:
999
+ delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
1000
+
1001
+ def initialize(reflection, previous_reflection)
1002
+ @reflection = reflection
1003
+ @previous_reflection = previous_reflection
1004
+ end
1005
+
1006
+ def join_scopes(table, predicate_builder) # :nodoc:
1007
+ scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
1008
+ scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
1009
+ end
1010
+
1011
+ def constraints
1012
+ @reflection.constraints + [source_type_scope]
1013
+ end
1014
+
1015
+ private
1016
+ def source_type_scope
1017
+ type = @previous_reflection.foreign_type
1018
+ source_type = @previous_reflection.options[:source_type]
1019
+ lambda { |object| where(type => source_type) }
1020
+ end
1021
+ end
1022
+
1023
+ class RuntimeReflection < AbstractReflection # :nodoc:
1024
+ delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
1025
+
1026
+ def initialize(reflection, association)
1027
+ @reflection = reflection
1028
+ @association = association
1029
+ end
1030
+
1031
+ def klass
1032
+ @association.klass
1033
+ end
1034
+
1035
+ def aliased_table
1036
+ @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
1037
+ end
1038
+
1039
+ def all_includes; yield; end
1040
+ end
1041
+ end
1042
+ end