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,586 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/associations/nested_error"
4
+
5
+ module ActiveRecord
6
+ # = Active Record Autosave Association
7
+ #
8
+ # AutosaveAssociation is a module that takes care of automatically saving
9
+ # associated records when their parent is saved. In addition to saving, it
10
+ # also destroys any associated records that were marked for destruction.
11
+ # (See #mark_for_destruction and #marked_for_destruction?).
12
+ #
13
+ # Saving of the parent, its associations, and the destruction of marked
14
+ # associations, all happen inside a transaction. This should never leave the
15
+ # database in an inconsistent state.
16
+ #
17
+ # If validations for any of the associations fail, their error messages will
18
+ # be applied to the parent.
19
+ #
20
+ # Note that it also means that associations marked for destruction won't
21
+ # be destroyed directly. They will however still be marked for destruction.
22
+ #
23
+ # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
24
+ # When the <tt>:autosave</tt> option is not present then new association records are
25
+ # saved but the updated association records are not saved.
26
+ #
27
+ # == Validation
28
+ #
29
+ # Child records are validated unless <tt>:validate</tt> is +false+.
30
+ #
31
+ # == \Callbacks
32
+ #
33
+ # Association with autosave option defines several callbacks on your
34
+ # model (around_save, before_save, after_create, after_update). Please note that
35
+ # callbacks are executed in the order they were defined in
36
+ # model. You should avoid modifying the association content before
37
+ # autosave callbacks are executed. Placing your callbacks after
38
+ # associations is usually a good practice.
39
+ #
40
+ # === One-to-one Example
41
+ #
42
+ # class Post < ActiveRecord::Base
43
+ # has_one :author, autosave: true
44
+ # end
45
+ #
46
+ # Saving changes to the parent and its associated model can now be performed
47
+ # automatically _and_ atomically:
48
+ #
49
+ # post = Post.find(1)
50
+ # post.title # => "The current global position of migrating ducks"
51
+ # post.author.name # => "alloy"
52
+ #
53
+ # post.title = "On the migration of ducks"
54
+ # post.author.name = "Eloy Duran"
55
+ #
56
+ # post.save
57
+ # post.reload
58
+ # post.title # => "On the migration of ducks"
59
+ # post.author.name # => "Eloy Duran"
60
+ #
61
+ # Destroying an associated model, as part of the parent's save action, is as
62
+ # simple as marking it for destruction:
63
+ #
64
+ # post.author.mark_for_destruction
65
+ # post.author.marked_for_destruction? # => true
66
+ #
67
+ # Note that the model is _not_ yet removed from the database:
68
+ #
69
+ # id = post.author.id
70
+ # Author.find_by(id: id).nil? # => false
71
+ #
72
+ # post.save
73
+ # post.reload.author # => nil
74
+ #
75
+ # Now it _is_ removed from the database:
76
+ #
77
+ # Author.find_by(id: id).nil? # => true
78
+ #
79
+ # === One-to-many Example
80
+ #
81
+ # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
82
+ #
83
+ # class Post < ActiveRecord::Base
84
+ # has_many :comments # :autosave option is not declared
85
+ # end
86
+ #
87
+ # post = Post.new(title: 'ruby rocks')
88
+ # post.comments.build(body: 'hello world')
89
+ # post.save # => saves both post and comment
90
+ #
91
+ # post = Post.create(title: 'ruby rocks')
92
+ # post.comments.build(body: 'hello world')
93
+ # post.save # => saves both post and comment
94
+ #
95
+ # post = Post.create(title: 'ruby rocks')
96
+ # comment = post.comments.create(body: 'hello world')
97
+ # comment.body = 'hi everyone'
98
+ # post.save # => saves post, but not comment
99
+ #
100
+ # When <tt>:autosave</tt> is true all children are saved, no matter whether they
101
+ # are new records or not:
102
+ #
103
+ # class Post < ActiveRecord::Base
104
+ # has_many :comments, autosave: true
105
+ # end
106
+ #
107
+ # post = Post.create(title: 'ruby rocks')
108
+ # comment = post.comments.create(body: 'hello world')
109
+ # comment.body = 'hi everyone'
110
+ # post.comments.build(body: "good morning.")
111
+ # post.save # => saves post and both comments.
112
+ #
113
+ # Destroying one of the associated models as part of the parent's save action
114
+ # is as simple as marking it for destruction:
115
+ #
116
+ # post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
117
+ # post.comments[1].mark_for_destruction
118
+ # post.comments[1].marked_for_destruction? # => true
119
+ # post.comments.length # => 2
120
+ #
121
+ # Note that the model is _not_ yet removed from the database:
122
+ #
123
+ # id = post.comments.last.id
124
+ # Comment.find_by(id: id).nil? # => false
125
+ #
126
+ # post.save
127
+ # post.reload.comments.length # => 1
128
+ #
129
+ # Now it _is_ removed from the database:
130
+ #
131
+ # Comment.find_by(id: id).nil? # => true
132
+ #
133
+ # === Caveats
134
+ #
135
+ # Note that autosave will only trigger for already-persisted association records
136
+ # if the records themselves have been changed. This is to protect against
137
+ # <tt>SystemStackError</tt> caused by circular association validations. The one
138
+ # exception is if a custom validation context is used, in which case the validations
139
+ # will always fire on the associated records.
140
+ module AutosaveAssociation
141
+ extend ActiveSupport::Concern
142
+
143
+ module AssociationBuilderExtension # :nodoc:
144
+ def self.build(model, reflection)
145
+ model.send(:add_autosave_association_callbacks, reflection)
146
+ end
147
+
148
+ def self.valid_options
149
+ [ :autosave ]
150
+ end
151
+ end
152
+
153
+ included do
154
+ Associations::Builder::Association.extensions << AssociationBuilderExtension
155
+ end
156
+
157
+ module ClassMethods # :nodoc:
158
+ private
159
+ def define_non_cyclic_method(name, &block)
160
+ return if method_defined?(name, false)
161
+
162
+ define_method(name) do |*args|
163
+ result = true; @_already_called ||= {}
164
+ # Loop prevention for validation of associations
165
+ unless @_already_called[name]
166
+ begin
167
+ @_already_called[name] = true
168
+ result = instance_eval(&block)
169
+ ensure
170
+ @_already_called[name] = false
171
+ end
172
+ end
173
+
174
+ result
175
+ end
176
+ end
177
+
178
+ # Adds validation and save callbacks for the association as specified by
179
+ # the +reflection+.
180
+ #
181
+ # For performance reasons, we don't check whether to validate at runtime.
182
+ # However the validation and callback methods are lazy and those methods
183
+ # get created when they are invoked for the very first time. However,
184
+ # this can change, for instance, when using nested attributes, which is
185
+ # called _after_ the association has been defined. Since we don't want
186
+ # the callbacks to get defined multiple times, there are guards that
187
+ # check if the save or validation methods have already been defined
188
+ # before actually defining them.
189
+ def add_autosave_association_callbacks(reflection)
190
+ save_method = :"autosave_associated_records_for_#{reflection.name}"
191
+
192
+ if reflection.collection?
193
+ around_save :around_save_collection_association
194
+
195
+ define_non_cyclic_method(save_method) { save_collection_association(reflection) }
196
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
197
+ after_create save_method
198
+ after_update save_method
199
+ elsif reflection.has_one?
200
+ define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
201
+ # Configures two callbacks instead of a single after_save so that
202
+ # the model may rely on their execution order relative to its
203
+ # own callbacks.
204
+ #
205
+ # For example, given that after_creates run before after_saves, if
206
+ # we configured instead an after_save there would be no way to fire
207
+ # a custom after_create callback after the child association gets
208
+ # created.
209
+ after_create save_method
210
+ after_update save_method
211
+ else
212
+ define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
213
+ before_save save_method
214
+ end
215
+
216
+ define_autosave_validation_callbacks(reflection)
217
+ end
218
+
219
+ def define_autosave_validation_callbacks(reflection)
220
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
221
+ if reflection.validate? && !method_defined?(validation_method)
222
+ if reflection.collection?
223
+ method = :validate_collection_association
224
+ elsif reflection.has_one?
225
+ method = :validate_has_one_association
226
+ else
227
+ method = :validate_belongs_to_association
228
+ end
229
+
230
+ define_non_cyclic_method(validation_method) { send(method, reflection) }
231
+ validate validation_method
232
+ after_validation :_ensure_no_duplicate_errors
233
+ end
234
+ end
235
+ end
236
+
237
+ # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
238
+ def reload(options = nil)
239
+ @marked_for_destruction = false
240
+ @destroyed_by_association = nil
241
+ super
242
+ end
243
+
244
+ # Marks this record to be destroyed as part of the parent's save transaction.
245
+ # This does _not_ actually destroy the record instantly, rather child record will be destroyed
246
+ # when <tt>parent.save</tt> is called.
247
+ #
248
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
249
+ def mark_for_destruction
250
+ @marked_for_destruction = true
251
+ end
252
+
253
+ # Returns whether or not this record will be destroyed as part of the parent's save transaction.
254
+ #
255
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
256
+ def marked_for_destruction?
257
+ @marked_for_destruction
258
+ end
259
+
260
+ # Records the association that is being destroyed and destroying this
261
+ # record in the process.
262
+ def destroyed_by_association=(reflection)
263
+ @destroyed_by_association = reflection
264
+ end
265
+
266
+ # Returns the association for the parent being destroyed.
267
+ #
268
+ # Used to avoid updating the counter cache unnecessarily.
269
+ def destroyed_by_association
270
+ @destroyed_by_association
271
+ end
272
+
273
+ # Returns whether or not this record has been changed in any way (including whether
274
+ # any of its nested autosave associations are likewise changed)
275
+ def changed_for_autosave?
276
+ new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
277
+ end
278
+
279
+ def validating_belongs_to_for?(association)
280
+ @validating_belongs_to_for ||= {}
281
+ @validating_belongs_to_for[association]
282
+ end
283
+
284
+ def autosaving_belongs_to_for?(association)
285
+ @autosaving_belongs_to_for ||= {}
286
+ @autosaving_belongs_to_for[association]
287
+ end
288
+
289
+ private
290
+ def init_internals
291
+ super
292
+ @_already_called = nil
293
+ end
294
+
295
+ # Returns the record for an association collection that should be validated
296
+ # or saved. If +autosave+ is +false+ only new records will be returned,
297
+ # unless the parent is/was a new record itself.
298
+ def associated_records_to_validate_or_save(association, new_record, autosave)
299
+ if new_record || custom_validation_context?
300
+ association && association.target
301
+ elsif autosave
302
+ association.target.find_all(&:changed_for_autosave?)
303
+ else
304
+ association.target.find_all(&:new_record?)
305
+ end
306
+ end
307
+
308
+ # Go through nested autosave associations that are loaded in memory (without loading
309
+ # any new ones), and return true if any are changed for autosave.
310
+ # Returns false if already called to prevent an infinite loop.
311
+ def nested_records_changed_for_autosave?
312
+ @_nested_records_changed_for_autosave_already_called ||= false
313
+ return false if @_nested_records_changed_for_autosave_already_called
314
+ begin
315
+ @_nested_records_changed_for_autosave_already_called = true
316
+ self.class._reflections.values.any? do |reflection|
317
+ if reflection.options[:autosave]
318
+ association = association_instance_get(reflection.name)
319
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
320
+ end
321
+ end
322
+ ensure
323
+ @_nested_records_changed_for_autosave_already_called = false
324
+ end
325
+ end
326
+
327
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
328
+ # turned on for the has_one association.
329
+ def validate_has_one_association(reflection)
330
+ association = association_instance_get(reflection.name)
331
+ record = association && association.reader
332
+ return unless record && (record.changed_for_autosave? || custom_validation_context?)
333
+
334
+ inverse_association = reflection.inverse_of && record.association(reflection.inverse_of.name)
335
+ return if inverse_association && (record.validating_belongs_to_for?(inverse_association) ||
336
+ record.autosaving_belongs_to_for?(inverse_association))
337
+
338
+ association_valid?(association, record)
339
+ end
340
+
341
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
342
+ # turned on for the belongs_to association.
343
+ def validate_belongs_to_association(reflection)
344
+ association = association_instance_get(reflection.name)
345
+ record = association && association.reader
346
+ return unless record && (record.changed_for_autosave? || custom_validation_context?)
347
+
348
+ begin
349
+ @validating_belongs_to_for ||= {}
350
+ @validating_belongs_to_for[association] = true
351
+ association_valid?(association, record)
352
+ ensure
353
+ @validating_belongs_to_for[association] = false
354
+ end
355
+ end
356
+
357
+ # Validate the associated records if <tt>:validate</tt> or
358
+ # <tt>:autosave</tt> is turned on for the association specified by
359
+ # +reflection+.
360
+ def validate_collection_association(reflection)
361
+ if association = association_instance_get(reflection.name)
362
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
363
+ records.each { |record| association_valid?(association, record) }
364
+ end
365
+ end
366
+ end
367
+
368
+ # Returns whether or not the association is valid and applies any errors to
369
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
370
+ # enabled records if they're marked_for_destruction? or destroyed.
371
+ def association_valid?(association, record)
372
+ return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
373
+
374
+ context = validation_context if custom_validation_context?
375
+
376
+ unless valid = record.valid?(context)
377
+ if association.options[:autosave]
378
+ record.errors.each { |error|
379
+ self.errors.objects.append(
380
+ Associations::NestedError.new(association, error)
381
+ )
382
+ }
383
+ else
384
+ errors.add(association.reflection.name)
385
+ end
386
+ end
387
+ valid
388
+ end
389
+
390
+ # Is used as an around_save callback to check while saving a collection
391
+ # association whether or not the parent was a new record before saving.
392
+ def around_save_collection_association
393
+ previously_new_record_before_save = (@new_record_before_save ||= false)
394
+ @new_record_before_save = !previously_new_record_before_save && new_record?
395
+
396
+ yield
397
+ ensure
398
+ @new_record_before_save = previously_new_record_before_save
399
+ end
400
+
401
+ # Saves any new associated records, or all loaded autosave associations if
402
+ # <tt>:autosave</tt> is enabled on the association.
403
+ #
404
+ # In addition, it destroys all children that were marked for destruction
405
+ # with #mark_for_destruction.
406
+ #
407
+ # This all happens inside a transaction, _if_ the Transactions module is included into
408
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
409
+ def save_collection_association(reflection)
410
+ if association = association_instance_get(reflection.name)
411
+ autosave = reflection.options[:autosave]
412
+
413
+ # By saving the instance variable in a local variable,
414
+ # we make the whole callback re-entrant.
415
+ new_record_before_save = @new_record_before_save
416
+
417
+ # reconstruct the scope now that we know the owner's id
418
+ association.reset_scope
419
+
420
+ if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
421
+ if autosave
422
+ records_to_destroy = records.select(&:marked_for_destruction?)
423
+ records_to_destroy.each { |record| association.destroy(record) }
424
+ records -= records_to_destroy
425
+ end
426
+
427
+ records.each do |record|
428
+ next if record.destroyed?
429
+
430
+ saved = true
431
+
432
+ if autosave != false && (new_record_before_save || record.new_record?)
433
+ association.set_inverse_instance(record)
434
+
435
+ if autosave
436
+ saved = association.insert_record(record, false)
437
+ elsif !reflection.nested?
438
+ association_saved = association.insert_record(record)
439
+
440
+ if reflection.validate?
441
+ errors.add(reflection.name) unless association_saved
442
+ saved = association_saved
443
+ end
444
+ end
445
+ elsif autosave
446
+ saved = record.save(validate: false)
447
+ end
448
+
449
+ raise(RecordInvalid.new(association.owner)) unless saved
450
+ end
451
+ end
452
+ end
453
+ end
454
+
455
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
456
+ # on the association.
457
+ #
458
+ # In addition, it will destroy the association if it was marked for
459
+ # destruction with #mark_for_destruction.
460
+ #
461
+ # This all happens inside a transaction, _if_ the Transactions module is included into
462
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
463
+ def save_has_one_association(reflection)
464
+ association = association_instance_get(reflection.name)
465
+ return unless association && association.loaded?
466
+
467
+ record = association.load_target
468
+ return unless record && !record.destroyed?
469
+
470
+ autosave = reflection.options[:autosave]
471
+
472
+ if autosave && record.marked_for_destruction?
473
+ record.destroy
474
+ elsif autosave != false
475
+ primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
476
+ primary_key_value = primary_key.map { |key| _read_attribute(key) }
477
+ return unless (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
478
+
479
+ unless reflection.through_reflection
480
+ foreign_key = Array(reflection.foreign_key)
481
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
482
+
483
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
484
+ association_id = _read_attribute(primary_key)
485
+ record[foreign_key] = association_id unless record[foreign_key] == association_id
486
+ end
487
+ association.set_inverse_instance(record)
488
+ end
489
+
490
+ inverse_association = reflection.inverse_of && record.association(reflection.inverse_of.name)
491
+ return if inverse_association && record.autosaving_belongs_to_for?(inverse_association)
492
+
493
+ saved = record.save(validate: !autosave)
494
+ raise ActiveRecord::Rollback if !saved && autosave
495
+ saved
496
+ end
497
+ end
498
+
499
+ # If the record is new or it has changed, returns true.
500
+ def _record_changed?(reflection, record, key)
501
+ record.new_record? ||
502
+ (association_foreign_key_changed?(reflection, record, key) ||
503
+ inverse_polymorphic_association_changed?(reflection, record)) ||
504
+ record.will_save_change_to_attribute?(reflection.foreign_key)
505
+ end
506
+
507
+ def association_foreign_key_changed?(reflection, record, key)
508
+ return false if reflection.through_reflection?
509
+
510
+ foreign_key = Array(reflection.foreign_key)
511
+ return false unless foreign_key.all? { |key| record._has_attribute?(key) }
512
+
513
+ foreign_key.map { |key| record._read_attribute(key) } != Array(key)
514
+ end
515
+
516
+ def inverse_polymorphic_association_changed?(reflection, record)
517
+ return false unless reflection.inverse_of&.polymorphic?
518
+
519
+ class_name = record._read_attribute(reflection.inverse_of.foreign_type)
520
+ reflection.active_record != record.class.polymorphic_class_for(class_name)
521
+ end
522
+
523
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
524
+ #
525
+ # In addition, it will destroy the association if it was marked for destruction.
526
+ def save_belongs_to_association(reflection)
527
+ association = association_instance_get(reflection.name)
528
+ return unless association && association.loaded? && !association.stale_target?
529
+
530
+ record = association.load_target
531
+ if record && !record.destroyed?
532
+ autosave = reflection.options[:autosave]
533
+
534
+ if autosave && record.marked_for_destruction?
535
+ foreign_key = Array(reflection.foreign_key)
536
+ foreign_key.each { |key| self[key] = nil }
537
+ record.destroy
538
+ elsif autosave != false
539
+ saved = if record.new_record? || (autosave && record.changed_for_autosave?)
540
+ begin
541
+ @autosaving_belongs_to_for ||= {}
542
+ @autosaving_belongs_to_for[association] = true
543
+ record.save(validate: !autosave)
544
+ ensure
545
+ @autosaving_belongs_to_for[association] = false
546
+ end
547
+ end
548
+
549
+ if association.updated?
550
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
551
+ foreign_key = Array(reflection.foreign_key)
552
+
553
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
554
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
555
+ association_id = record._read_attribute(primary_key)
556
+ self[foreign_key] = association_id unless self[foreign_key] == association_id
557
+ end
558
+ association.loaded!
559
+ end
560
+
561
+ saved if autosave
562
+ end
563
+ end
564
+ end
565
+
566
+ def compute_primary_key(reflection, record)
567
+ if primary_key_options = reflection.options[:primary_key]
568
+ primary_key_options
569
+ elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
570
+ query_constraints
571
+ elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
572
+ record.class.query_constraints_list
573
+ elsif record.class.composite_primary_key?
574
+ # If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
575
+ primary_key = record.class.primary_key
576
+ primary_key.include?("id") ? "id" : primary_key
577
+ else
578
+ record.class.primary_key
579
+ end
580
+ end
581
+
582
+ def _ensure_no_duplicate_errors
583
+ errors.uniq!
584
+ end
585
+ end
586
+ end