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,968 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/insert_all"
4
+
5
+ module ActiveRecord
6
+ # = Active Record \Persistence
7
+ module Persistence
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
12
+ # The resulting object is returned whether the object was saved successfully to the database or not.
13
+ #
14
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
15
+ # attributes on the objects that are to be created.
16
+ #
17
+ # ==== Examples
18
+ # # Create a single new object
19
+ # User.create(first_name: 'Jamie')
20
+ #
21
+ # # Create an Array of new objects
22
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
23
+ #
24
+ # # Create a single object and pass it into a block to set other attributes.
25
+ # User.create(first_name: 'Jamie') do |u|
26
+ # u.is_admin = false
27
+ # end
28
+ #
29
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
30
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
31
+ # u.is_admin = false
32
+ # end
33
+ def create(attributes = nil, &block)
34
+ if attributes.is_a?(Array)
35
+ attributes.collect { |attr| create(attr, &block) }
36
+ else
37
+ object = new(attributes, &block)
38
+ object.save
39
+ object
40
+ end
41
+ end
42
+
43
+ # Creates an object (or multiple objects) and saves it to the database,
44
+ # if validations pass. Raises a RecordInvalid error if validations fail,
45
+ # unlike Base#create.
46
+ #
47
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes.
48
+ # These describe which attributes to be created on the object, or
49
+ # multiple objects when given an Array of Hashes.
50
+ def create!(attributes = nil, &block)
51
+ if attributes.is_a?(Array)
52
+ attributes.collect { |attr| create!(attr, &block) }
53
+ else
54
+ object = new(attributes, &block)
55
+ object.save!
56
+ object
57
+ end
58
+ end
59
+
60
+ # Builds an object (or multiple objects) and returns either the built object or a list of built
61
+ # objects.
62
+ #
63
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
64
+ # attributes on the objects that are to be built.
65
+ #
66
+ # ==== Examples
67
+ # # Build a single new object
68
+ # User.build(first_name: 'Jamie')
69
+ #
70
+ # # Build an Array of new objects
71
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
72
+ #
73
+ # # Build a single object and pass it into a block to set other attributes.
74
+ # User.build(first_name: 'Jamie') do |u|
75
+ # u.is_admin = false
76
+ # end
77
+ #
78
+ # # Building an Array of new objects using a block, where the block is executed for each object:
79
+ # User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
80
+ # u.is_admin = false
81
+ # end
82
+ def build(attributes = nil, &block)
83
+ if attributes.is_a?(Array)
84
+ attributes.collect { |attr| build(attr, &block) }
85
+ else
86
+ new(attributes, &block)
87
+ end
88
+ end
89
+
90
+ # Given an attributes hash, +instantiate+ returns a new instance of
91
+ # the appropriate class. Accepts only keys as strings.
92
+ #
93
+ # For example, +Post.all+ may return Comments, Messages, and Emails
94
+ # by storing the record's subclass in a +type+ attribute. By calling
95
+ # +instantiate+ instead of +new+, finder methods ensure they get new
96
+ # instances of the appropriate class for each record.
97
+ #
98
+ # See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
99
+ # how this "single-table" inheritance mapping is implemented.
100
+ def instantiate(attributes, column_types = {}, &block)
101
+ klass = discriminate_class_for_record(attributes)
102
+ instantiate_instance_of(klass, attributes, column_types, &block)
103
+ end
104
+
105
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
106
+ # The resulting object is returned whether the object was saved successfully to the database or not.
107
+ #
108
+ # ==== Parameters
109
+ #
110
+ # * +id+ - This should be the id or an array of ids to be updated.
111
+ # Optional argument, defaults to all records in the relation.
112
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
113
+ #
114
+ # ==== Examples
115
+ #
116
+ # # Updates one record
117
+ # Person.update(15, user_name: "Samuel", group: "expert")
118
+ #
119
+ # # Updates multiple records
120
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
121
+ # Person.update(people.keys, people.values)
122
+ #
123
+ # # Updates multiple records from the result of a relation
124
+ # people = Person.where(group: "expert")
125
+ # people.update(group: "masters")
126
+ #
127
+ # Note: Updating a large number of records will run an UPDATE
128
+ # query for each record, which may cause a performance issue.
129
+ # When running callbacks is not needed for each record update,
130
+ # it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
131
+ # for updating all records in a single query.
132
+ def update(id = :all, attributes)
133
+ if id.is_a?(Array)
134
+ if id.any?(ActiveRecord::Base)
135
+ raise ArgumentError,
136
+ "You are passing an array of ActiveRecord::Base instances to `update`. " \
137
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
138
+ end
139
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
140
+ object.update(attributes[idx])
141
+ }
142
+ elsif id == :all
143
+ all.each { |record| record.update(attributes) }
144
+ else
145
+ if ActiveRecord::Base === id
146
+ raise ArgumentError,
147
+ "You are passing an instance of ActiveRecord::Base to `update`. " \
148
+ "Please pass the id of the object by calling `.id`."
149
+ end
150
+ object = find(id)
151
+ object.update(attributes)
152
+ object
153
+ end
154
+ end
155
+
156
+ # Updates the object (or multiple objects) just like #update but calls #update! instead
157
+ # of +update+, so an exception is raised if the record is invalid and saving will fail.
158
+ def update!(id = :all, attributes)
159
+ if id.is_a?(Array)
160
+ if id.any?(ActiveRecord::Base)
161
+ raise ArgumentError,
162
+ "You are passing an array of ActiveRecord::Base instances to `update!`. " \
163
+ "Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
164
+ end
165
+ id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
166
+ object.update!(attributes[idx])
167
+ }
168
+ elsif id == :all
169
+ all.each { |record| record.update!(attributes) }
170
+ else
171
+ if ActiveRecord::Base === id
172
+ raise ArgumentError,
173
+ "You are passing an instance of ActiveRecord::Base to `update!`. " \
174
+ "Please pass the id of the object by calling `.id`."
175
+ end
176
+ object = find(id)
177
+ object.update!(attributes)
178
+ object
179
+ end
180
+ end
181
+
182
+ # Accepts a list of attribute names to be used in the WHERE clause
183
+ # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
184
+ #
185
+ # class Developer < ActiveRecord::Base
186
+ # query_constraints :company_id, :id
187
+ # end
188
+ #
189
+ # developer = Developer.first
190
+ # # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
191
+ # developer.inspect # => #<Developer id: 1, company_id: 1, ...>
192
+ #
193
+ # developer.update!(name: "Nikita")
194
+ # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
195
+ #
196
+ # # It is possible to update an attribute used in the query_constraints clause:
197
+ # developer.update!(company_id: 2)
198
+ # # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
199
+ #
200
+ # developer.name = "Bob"
201
+ # developer.save!
202
+ # # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
203
+ #
204
+ # developer.destroy!
205
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
206
+ #
207
+ # developer.delete
208
+ # # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
209
+ #
210
+ # developer.reload
211
+ # # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
212
+ def query_constraints(*columns_list)
213
+ raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
214
+
215
+ @query_constraints_list = columns_list.map(&:to_s)
216
+ @has_query_constraints = @query_constraints_list
217
+ end
218
+
219
+ def has_query_constraints? # :nodoc:
220
+ @has_query_constraints
221
+ end
222
+
223
+ def query_constraints_list # :nodoc:
224
+ @query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
225
+ primary_key if primary_key.is_a?(Array)
226
+ else
227
+ base_class.query_constraints_list
228
+ end
229
+ end
230
+
231
+ # Returns an array of column names to be used in queries. The source of column
232
+ # names is derived from +query_constraints_list+ or +primary_key+. This method
233
+ # is for internal use when the primary key is to be treated as an array.
234
+ def composite_query_constraints_list # :nodoc:
235
+ @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
236
+ end
237
+
238
+ def _insert_record(connection, values, returning) # :nodoc:
239
+ primary_key = self.primary_key
240
+ primary_key_value = nil
241
+
242
+ if prefetch_primary_key? && primary_key
243
+ values[primary_key] ||= begin
244
+ primary_key_value = next_sequence_value
245
+ _default_attributes[primary_key].with_cast_value(primary_key_value)
246
+ end
247
+ end
248
+
249
+ im = Arel::InsertManager.new(arel_table)
250
+
251
+ if values.empty?
252
+ im.insert(connection.empty_insert_statement_value(primary_key))
253
+ else
254
+ im.insert(values.transform_keys { |name| arel_table[name] })
255
+ end
256
+
257
+ connection.insert(
258
+ im, "#{self} Create", primary_key || false, primary_key_value,
259
+ returning: returning
260
+ )
261
+ end
262
+
263
+ def _update_record(values, constraints) # :nodoc:
264
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
265
+
266
+ default_constraint = build_default_constraint
267
+ constraints << default_constraint if default_constraint
268
+
269
+ if current_scope = self.global_current_scope
270
+ constraints << current_scope.where_clause.ast
271
+ end
272
+
273
+ um = Arel::UpdateManager.new(arel_table)
274
+ um.set(values.transform_keys { |name| arel_table[name] })
275
+ um.wheres = constraints
276
+
277
+ with_connection do |c|
278
+ c.update(um, "#{self} Update")
279
+ end
280
+ end
281
+
282
+ def _delete_record(constraints) # :nodoc:
283
+ constraints = constraints.map { |name, value| predicate_builder[name, value] }
284
+
285
+ default_constraint = build_default_constraint
286
+ constraints << default_constraint if default_constraint
287
+
288
+ if current_scope = self.global_current_scope
289
+ constraints << current_scope.where_clause.ast
290
+ end
291
+
292
+ dm = Arel::DeleteManager.new(arel_table)
293
+ dm.wheres = constraints
294
+
295
+ with_connection do |c|
296
+ c.delete(dm, "#{self} Destroy")
297
+ end
298
+ end
299
+
300
+ private
301
+ def inherited(subclass)
302
+ super
303
+ subclass.class_eval do
304
+ @_query_constraints_list = nil
305
+ @has_query_constraints = false
306
+ end
307
+ end
308
+
309
+ # Given a class, an attributes hash, +instantiate_instance_of+ returns a
310
+ # new instance of the class. Accepts only keys as strings.
311
+ def instantiate_instance_of(klass, attributes, column_types = {}, &block)
312
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
313
+ klass.allocate.init_with_attributes(attributes, &block)
314
+ end
315
+
316
+ # Called by +instantiate+ to decide which class to use for a new
317
+ # record instance.
318
+ #
319
+ # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
320
+ # the single-table inheritance discriminator.
321
+ def discriminate_class_for_record(record)
322
+ self
323
+ end
324
+
325
+ # Called by +_update_record+ and +_delete_record+
326
+ # to build `where` clause from default scopes.
327
+ # Skips empty scopes.
328
+ def build_default_constraint
329
+ return unless default_scopes?(all_queries: true)
330
+
331
+ default_where_clause = default_scoped(all_queries: true).where_clause
332
+ default_where_clause.ast unless default_where_clause.empty?
333
+ end
334
+ end
335
+
336
+ # Returns true if this object hasn't been saved yet -- that is, a record
337
+ # for the object doesn't exist in the database yet; otherwise, returns false.
338
+ def new_record?
339
+ @new_record
340
+ end
341
+
342
+ # Returns true if this object was just created -- that is, prior to the last
343
+ # update or delete, the object didn't exist in the database and new_record? would have
344
+ # returned true.
345
+ def previously_new_record?
346
+ @previously_new_record
347
+ end
348
+
349
+ # Returns true if this object was previously persisted but now it has been deleted.
350
+ def previously_persisted?
351
+ !new_record? && destroyed?
352
+ end
353
+
354
+ # Returns true if this object has been destroyed, otherwise returns false.
355
+ def destroyed?
356
+ @destroyed
357
+ end
358
+
359
+ # Returns true if the record is persisted, i.e. it's not a new record and it was
360
+ # not destroyed, otherwise returns false.
361
+ def persisted?
362
+ !(@new_record || @destroyed)
363
+ end
364
+
365
+ ##
366
+ # :call-seq:
367
+ # save(**options)
368
+ #
369
+ # Saves the model.
370
+ #
371
+ # If the model is new, a record gets created in the database, otherwise
372
+ # the existing record gets updated.
373
+ #
374
+ # By default, save always runs validations. If any of them fail the action
375
+ # is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
376
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
377
+ # ActiveRecord::Validations for more information.
378
+ #
379
+ # By default, #save also sets the +updated_at+/+updated_on+ attributes to
380
+ # the current time. However, if you supply <tt>touch: false</tt>, these
381
+ # timestamps will not be updated.
382
+ #
383
+ # There's a series of callbacks associated with #save. If any of the
384
+ # <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled and
385
+ # #save returns +false+. See ActiveRecord::Callbacks for further
386
+ # details.
387
+ #
388
+ # Attributes marked as readonly are silently ignored if the record is
389
+ # being updated.
390
+ def save(**options, &block)
391
+ create_or_update(**options, &block)
392
+ rescue ActiveRecord::RecordInvalid
393
+ false
394
+ end
395
+
396
+ ##
397
+ # :call-seq:
398
+ # save!(**options)
399
+ #
400
+ # Saves the model.
401
+ #
402
+ # If the model is new, a record gets created in the database, otherwise
403
+ # the existing record gets updated.
404
+ #
405
+ # By default, #save! always runs validations. If any of them fail
406
+ # ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
407
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
408
+ # ActiveRecord::Validations for more information.
409
+ #
410
+ # By default, #save! also sets the +updated_at+/+updated_on+ attributes to
411
+ # the current time. However, if you supply <tt>touch: false</tt>, these
412
+ # timestamps will not be updated.
413
+ #
414
+ # There's a series of callbacks associated with #save!. If any of
415
+ # the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
416
+ # and #save! raises ActiveRecord::RecordNotSaved. See
417
+ # ActiveRecord::Callbacks for further details.
418
+ #
419
+ # Attributes marked as readonly are silently ignored if the record is
420
+ # being updated.
421
+ #
422
+ # Unless an error is raised, returns true.
423
+ def save!(**options, &block)
424
+ create_or_update(**options, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
425
+ end
426
+
427
+ # Deletes the record in the database and freezes this instance to
428
+ # reflect that no changes should be made (since they can't be
429
+ # persisted). Returns the frozen instance.
430
+ #
431
+ # The row is simply removed with an SQL +DELETE+ statement on the
432
+ # record's primary key, and no callbacks are executed.
433
+ #
434
+ # Note that this will also delete records marked as {#readonly?}[rdoc-ref:Core#readonly?].
435
+ #
436
+ # To enforce the object's +before_destroy+ and +after_destroy+
437
+ # callbacks or any <tt>:dependent</tt> association
438
+ # options, use #destroy.
439
+ def delete
440
+ _delete_row if persisted?
441
+ @destroyed = true
442
+ @previously_new_record = false
443
+ freeze
444
+ end
445
+
446
+ # Deletes the record in the database and freezes this instance to reflect
447
+ # that no changes should be made (since they can't be persisted).
448
+ #
449
+ # There's a series of callbacks associated with #destroy. If the
450
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
451
+ # and #destroy returns +false+.
452
+ # See ActiveRecord::Callbacks for further details.
453
+ def destroy
454
+ _raise_readonly_record_error if readonly?
455
+ destroy_associations
456
+ @_trigger_destroy_callback ||= persisted? && destroy_row > 0
457
+ @destroyed = true
458
+ @previously_new_record = false
459
+ freeze
460
+ end
461
+
462
+ # Deletes the record in the database and freezes this instance to reflect
463
+ # that no changes should be made (since they can't be persisted).
464
+ #
465
+ # There's a series of callbacks associated with #destroy!. If the
466
+ # <tt>before_destroy</tt> callback throws +:abort+ the action is cancelled
467
+ # and #destroy! raises ActiveRecord::RecordNotDestroyed.
468
+ # See ActiveRecord::Callbacks for further details.
469
+ def destroy!
470
+ destroy || _raise_record_not_destroyed
471
+ end
472
+
473
+ # Returns an instance of the specified +klass+ with the attributes of the
474
+ # current record. This is mostly useful in relation to single table
475
+ # inheritance (STI) structures where you want a subclass to appear as the
476
+ # superclass. This can be used along with record identification in
477
+ # Action Pack to allow, say, <tt>Client < Company</tt> to do something
478
+ # like render <tt>partial: @client.becomes(Company)</tt> to render that
479
+ # instance using the companies/company partial instead of clients/client.
480
+ #
481
+ # Note: The new instance will share a link to the same attributes as the original class.
482
+ # Therefore the STI column value will still be the same.
483
+ # Any change to the attributes on either instance will affect both instances.
484
+ # This includes any attribute initialization done by the new instance.
485
+ #
486
+ # If you want to change the STI column as well, use #becomes! instead.
487
+ def becomes(klass)
488
+ became = klass.allocate
489
+
490
+ became.send(:initialize) do |becoming|
491
+ @attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
492
+ becoming.instance_variable_set(:@attributes, @attributes)
493
+ becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
494
+ becoming.instance_variable_set(:@new_record, new_record?)
495
+ becoming.instance_variable_set(:@destroyed, destroyed?)
496
+ becoming.errors.copy!(errors)
497
+ end
498
+
499
+ became
500
+ end
501
+
502
+ # Wrapper around #becomes that also changes the instance's STI column value.
503
+ # This is especially useful if you want to persist the changed class in your
504
+ # database.
505
+ #
506
+ # Note: The old instance's STI column value will be changed too, as both objects
507
+ # share the same set of attributes.
508
+ def becomes!(klass)
509
+ became = becomes(klass)
510
+ sti_type = nil
511
+ if !klass.descends_from_active_record?
512
+ sti_type = klass.sti_name
513
+ end
514
+ became.public_send("#{klass.inheritance_column}=", sti_type)
515
+ became
516
+ end
517
+
518
+ # Updates a single attribute and saves the record.
519
+ # This is especially useful for boolean flags on existing records. Also note that
520
+ #
521
+ # * Validation is skipped.
522
+ # * \Callbacks are invoked.
523
+ # * updated_at/updated_on column is updated if that column is available.
524
+ # * Updates all the attributes that are dirty in this object.
525
+ #
526
+ # This method raises an ActiveRecord::ActiveRecordError if the
527
+ # attribute is marked as readonly.
528
+ #
529
+ # Also see #update_column.
530
+ def update_attribute(name, value)
531
+ name = name.to_s
532
+ verify_readonly_attribute(name)
533
+ public_send("#{name}=", value)
534
+
535
+ save(validate: false)
536
+ end
537
+
538
+ # Updates a single attribute and saves the record.
539
+ # This is especially useful for boolean flags on existing records. Also note that
540
+ #
541
+ # * Validation is skipped.
542
+ # * \Callbacks are invoked.
543
+ # * updated_at/updated_on column is updated if that column is available.
544
+ # * Updates all the attributes that are dirty in this object.
545
+ #
546
+ # This method raises an ActiveRecord::ActiveRecordError if the
547
+ # attribute is marked as readonly.
548
+ #
549
+ # If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
550
+ # and #update_attribute! raises ActiveRecord::RecordNotSaved. See
551
+ # ActiveRecord::Callbacks for further details.
552
+ def update_attribute!(name, value)
553
+ name = name.to_s
554
+ verify_readonly_attribute(name)
555
+ public_send("#{name}=", value)
556
+
557
+ save!(validate: false)
558
+ end
559
+
560
+ # Updates the attributes of the model from the passed-in hash and saves the
561
+ # record, all wrapped in a transaction. If the object is invalid, the saving
562
+ # will fail and false will be returned.
563
+ def update(attributes)
564
+ # The following transaction covers any possible database side-effects of the
565
+ # attributes assignment. For example, setting the IDs of a child collection.
566
+ with_transaction_returning_status do
567
+ assign_attributes(attributes)
568
+ save
569
+ end
570
+ end
571
+
572
+ # Updates its receiver just like #update but calls #save! instead
573
+ # of +save+, so an exception is raised if the record is invalid and saving will fail.
574
+ def update!(attributes)
575
+ # The following transaction covers any possible database side-effects of the
576
+ # attributes assignment. For example, setting the IDs of a child collection.
577
+ with_transaction_returning_status do
578
+ assign_attributes(attributes)
579
+ save!
580
+ end
581
+ end
582
+
583
+ # Equivalent to <code>update_columns(name => value)</code>.
584
+ def update_column(name, value)
585
+ update_columns(name => value)
586
+ end
587
+
588
+ # Updates the attributes directly in the database issuing an UPDATE SQL
589
+ # statement and sets them in the receiver:
590
+ #
591
+ # user.update_columns(last_request_at: Time.current)
592
+ #
593
+ # This is the fastest way to update attributes because it goes straight to
594
+ # the database, but take into account that in consequence the regular update
595
+ # procedures are totally bypassed. In particular:
596
+ #
597
+ # * \Validations are skipped.
598
+ # * \Callbacks are skipped.
599
+ # * +updated_at+/+updated_on+ are not updated.
600
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
601
+ #
602
+ # This method raises an ActiveRecord::ActiveRecordError when called on new
603
+ # objects, or when at least one of the attributes is marked as readonly.
604
+ def update_columns(attributes)
605
+ raise ActiveRecordError, "cannot update a new record" if new_record?
606
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
607
+ _raise_readonly_record_error if readonly?
608
+
609
+ attributes = attributes.transform_keys do |key|
610
+ name = key.to_s
611
+ name = self.class.attribute_aliases[name] || name
612
+ verify_readonly_attribute(name) || name
613
+ end
614
+
615
+ update_constraints = _query_constraints_hash
616
+ attributes = attributes.each_with_object({}) do |(k, v), h|
617
+ h[k] = @attributes.write_cast_value(k, v)
618
+ clear_attribute_change(k)
619
+ end
620
+
621
+ affected_rows = self.class._update_record(
622
+ attributes,
623
+ update_constraints
624
+ )
625
+
626
+ affected_rows == 1
627
+ end
628
+
629
+ # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
630
+ # The increment is performed directly on the underlying attribute, no setter is invoked.
631
+ # Only makes sense for number-based attributes. Returns +self+.
632
+ def increment(attribute, by = 1)
633
+ self[attribute] ||= 0
634
+ self[attribute] += by
635
+ self
636
+ end
637
+
638
+ # Wrapper around #increment that writes the update to the database.
639
+ # Only +attribute+ is updated; the record itself is not saved.
640
+ # This means that any other modified attributes will still be dirty.
641
+ # Validations and callbacks are skipped. Supports the +touch+ option from
642
+ # +update_counters+, see that for more.
643
+ # Returns +self+.
644
+ def increment!(attribute, by = 1, touch: nil)
645
+ increment(attribute, by)
646
+ change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
647
+ self.class.update_counters(id, attribute => change, touch: touch)
648
+ public_send(:"clear_#{attribute}_change")
649
+ self
650
+ end
651
+
652
+ # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
653
+ # The decrement is performed directly on the underlying attribute, no setter is invoked.
654
+ # Only makes sense for number-based attributes. Returns +self+.
655
+ def decrement(attribute, by = 1)
656
+ increment(attribute, -by)
657
+ end
658
+
659
+ # Wrapper around #decrement that writes the update to the database.
660
+ # Only +attribute+ is updated; the record itself is not saved.
661
+ # This means that any other modified attributes will still be dirty.
662
+ # Validations and callbacks are skipped. Supports the +touch+ option from
663
+ # +update_counters+, see that for more.
664
+ # Returns +self+.
665
+ def decrement!(attribute, by = 1, touch: nil)
666
+ increment!(attribute, -by, touch: touch)
667
+ end
668
+
669
+ # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
670
+ # if the predicate returns +true+ the attribute will become +false+. This
671
+ # method toggles directly the underlying value without calling any setter.
672
+ # Returns +self+.
673
+ #
674
+ # Example:
675
+ #
676
+ # user = User.first
677
+ # user.banned? # => false
678
+ # user.toggle(:banned)
679
+ # user.banned? # => true
680
+ #
681
+ def toggle(attribute)
682
+ self[attribute] = !public_send("#{attribute}?")
683
+ self
684
+ end
685
+
686
+ # Wrapper around #toggle that saves the record. This method differs from
687
+ # its non-bang version in the sense that it passes through the attribute setter.
688
+ # Saving is not subjected to validation checks. Returns +true+ if the
689
+ # record could be saved.
690
+ def toggle!(attribute)
691
+ toggle(attribute).update_attribute(attribute, self[attribute])
692
+ end
693
+
694
+ # Reloads the record from the database.
695
+ #
696
+ # This method finds the record by its primary key (which could be assigned
697
+ # manually) and modifies the receiver in-place:
698
+ #
699
+ # account = Account.new
700
+ # # => #<Account id: nil, email: nil>
701
+ # account.id = 1
702
+ # account.reload
703
+ # # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]]
704
+ # # => #<Account id: 1, email: 'account@example.com'>
705
+ #
706
+ # Attributes are reloaded from the database, and caches busted, in
707
+ # particular the associations cache and the QueryCache.
708
+ #
709
+ # If the record no longer exists in the database ActiveRecord::RecordNotFound
710
+ # is raised. Otherwise, in addition to the in-place modification the method
711
+ # returns +self+ for convenience.
712
+ #
713
+ # The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
714
+ #
715
+ # reload(lock: true) # reload with pessimistic locking
716
+ #
717
+ # Reloading is commonly used in test suites to test something is actually
718
+ # written to the database, or when some action modifies the corresponding
719
+ # row in the database but not the object in memory:
720
+ #
721
+ # assert account.deposit!(25)
722
+ # assert_equal 25, account.credit # check it is updated in memory
723
+ # assert_equal 25, account.reload.credit # check it is also persisted
724
+ #
725
+ # Another common use case is optimistic locking handling:
726
+ #
727
+ # def with_optimistic_retry
728
+ # begin
729
+ # yield
730
+ # rescue ActiveRecord::StaleObjectError
731
+ # begin
732
+ # # Reload lock_version in particular.
733
+ # reload
734
+ # rescue ActiveRecord::RecordNotFound
735
+ # # If the record is gone there is nothing to do.
736
+ # else
737
+ # retry
738
+ # end
739
+ # end
740
+ # end
741
+ #
742
+ def reload(options = nil)
743
+ self.class.connection_pool.clear_query_cache
744
+
745
+ fresh_object = if apply_scoping?(options)
746
+ _find_record((options || {}).merge(all_queries: true))
747
+ else
748
+ self.class.unscoped { _find_record(options) }
749
+ end
750
+
751
+ @association_cache = fresh_object.instance_variable_get(:@association_cache)
752
+ @association_cache.each_value { |association| association.owner = self }
753
+ @attributes = fresh_object.instance_variable_get(:@attributes)
754
+ @new_record = false
755
+ @previously_new_record = false
756
+ self
757
+ end
758
+
759
+ # Saves the record with the updated_at/on attributes set to the current time
760
+ # or the time specified.
761
+ # Please note that no validation is performed and only the +after_touch+,
762
+ # +after_commit+ and +after_rollback+ callbacks are executed.
763
+ #
764
+ # This method can be passed attribute names and an optional time argument.
765
+ # If attribute names are passed, they are updated along with updated_at/on
766
+ # attributes. If no time argument is passed, the current time is used as default.
767
+ #
768
+ # product.touch # updates updated_at/on with current time
769
+ # product.touch(time: Time.new(2015, 2, 16, 0, 0, 0)) # updates updated_at/on with specified time
770
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
771
+ # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
772
+ #
773
+ # If used along with {belongs_to}[rdoc-ref:Associations::ClassMethods#belongs_to]
774
+ # then +touch+ will invoke +touch+ method on associated object.
775
+ #
776
+ # class Brake < ActiveRecord::Base
777
+ # belongs_to :car, touch: true
778
+ # end
779
+ #
780
+ # class Car < ActiveRecord::Base
781
+ # belongs_to :corporation, touch: true
782
+ # end
783
+ #
784
+ # # triggers @brake.car.touch and @brake.car.corporation.touch
785
+ # @brake.touch
786
+ #
787
+ # Note that +touch+ must be used on a persisted object, or else an
788
+ # ActiveRecordError will be thrown. For example:
789
+ #
790
+ # ball = Ball.new
791
+ # ball.touch(:updated_at) # => raises ActiveRecordError
792
+ #
793
+ def touch(*names, time: nil)
794
+ _raise_record_not_touched_error unless persisted?
795
+ _raise_readonly_record_error if readonly?
796
+
797
+ attribute_names = timestamp_attributes_for_update_in_model
798
+ attribute_names = (attribute_names | names).map! do |name|
799
+ name = name.to_s
800
+ name = self.class.attribute_aliases[name] || name
801
+ verify_readonly_attribute(name)
802
+ name
803
+ end
804
+
805
+ unless attribute_names.empty?
806
+ affected_rows = _touch_row(attribute_names, time)
807
+ @_trigger_update_callback = affected_rows == 1
808
+ else
809
+ true
810
+ end
811
+ end
812
+
813
+ private
814
+ def init_internals
815
+ super
816
+ @_trigger_destroy_callback = @_trigger_update_callback = nil
817
+ @previously_new_record = false
818
+ end
819
+
820
+ def strict_loaded_associations
821
+ @association_cache.find_all do |_, assoc|
822
+ assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
823
+ end.map(&:first)
824
+ end
825
+
826
+ def _find_record(options)
827
+ all_queries = options ? options[:all_queries] : nil
828
+ base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
829
+
830
+ if options && options[:lock]
831
+ base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
832
+ else
833
+ base.find_by!(_in_memory_query_constraints_hash)
834
+ end
835
+ end
836
+
837
+ def _in_memory_query_constraints_hash
838
+ if self.class.query_constraints_list.nil?
839
+ { @primary_key => id }
840
+ else
841
+ self.class.query_constraints_list.index_with do |column_name|
842
+ attribute(column_name)
843
+ end
844
+ end
845
+ end
846
+
847
+ def apply_scoping?(options)
848
+ !(options && options[:unscoped]) &&
849
+ (self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
850
+ end
851
+
852
+ def _query_constraints_hash
853
+ if self.class.query_constraints_list.nil?
854
+ { @primary_key => id_in_database }
855
+ else
856
+ self.class.query_constraints_list.index_with do |column_name|
857
+ attribute_in_database(column_name)
858
+ end
859
+ end
860
+ end
861
+
862
+ # A hook to be overridden by association modules.
863
+ def destroy_associations
864
+ end
865
+
866
+ def destroy_row
867
+ _delete_row
868
+ end
869
+
870
+ def _delete_row
871
+ self.class._delete_record(_query_constraints_hash)
872
+ end
873
+
874
+ def _touch_row(attribute_names, time)
875
+ time ||= current_time_from_proper_timezone
876
+
877
+ attribute_names.each do |attr_name|
878
+ _write_attribute(attr_name, time)
879
+ end
880
+
881
+ _update_row(attribute_names, "touch")
882
+ end
883
+
884
+ def _update_row(attribute_names, attempted_action = "update")
885
+ self.class._update_record(
886
+ attributes_with_values(attribute_names),
887
+ _query_constraints_hash
888
+ )
889
+ end
890
+
891
+ def create_or_update(**, &block)
892
+ _raise_readonly_record_error if readonly?
893
+ return false if destroyed?
894
+ result = new_record? ? _create_record(&block) : _update_record(&block)
895
+ result != false
896
+ end
897
+
898
+ # Updates the associated record with values matching those of the instance attributes.
899
+ # Returns the number of affected rows.
900
+ def _update_record(attribute_names = self.attribute_names)
901
+ attribute_names = attributes_for_update(attribute_names)
902
+
903
+ if attribute_names.empty?
904
+ affected_rows = 0
905
+ @_trigger_update_callback = true
906
+ else
907
+ affected_rows = _update_row(attribute_names)
908
+ @_trigger_update_callback = affected_rows == 1
909
+ end
910
+
911
+ @previously_new_record = false
912
+
913
+ yield(self) if block_given?
914
+
915
+ affected_rows
916
+ end
917
+
918
+ # Creates a record with values matching those of the instance attributes
919
+ # and returns its id.
920
+ def _create_record(attribute_names = self.attribute_names)
921
+ attribute_names = attributes_for_create(attribute_names)
922
+
923
+ self.class.with_connection do |connection|
924
+ returning_columns = self.class._returning_columns_for_insert(connection)
925
+
926
+ returning_values = self.class._insert_record(
927
+ connection,
928
+ attributes_with_values(attribute_names),
929
+ returning_columns
930
+ )
931
+
932
+ returning_columns.zip(returning_values).each do |column, value|
933
+ _write_attribute(column, value) if !_read_attribute(column)
934
+ end if returning_values
935
+ end
936
+
937
+ @new_record = false
938
+ @previously_new_record = true
939
+
940
+ yield(self) if block_given?
941
+
942
+ id
943
+ end
944
+
945
+ def verify_readonly_attribute(name)
946
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attribute?(name)
947
+ end
948
+
949
+ def _raise_record_not_destroyed
950
+ @_association_destroy_exception ||= nil
951
+ key = self.class.primary_key
952
+ raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
953
+ ensure
954
+ @_association_destroy_exception = nil
955
+ end
956
+
957
+ def _raise_readonly_record_error
958
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly"
959
+ end
960
+
961
+ def _raise_record_not_touched_error
962
+ raise ActiveRecordError, <<~MSG.squish
963
+ Cannot touch on a new or destroyed record object. Consider using
964
+ persisted?, new_record?, or destroyed? before touching.
965
+ MSG
966
+ end
967
+ end
968
+ end