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,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class MySQLDatabaseTasks # :nodoc:
6
+ def self.using_database_configurations?
7
+ true
8
+ end
9
+
10
+ def initialize(db_config)
11
+ @db_config = db_config
12
+ @configuration_hash = db_config.configuration_hash
13
+ end
14
+
15
+ def create
16
+ establish_connection(configuration_hash_without_database)
17
+ connection.create_database(db_config.database, creation_options)
18
+ establish_connection
19
+ end
20
+
21
+ def drop
22
+ establish_connection
23
+ connection.drop_database(db_config.database)
24
+ end
25
+
26
+ def purge
27
+ establish_connection(configuration_hash_without_database)
28
+ connection.recreate_database(db_config.database, creation_options)
29
+ establish_connection
30
+ end
31
+
32
+ def charset
33
+ connection.charset
34
+ end
35
+
36
+ def collation
37
+ connection.collation
38
+ end
39
+
40
+ def structure_dump(filename, extra_flags)
41
+ args = prepare_command_options
42
+ args.concat(["--result-file", "#{filename}"])
43
+ args.concat(["--no-data"])
44
+ args.concat(["--routines"])
45
+ args.concat(["--skip-comments"])
46
+
47
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
48
+ if ignore_tables.any?
49
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
50
+ args += ignore_tables.map { |table| "--ignore-table=#{db_config.database}.#{table}" }
51
+ end
52
+
53
+ args.concat([db_config.database.to_s])
54
+ args.unshift(*extra_flags) if extra_flags
55
+
56
+ run_cmd("mysqldump", args, "dumping")
57
+ end
58
+
59
+ def structure_load(filename, extra_flags)
60
+ args = prepare_command_options
61
+ args.concat(["--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
62
+ args.concat(["--database", db_config.database.to_s])
63
+ args.unshift(*extra_flags) if extra_flags
64
+
65
+ run_cmd("mysql", args, "loading")
66
+ end
67
+
68
+ private
69
+ attr_reader :db_config, :configuration_hash
70
+
71
+ def connection
72
+ ActiveRecord::Base.lease_connection
73
+ end
74
+
75
+ def establish_connection(config = db_config)
76
+ ActiveRecord::Base.establish_connection(config)
77
+ end
78
+
79
+ def configuration_hash_without_database
80
+ configuration_hash.merge(database: nil)
81
+ end
82
+
83
+ def creation_options
84
+ Hash.new.tap do |options|
85
+ options[:charset] = configuration_hash[:encoding] if configuration_hash.include?(:encoding)
86
+ options[:collation] = configuration_hash[:collation] if configuration_hash.include?(:collation)
87
+ end
88
+ end
89
+
90
+ def prepare_command_options
91
+ args = {
92
+ host: "--host",
93
+ port: "--port",
94
+ socket: "--socket",
95
+ username: "--user",
96
+ password: "--password",
97
+ encoding: "--default-character-set",
98
+ sslca: "--ssl-ca",
99
+ sslcert: "--ssl-cert",
100
+ sslcapath: "--ssl-capath",
101
+ sslcipher: "--ssl-cipher",
102
+ sslkey: "--ssl-key",
103
+ ssl_mode: "--ssl-mode"
104
+ }.filter_map { |opt, arg| "#{arg}=#{configuration_hash[opt]}" if configuration_hash[opt] }
105
+
106
+ args
107
+ end
108
+
109
+ def run_cmd(cmd, args, action)
110
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
111
+ end
112
+
113
+ def run_cmd_error(cmd, args, action)
114
+ msg = +"failed to execute: `#{cmd}`\n"
115
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
116
+ msg
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+
5
+ module ActiveRecord
6
+ module Tasks # :nodoc:
7
+ class PostgreSQLDatabaseTasks # :nodoc:
8
+ DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
9
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
10
+ SQL_COMMENT_BEGIN = "--"
11
+
12
+ def self.using_database_configurations?
13
+ true
14
+ end
15
+
16
+ def initialize(db_config)
17
+ @db_config = db_config
18
+ @configuration_hash = db_config.configuration_hash
19
+ end
20
+
21
+ def create(connection_already_established = false)
22
+ establish_connection(public_schema_config) unless connection_already_established
23
+ connection.create_database(db_config.database, configuration_hash.merge(encoding: encoding))
24
+ establish_connection
25
+ end
26
+
27
+ def drop
28
+ establish_connection(public_schema_config)
29
+ connection.drop_database(db_config.database)
30
+ end
31
+
32
+ def charset
33
+ connection.encoding
34
+ end
35
+
36
+ def collation
37
+ connection.collation
38
+ end
39
+
40
+ def purge
41
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
42
+ drop
43
+ create true
44
+ end
45
+
46
+ def structure_dump(filename, extra_flags)
47
+ search_path = \
48
+ case ActiveRecord.dump_schemas
49
+ when :schema_search_path
50
+ configuration_hash[:schema_search_path]
51
+ when :all
52
+ nil
53
+ when String
54
+ ActiveRecord.dump_schemas
55
+ end
56
+
57
+ args = ["--schema-only", "--no-privileges", "--no-owner"]
58
+ args.concat(["--file", filename])
59
+
60
+ args.concat(Array(extra_flags)) if extra_flags
61
+
62
+ unless search_path.blank?
63
+ args += search_path.split(",").map do |part|
64
+ "--schema=#{part.strip}"
65
+ end
66
+ end
67
+
68
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
69
+ if ignore_tables.any?
70
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
71
+ args += ignore_tables.flat_map { |table| ["-T", table] }
72
+ end
73
+
74
+ args << db_config.database
75
+ run_cmd("pg_dump", args, "dumping")
76
+ remove_sql_header_comments(filename)
77
+ File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
78
+ end
79
+
80
+ def structure_load(filename, extra_flags)
81
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
82
+ args.concat(Array(extra_flags)) if extra_flags
83
+ args << db_config.database
84
+ run_cmd("psql", args, "loading")
85
+ end
86
+
87
+ private
88
+ attr_reader :db_config, :configuration_hash
89
+
90
+ def connection
91
+ ActiveRecord::Base.lease_connection
92
+ end
93
+
94
+ def establish_connection(config = db_config)
95
+ ActiveRecord::Base.establish_connection(config)
96
+ end
97
+
98
+ def encoding
99
+ configuration_hash[:encoding] || DEFAULT_ENCODING
100
+ end
101
+
102
+ def public_schema_config
103
+ configuration_hash.merge(database: "postgres", schema_search_path: "public")
104
+ end
105
+
106
+ def psql_env
107
+ {}.tap do |env|
108
+ env["PGHOST"] = db_config.host if db_config.host
109
+ env["PGPORT"] = configuration_hash[:port].to_s if configuration_hash[:port]
110
+ env["PGPASSWORD"] = configuration_hash[:password].to_s if configuration_hash[:password]
111
+ env["PGUSER"] = configuration_hash[:username].to_s if configuration_hash[:username]
112
+ env["PGSSLMODE"] = configuration_hash[:sslmode].to_s if configuration_hash[:sslmode]
113
+ env["PGSSLCERT"] = configuration_hash[:sslcert].to_s if configuration_hash[:sslcert]
114
+ env["PGSSLKEY"] = configuration_hash[:sslkey].to_s if configuration_hash[:sslkey]
115
+ env["PGSSLROOTCERT"] = configuration_hash[:sslrootcert].to_s if configuration_hash[:sslrootcert]
116
+ end
117
+ end
118
+
119
+ def run_cmd(cmd, args, action)
120
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(psql_env, cmd, *args)
121
+ end
122
+
123
+ def run_cmd_error(cmd, args, action)
124
+ msg = +"failed to execute:\n"
125
+ msg << "#{cmd} #{args.join(' ')}\n\n"
126
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
127
+ msg
128
+ end
129
+
130
+ def remove_sql_header_comments(filename)
131
+ removing_comments = true
132
+ tempfile = Tempfile.open("uncommented_structure.sql")
133
+ begin
134
+ File.foreach(filename) do |line|
135
+ unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
136
+ tempfile << line
137
+ removing_comments = false
138
+ end
139
+ end
140
+ ensure
141
+ tempfile.close
142
+ end
143
+ FileUtils.cp(tempfile.path, filename)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tasks # :nodoc:
5
+ class SQLiteDatabaseTasks # :nodoc:
6
+ def self.using_database_configurations?
7
+ true
8
+ end
9
+
10
+ def initialize(db_config, root = ActiveRecord::Tasks::DatabaseTasks.root)
11
+ @db_config = db_config
12
+ @root = root
13
+ end
14
+
15
+ def create
16
+ raise DatabaseAlreadyExists if File.exist?(db_config.database)
17
+
18
+ establish_connection
19
+ connection
20
+ end
21
+
22
+ def drop
23
+ db_path = db_config.database
24
+ file = File.absolute_path?(db_path) ? db_path : File.join(root, db_path)
25
+ FileUtils.rm(file)
26
+ FileUtils.rm_f(["#{file}-shm", "#{file}-wal"])
27
+ rescue Errno::ENOENT => error
28
+ raise NoDatabaseError.new(error.message)
29
+ end
30
+
31
+ def purge
32
+ connection.disconnect!
33
+ drop
34
+ rescue NoDatabaseError
35
+ ensure
36
+ create
37
+ connection.reconnect!
38
+ end
39
+
40
+ def charset
41
+ connection.encoding
42
+ end
43
+
44
+ def structure_dump(filename, extra_flags)
45
+ args = []
46
+ args.concat(Array(extra_flags)) if extra_flags
47
+ args << db_config.database
48
+
49
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
50
+ if ignore_tables.any?
51
+ ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
52
+ condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
53
+ args << "SELECT sql || ';' FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
54
+ else
55
+ args << ".schema --nosys"
56
+ end
57
+ run_cmd("sqlite3", args, filename)
58
+ end
59
+
60
+ def structure_load(filename, extra_flags)
61
+ flags = extra_flags.join(" ") if extra_flags
62
+ `sqlite3 #{flags} #{db_config.database} < "#{filename}"`
63
+ end
64
+
65
+ private
66
+ attr_reader :db_config, :root
67
+
68
+ def connection
69
+ ActiveRecord::Base.lease_connection
70
+ end
71
+
72
+ def establish_connection(config = db_config)
73
+ ActiveRecord::Base.establish_connection(config)
74
+ connection.connect!
75
+ end
76
+
77
+ def run_cmd(cmd, args, out)
78
+ fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
79
+ end
80
+
81
+ def run_cmd_error(cmd, args)
82
+ msg = +"failed to execute:\n"
83
+ msg << "#{cmd} #{args.join(' ')}\n\n"
84
+ msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
85
+ msg
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/testing/parallelization"
4
+
5
+ module ActiveRecord
6
+ module TestDatabases # :nodoc:
7
+ ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
8
+ create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
9
+ end
10
+
11
+ def self.create_and_load_schema(i, env_name:)
12
+ old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
13
+
14
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
+ db_config._database = "#{db_config.database}-#{i}"
16
+
17
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord.schema_format, nil)
18
+ end
19
+ ensure
20
+ ActiveRecord::Base.establish_connection
21
+ ENV["VERBOSE"] = old
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
5
+ module ActiveRecord
6
+ module TestFixtures
7
+ extend ActiveSupport::Concern
8
+
9
+ def before_setup # :nodoc:
10
+ setup_fixtures
11
+ super
12
+ end
13
+
14
+ def after_teardown # :nodoc:
15
+ super
16
+ ensure
17
+ teardown_fixtures
18
+ end
19
+
20
+ included do
21
+ ##
22
+ # :singleton-method: fixture_paths
23
+ #
24
+ # Returns the ActiveRecord::FixtureSet collection
25
+
26
+ ##
27
+ # :singleton-method: fixture_paths=
28
+ #
29
+ # :call-seq:
30
+ # fixture_paths=(fixture_paths)
31
+ class_attribute :fixture_paths, instance_writer: false, default: []
32
+ class_attribute :fixture_table_names, default: []
33
+ class_attribute :fixture_class_names, default: {}
34
+ class_attribute :use_transactional_tests, default: true
35
+ class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
36
+ class_attribute :pre_loaded_fixtures, default: false
37
+ class_attribute :lock_threads, default: true
38
+ class_attribute :fixture_sets, default: {}
39
+
40
+ ActiveSupport.run_load_hooks(:active_record_fixtures, self)
41
+ end
42
+
43
+ module ClassMethods
44
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
45
+ #
46
+ # Examples:
47
+ #
48
+ # set_fixture_class some_fixture: SomeModel,
49
+ # 'namespaced/fixture' => Another::Model
50
+ #
51
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
52
+ def set_fixture_class(class_names = {})
53
+ self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
54
+ end
55
+
56
+ def fixtures(*fixture_set_names)
57
+ if fixture_set_names.first == :all
58
+ raise StandardError, "No fixture path found. Please set `#{self}.fixture_paths`." if fixture_paths.blank?
59
+ fixture_set_names = fixture_paths.flat_map do |path|
60
+ names = Dir[::File.join(path, "{**,*}/*.{yml}")].uniq
61
+ names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
62
+ names.map! { |f| f[path.to_s.size..-5].delete_prefix("/") }
63
+ end.uniq
64
+ else
65
+ fixture_set_names = fixture_set_names.flatten.map(&:to_s)
66
+ end
67
+
68
+ self.fixture_table_names = (fixture_table_names | fixture_set_names).sort
69
+ setup_fixture_accessors(fixture_set_names)
70
+ end
71
+
72
+ def setup_fixture_accessors(fixture_set_names = nil)
73
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
74
+ unless fixture_set_names.empty?
75
+ self.fixture_sets = fixture_sets.dup
76
+ fixture_set_names.each do |fs_name|
77
+ key = fs_name.to_s.include?("/") ? -fs_name.to_s.tr("/", "_") : fs_name
78
+ key = -key.to_s if key.is_a?(Symbol)
79
+ fs_name = -fs_name.to_s if fs_name.is_a?(Symbol)
80
+ fixture_sets[key] = fs_name
81
+ end
82
+ end
83
+ end
84
+
85
+ # Prevents automatically wrapping each specified test in a transaction,
86
+ # to allow application logic transactions to be tested in a top-level
87
+ # (non-nested) context.
88
+ def uses_transaction(*methods)
89
+ @uses_transaction = [] unless defined?(@uses_transaction)
90
+ @uses_transaction.concat methods.map(&:to_s)
91
+ end
92
+
93
+ def uses_transaction?(method)
94
+ @uses_transaction = [] unless defined?(@uses_transaction)
95
+ @uses_transaction.include?(method.to_s)
96
+ end
97
+ end
98
+
99
+ # Generic fixture accessor for fixture names that may conflict with other methods.
100
+ #
101
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
102
+ # assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
103
+ def fixture(fixture_set_name, *fixture_names)
104
+ active_record_fixture(fixture_set_name, *fixture_names)
105
+ end
106
+
107
+ private
108
+ def run_in_transaction?
109
+ use_transactional_tests &&
110
+ !self.class.uses_transaction?(name)
111
+ end
112
+
113
+ def setup_fixtures(config = ActiveRecord::Base)
114
+ if pre_loaded_fixtures && !use_transactional_tests
115
+ raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
116
+ end
117
+
118
+ @fixture_cache = {}
119
+ @fixture_cache_key = [self.class.fixture_table_names.dup, self.class.fixture_paths.dup, self.class.fixture_class_names.dup]
120
+ @fixture_connection_pools = []
121
+ @@already_loaded_fixtures ||= {}
122
+ @connection_subscriber = nil
123
+ @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
124
+
125
+ if run_in_transaction?
126
+ # Load fixtures once and begin transaction.
127
+ @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key]
128
+ unless @loaded_fixtures
129
+ @@already_loaded_fixtures.clear
130
+ @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
131
+ end
132
+
133
+ setup_transactional_fixtures
134
+ else
135
+ # Load fixtures for every test.
136
+ ActiveRecord::FixtureSet.reset_cache
137
+ invalidate_already_loaded_fixtures
138
+ @loaded_fixtures = load_fixtures(config)
139
+ end
140
+ setup_asynchronous_queries_session
141
+
142
+ # Instantiate fixtures for every test if requested.
143
+ instantiate_fixtures if use_instantiated_fixtures
144
+ end
145
+
146
+ def teardown_fixtures
147
+ teardown_asynchronous_queries_session
148
+
149
+ # Rollback changes if a transaction is active.
150
+ if run_in_transaction?
151
+ teardown_transactional_fixtures
152
+ else
153
+ ActiveRecord::FixtureSet.reset_cache
154
+ invalidate_already_loaded_fixtures
155
+ end
156
+
157
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
158
+ end
159
+
160
+ def setup_asynchronous_queries_session
161
+ @_async_queries_session = ActiveRecord::Base.asynchronous_queries_tracker.start_session
162
+ end
163
+
164
+ def teardown_asynchronous_queries_session
165
+ ActiveRecord::Base.asynchronous_queries_tracker.finalize_session(true) if @_async_queries_session
166
+ end
167
+
168
+ def invalidate_already_loaded_fixtures
169
+ @@already_loaded_fixtures.clear
170
+ end
171
+
172
+ def setup_transactional_fixtures
173
+ setup_shared_connection_pool
174
+
175
+ # Begin transactions for connections already established
176
+ @fixture_connection_pools = ActiveRecord::Base.connection_handler.connection_pool_list(:writing)
177
+ @fixture_connection_pools.each do |pool|
178
+ pool.pin_connection!(lock_threads)
179
+ pool.lease_connection
180
+ end
181
+
182
+ # When connections are established in the future, begin a transaction too
183
+ @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
184
+ connection_name = payload[:connection_name] if payload.key?(:connection_name)
185
+ shard = payload[:shard] if payload.key?(:shard)
186
+
187
+ if connection_name
188
+ pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
189
+ if pool
190
+ setup_shared_connection_pool
191
+
192
+ unless @fixture_connection_pools.include?(pool)
193
+ pool.pin_connection!(lock_threads)
194
+ pool.lease_connection
195
+ @fixture_connection_pools << pool
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def teardown_transactional_fixtures
203
+ ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
204
+
205
+ unless @fixture_connection_pools.map(&:unpin_connection!).all?
206
+ # Something caused the transaction to be committed or rolled back
207
+ # We can no longer trust the database is in a clean state.
208
+ @@already_loaded_fixtures.clear
209
+ end
210
+ @fixture_connection_pools.clear
211
+ teardown_shared_connection_pool
212
+ end
213
+
214
+ # Shares the writing connection pool with connections on
215
+ # other handlers.
216
+ #
217
+ # In an application with a primary and replica the test fixtures
218
+ # need to share a connection pool so that the reading connection
219
+ # can see data in the open transaction on the writing connection.
220
+ def setup_shared_connection_pool
221
+ handler = ActiveRecord::Base.connection_handler
222
+
223
+ handler.connection_pool_names.each do |name|
224
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
225
+ pool_manager.shard_names.each do |shard_name|
226
+ writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
227
+ @saved_pool_configs[name][shard_name] ||= {}
228
+ pool_manager.role_names.each do |role|
229
+ next unless pool_config = pool_manager.get_pool_config(role, shard_name)
230
+ next if pool_config == writing_pool_config
231
+
232
+ @saved_pool_configs[name][shard_name][role] = pool_config
233
+ pool_manager.set_pool_config(role, shard_name, writing_pool_config)
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ def teardown_shared_connection_pool
240
+ handler = ActiveRecord::Base.connection_handler
241
+
242
+ @saved_pool_configs.each_pair do |name, shards|
243
+ pool_manager = handler.send(:connection_name_to_pool_manager)[name]
244
+ shards.each_pair do |shard_name, roles|
245
+ roles.each_pair do |role, pool_config|
246
+ next unless pool_manager.get_pool_config(role, shard_name)
247
+
248
+ pool_manager.set_pool_config(role, shard_name, pool_config)
249
+ end
250
+ end
251
+ end
252
+
253
+ @saved_pool_configs.clear
254
+ end
255
+
256
+ def load_fixtures(config)
257
+ ActiveRecord::FixtureSet.create_fixtures(fixture_paths, fixture_table_names, fixture_class_names, config).index_by(&:name)
258
+ end
259
+
260
+ def instantiate_fixtures
261
+ if pre_loaded_fixtures
262
+ raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
263
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
264
+ else
265
+ raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
266
+ @loaded_fixtures.each_value do |fixture_set|
267
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
268
+ end
269
+ end
270
+ end
271
+
272
+ def load_instances?
273
+ use_instantiated_fixtures != :no_instances
274
+ end
275
+
276
+ def method_missing(method, ...)
277
+ if fixture_sets.key?(method.name)
278
+ active_record_fixture(method, ...)
279
+ else
280
+ super
281
+ end
282
+ end
283
+
284
+ def respond_to_missing?(method, include_private = false)
285
+ if include_private && fixture_sets.key?(method.name)
286
+ true
287
+ else
288
+ super
289
+ end
290
+ end
291
+
292
+ def active_record_fixture(fixture_set_name, *fixture_names)
293
+ if fs_name = fixture_sets[fixture_set_name.name]
294
+ access_fixture(fs_name, *fixture_names)
295
+ else
296
+ raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
297
+ end
298
+ end
299
+
300
+ def access_fixture(fs_name, *fixture_names)
301
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
302
+ return_single_record = fixture_names.size == 1
303
+
304
+ fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
305
+ @fixture_cache[fs_name] ||= {}
306
+
307
+ instances = fixture_names.map do |f_name|
308
+ f_name = f_name.to_s if f_name.is_a?(Symbol)
309
+ @fixture_cache[fs_name].delete(f_name) if force_reload
310
+
311
+ if @loaded_fixtures[fs_name][f_name]
312
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
313
+ else
314
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
315
+ end
316
+ end
317
+
318
+ return_single_record ? instances.first : instances
319
+ end
320
+ end
321
+ end