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,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/atomic/atomic_boolean"
4
+ require "concurrent/atomic/read_write_lock"
5
+
6
+ module ActiveRecord
7
+ class AsynchronousQueriesTracker # :nodoc:
8
+ class Session # :nodoc:
9
+ def initialize
10
+ @active = Concurrent::AtomicBoolean.new(true)
11
+ @lock = Concurrent::ReadWriteLock.new
12
+ end
13
+
14
+ def active?
15
+ @active.true?
16
+ end
17
+
18
+ def synchronize(&block)
19
+ @lock.with_read_lock(&block)
20
+ end
21
+
22
+ def finalize(wait = false)
23
+ @active.make_false
24
+ if wait
25
+ # Wait until all thread with a read lock are done
26
+ @lock.with_write_lock { }
27
+ end
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def install_executor_hooks(executor = ActiveSupport::Executor)
33
+ executor.register_hook(self)
34
+ end
35
+
36
+ def run
37
+ ActiveRecord::Base.asynchronous_queries_tracker.tap(&:start_session)
38
+ end
39
+
40
+ def complete(asynchronous_queries_tracker)
41
+ asynchronous_queries_tracker.finalize_session
42
+ end
43
+ end
44
+
45
+ def initialize
46
+ @stack = []
47
+ end
48
+
49
+ def current_session
50
+ @stack.last or raise ActiveRecordError, "Can't perform asynchronous queries without a query session"
51
+ end
52
+
53
+ def start_session
54
+ session = Session.new
55
+ @stack << session
56
+ end
57
+
58
+ def finalize_session(wait = false)
59
+ session = @stack.pop
60
+ session&.finalize(wait)
61
+ self
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeAssignment
5
+ private
6
+ def _assign_attributes(attributes)
7
+ multi_parameter_attributes = nested_parameter_attributes = nil
8
+
9
+ attributes.each do |k, v|
10
+ key = k.to_s
11
+
12
+ if key.include?("(")
13
+ (multi_parameter_attributes ||= {})[key] = v
14
+ elsif v.is_a?(Hash)
15
+ (nested_parameter_attributes ||= {})[key] = v
16
+ else
17
+ _assign_attribute(key, v)
18
+ end
19
+ end
20
+
21
+ assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
22
+ assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
23
+ end
24
+
25
+ # Assign any deferred nested attributes after the base attributes have been set.
26
+ def assign_nested_parameter_attributes(pairs)
27
+ pairs.each { |k, v| _assign_attribute(k, v) }
28
+ end
29
+
30
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
31
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
32
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
33
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
34
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
35
+ # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
36
+ def assign_multiparameter_attributes(pairs)
37
+ execute_callstack_for_multiparameter_attributes(
38
+ extract_callstack_for_multiparameter_attributes(pairs)
39
+ )
40
+ end
41
+
42
+ def execute_callstack_for_multiparameter_attributes(callstack)
43
+ errors = []
44
+ callstack.each do |name, values_with_empty_parameters|
45
+ if values_with_empty_parameters.each_value.all?(NilClass)
46
+ values = nil
47
+ else
48
+ values = values_with_empty_parameters
49
+ end
50
+ send("#{name}=", values)
51
+ rescue => ex
52
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
53
+ end
54
+ unless errors.empty?
55
+ error_descriptions = errors.map(&:message).join(",")
56
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
57
+ end
58
+ end
59
+
60
+ def extract_callstack_for_multiparameter_attributes(pairs)
61
+ attributes = {}
62
+
63
+ pairs.each do |(multiparameter_name, value)|
64
+ attribute_name = multiparameter_name.split("(").first
65
+ attributes[attribute_name] ||= {}
66
+
67
+ parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
68
+ attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
69
+ end
70
+
71
+ attributes
72
+ end
73
+
74
+ def type_cast_attribute_value(multiparameter_name, value)
75
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
76
+ end
77
+
78
+ def find_parameter_position(multiparameter_name)
79
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ # = Active Record Attribute Methods Before Type Cast
6
+ #
7
+ # ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to
8
+ # read the value of the attributes before typecasting and deserialization.
9
+ #
10
+ # class Task < ActiveRecord::Base
11
+ # end
12
+ #
13
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
14
+ # task.id # => 1
15
+ # task.completed_on # => Sun, 21 Oct 2012
16
+ #
17
+ # task.attributes_before_type_cast
18
+ # # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
19
+ # task.read_attribute_before_type_cast('id') # => "1"
20
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
21
+ #
22
+ # In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
23
+ # it declares a method for all attributes with the <tt>*_before_type_cast</tt>
24
+ # suffix.
25
+ #
26
+ # task.id_before_type_cast # => "1"
27
+ # task.completed_on_before_type_cast # => "2012-10-21"
28
+ module BeforeTypeCast
29
+ extend ActiveSupport::Concern
30
+
31
+ included do
32
+ attribute_method_suffix "_before_type_cast", "_for_database", parameters: false
33
+ attribute_method_suffix "_came_from_user?", parameters: false
34
+ end
35
+
36
+ # Returns the value of the attribute identified by +attr_name+ before
37
+ # typecasting and deserialization.
38
+ #
39
+ # class Task < ActiveRecord::Base
40
+ # end
41
+ #
42
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
43
+ # task.read_attribute('id') # => 1
44
+ # task.read_attribute_before_type_cast('id') # => '1'
45
+ # task.read_attribute('completed_on') # => Sun, 21 Oct 2012
46
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
47
+ # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
48
+ def read_attribute_before_type_cast(attr_name)
49
+ name = attr_name.to_s
50
+ name = self.class.attribute_aliases[name] || name
51
+
52
+ attribute_before_type_cast(name)
53
+ end
54
+
55
+ # Returns the value of the attribute identified by +attr_name+ after
56
+ # serialization.
57
+ #
58
+ # class Book < ActiveRecord::Base
59
+ # enum :status, { draft: 1, published: 2 }
60
+ # end
61
+ #
62
+ # book = Book.new(status: "published")
63
+ # book.read_attribute(:status) # => "published"
64
+ # book.read_attribute_for_database(:status) # => 2
65
+ def read_attribute_for_database(attr_name)
66
+ name = attr_name.to_s
67
+ name = self.class.attribute_aliases[name] || name
68
+
69
+ attribute_for_database(name)
70
+ end
71
+
72
+ # Returns a hash of attributes before typecasting and deserialization.
73
+ #
74
+ # class Task < ActiveRecord::Base
75
+ # end
76
+ #
77
+ # task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
78
+ # task.attributes
79
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
80
+ # task.attributes_before_type_cast
81
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
82
+ def attributes_before_type_cast
83
+ @attributes.values_before_type_cast
84
+ end
85
+
86
+ # Returns a hash of attributes for assignment to the database.
87
+ def attributes_for_database
88
+ @attributes.values_for_database
89
+ end
90
+
91
+ private
92
+ # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
93
+ def attribute_before_type_cast(attr_name)
94
+ @attributes[attr_name].value_before_type_cast
95
+ end
96
+
97
+ def attribute_for_database(attr_name)
98
+ @attributes[attr_name].value_for_database
99
+ end
100
+
101
+ def attribute_came_from_user?(attr_name)
102
+ @attributes[attr_name].came_from_user?
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module CompositePrimaryKey # :nodoc:
6
+ # Returns the primary key column's value. If the primary key is composite,
7
+ # returns an array of the primary key column values.
8
+ def id
9
+ if self.class.composite_primary_key?
10
+ @primary_key.map { |pk| _read_attribute(pk) }
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def primary_key_values_present? # :nodoc:
17
+ if self.class.composite_primary_key?
18
+ id.all?
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ # Sets the primary key column's value. If the primary key is composite,
25
+ # raises TypeError when the set value not enumerable.
26
+ def id=(value)
27
+ if self.class.composite_primary_key?
28
+ raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
29
+ @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ # Queries the primary key column's value. If the primary key is composite,
36
+ # all primary key column values must be queryable.
37
+ def id?
38
+ if self.class.composite_primary_key?
39
+ @primary_key.all? { |col| _query_attribute(col) }
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ # Returns the primary key column's value before type cast. If the primary key is composite,
46
+ # returns an array of primary key column values before type cast.
47
+ def id_before_type_cast
48
+ if self.class.composite_primary_key?
49
+ @primary_key.map { |col| attribute_before_type_cast(col) }
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # Returns the primary key column's previous value. If the primary key is composite,
56
+ # returns an array of primary key column previous values.
57
+ def id_was
58
+ if self.class.composite_primary_key?
59
+ @primary_key.map { |col| attribute_was(col) }
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ # Returns the primary key column's value from the database. If the primary key is composite,
66
+ # returns an array of primary key column values from database.
67
+ def id_in_database
68
+ if self.class.composite_primary_key?
69
+ @primary_key.map { |col| attribute_in_database(col) }
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ def id_for_database # :nodoc:
76
+ if self.class.composite_primary_key?
77
+ @primary_key.map { |col| @attributes[col].value_for_database }
78
+ else
79
+ super
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+
5
+ module ActiveRecord
6
+ module AttributeMethods
7
+ # = Active Record Attribute Methods \Dirty
8
+ #
9
+ # Provides a way to track changes in your Active Record models. It adds all
10
+ # methods from ActiveModel::Dirty and adds database-specific methods.
11
+ #
12
+ # A newly created +Person+ object is unchanged:
13
+ #
14
+ # class Person < ActiveRecord::Base
15
+ # end
16
+ #
17
+ # person = Person.create(name: "Allison")
18
+ # person.changed? # => false
19
+ #
20
+ # Change the name:
21
+ #
22
+ # person.name = 'Alice'
23
+ # person.name_in_database # => "Allison"
24
+ # person.will_save_change_to_name? # => true
25
+ # person.name_change_to_be_saved # => ["Allison", "Alice"]
26
+ # person.changes_to_save # => {"name"=>["Allison", "Alice"]}
27
+ #
28
+ # Save the changes:
29
+ #
30
+ # person.save
31
+ # person.name_in_database # => "Alice"
32
+ # person.saved_change_to_name? # => true
33
+ # person.saved_change_to_name # => ["Allison", "Alice"]
34
+ # person.name_before_last_save # => "Allison"
35
+ #
36
+ # Similar to ActiveModel::Dirty, methods can be invoked as
37
+ # +saved_change_to_name?+ or by passing an argument to the generic method
38
+ # <tt>saved_change_to_attribute?("name")</tt>.
39
+ module Dirty
40
+ extend ActiveSupport::Concern
41
+
42
+ include ActiveModel::Dirty
43
+
44
+ included do
45
+ if self < ::ActiveRecord::Timestamp
46
+ raise "You cannot include Dirty after Timestamp"
47
+ end
48
+
49
+ class_attribute :partial_updates, instance_writer: false, default: true
50
+ class_attribute :partial_inserts, instance_writer: false, default: true
51
+
52
+ # Attribute methods for "changed in last call to save?"
53
+ attribute_method_affix(prefix: "saved_change_to_", suffix: "?", parameters: "**options")
54
+ attribute_method_prefix("saved_change_to_", parameters: false)
55
+ attribute_method_suffix("_before_last_save", parameters: false)
56
+
57
+ # Attribute methods for "will change if I call save?"
58
+ attribute_method_affix(prefix: "will_save_change_to_", suffix: "?", parameters: "**options")
59
+ attribute_method_suffix("_change_to_be_saved", "_in_database", parameters: false)
60
+ end
61
+
62
+ # <tt>reload</tt> the record and clears changed attributes.
63
+ def reload(*)
64
+ super.tap do
65
+ @mutations_before_last_save = nil
66
+ @mutations_from_database = nil
67
+ end
68
+ end
69
+
70
+ # Did this attribute change when we last saved?
71
+ #
72
+ # This method is useful in after callbacks to determine if an attribute
73
+ # was changed during the save that triggered the callbacks to run. It can
74
+ # be invoked as +saved_change_to_name?+ instead of
75
+ # <tt>saved_change_to_attribute?("name")</tt>.
76
+ #
77
+ # ==== Options
78
+ #
79
+ # [+from+]
80
+ # When specified, this method will return false unless the original
81
+ # value is equal to the given value.
82
+ #
83
+ # [+to+]
84
+ # When specified, this method will return false unless the value will be
85
+ # changed to the given value.
86
+ def saved_change_to_attribute?(attr_name, **options)
87
+ mutations_before_last_save.changed?(attr_name.to_s, **options)
88
+ end
89
+
90
+ # Returns the change to an attribute during the last save. If the
91
+ # attribute was changed, the result will be an array containing the
92
+ # original value and the saved value.
93
+ #
94
+ # This method is useful in after callbacks, to see the change in an
95
+ # attribute during the save that triggered the callbacks to run. It can be
96
+ # invoked as +saved_change_to_name+ instead of
97
+ # <tt>saved_change_to_attribute("name")</tt>.
98
+ def saved_change_to_attribute(attr_name)
99
+ mutations_before_last_save.change_to_attribute(attr_name.to_s)
100
+ end
101
+
102
+ # Returns the original value of an attribute before the last save.
103
+ #
104
+ # This method is useful in after callbacks to get the original value of an
105
+ # attribute before the save that triggered the callbacks to run. It can be
106
+ # invoked as +name_before_last_save+ instead of
107
+ # <tt>attribute_before_last_save("name")</tt>.
108
+ def attribute_before_last_save(attr_name)
109
+ mutations_before_last_save.original_value(attr_name.to_s)
110
+ end
111
+
112
+ # Did the last call to +save+ have any changes to change?
113
+ def saved_changes?
114
+ mutations_before_last_save.any_changes?
115
+ end
116
+
117
+ # Returns a hash containing all the changes that were just saved.
118
+ def saved_changes
119
+ mutations_before_last_save.changes
120
+ end
121
+
122
+ # Will this attribute change the next time we save?
123
+ #
124
+ # This method is useful in validations and before callbacks to determine
125
+ # if the next call to +save+ will change a particular attribute. It can be
126
+ # invoked as +will_save_change_to_name?+ instead of
127
+ # <tt>will_save_change_to_attribute?("name")</tt>.
128
+ #
129
+ # ==== Options
130
+ #
131
+ # [+from+]
132
+ # When specified, this method will return false unless the original
133
+ # value is equal to the given value.
134
+ #
135
+ # [+to+]
136
+ # When specified, this method will return false unless the value will be
137
+ # changed to the given value.
138
+ def will_save_change_to_attribute?(attr_name, **options)
139
+ mutations_from_database.changed?(attr_name.to_s, **options)
140
+ end
141
+
142
+ # Returns the change to an attribute that will be persisted during the
143
+ # next save.
144
+ #
145
+ # This method is useful in validations and before callbacks, to see the
146
+ # change to an attribute that will occur when the record is saved. It can
147
+ # be invoked as +name_change_to_be_saved+ instead of
148
+ # <tt>attribute_change_to_be_saved("name")</tt>.
149
+ #
150
+ # If the attribute will change, the result will be an array containing the
151
+ # original value and the new value about to be saved.
152
+ def attribute_change_to_be_saved(attr_name)
153
+ mutations_from_database.change_to_attribute(attr_name.to_s)
154
+ end
155
+
156
+ # Returns the value of an attribute in the database, as opposed to the
157
+ # in-memory value that will be persisted the next time the record is
158
+ # saved.
159
+ #
160
+ # This method is useful in validations and before callbacks, to see the
161
+ # original value of an attribute prior to any changes about to be
162
+ # saved. It can be invoked as +name_in_database+ instead of
163
+ # <tt>attribute_in_database("name")</tt>.
164
+ def attribute_in_database(attr_name)
165
+ mutations_from_database.original_value(attr_name.to_s)
166
+ end
167
+
168
+ # Will the next call to +save+ have any changes to persist?
169
+ def has_changes_to_save?
170
+ mutations_from_database.any_changes?
171
+ end
172
+
173
+ # Returns a hash containing all the changes that will be persisted during
174
+ # the next save.
175
+ def changes_to_save
176
+ mutations_from_database.changes
177
+ end
178
+
179
+ # Returns an array of the names of any attributes that will change when
180
+ # the record is next saved.
181
+ def changed_attribute_names_to_save
182
+ mutations_from_database.changed_attribute_names
183
+ end
184
+
185
+ # Returns a hash of the attributes that will change when the record is
186
+ # next saved.
187
+ #
188
+ # The hash keys are the attribute names, and the hash values are the
189
+ # original attribute values in the database (as opposed to the in-memory
190
+ # values about to be saved).
191
+ def attributes_in_database
192
+ mutations_from_database.changed_values
193
+ end
194
+
195
+ private
196
+ def init_internals
197
+ super
198
+ @mutations_before_last_save = nil
199
+ @mutations_from_database = nil
200
+ @_touch_attr_names = nil
201
+ @_skip_dirty_tracking = nil
202
+ end
203
+
204
+ def _touch_row(attribute_names, time)
205
+ @_touch_attr_names = Set.new(attribute_names)
206
+
207
+ affected_rows = super
208
+
209
+ if @_skip_dirty_tracking ||= false
210
+ clear_attribute_changes(@_touch_attr_names)
211
+ return affected_rows
212
+ end
213
+
214
+ changes = {}
215
+ @attributes.keys.each do |attr_name|
216
+ next if @_touch_attr_names.include?(attr_name)
217
+
218
+ if attribute_changed?(attr_name)
219
+ changes[attr_name] = _read_attribute(attr_name)
220
+ _write_attribute(attr_name, attribute_was(attr_name))
221
+ clear_attribute_change(attr_name)
222
+ end
223
+ end
224
+
225
+ changes_applied
226
+ changes.each { |attr_name, value| _write_attribute(attr_name, value) }
227
+
228
+ affected_rows
229
+ ensure
230
+ @_touch_attr_names, @_skip_dirty_tracking = nil, nil
231
+ end
232
+
233
+ def _update_record(attribute_names = attribute_names_for_partial_updates)
234
+ affected_rows = super
235
+ changes_applied
236
+ affected_rows
237
+ end
238
+
239
+ def _create_record(attribute_names = attribute_names_for_partial_inserts)
240
+ id = super
241
+ changes_applied
242
+ id
243
+ end
244
+
245
+ def attribute_names_for_partial_updates
246
+ partial_updates? ? changed_attribute_names_to_save : attribute_names
247
+ end
248
+
249
+ def attribute_names_for_partial_inserts
250
+ if partial_inserts?
251
+ changed_attribute_names_to_save
252
+ else
253
+ attribute_names.reject do |attr_name|
254
+ if column_for_attribute(attr_name).auto_populated?
255
+ !attribute_changed?(attr_name)
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end