activerecord 6.1.7 → 7.2.0

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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -20,6 +20,7 @@ module Arel # :nodoc: all
20
20
 
21
21
  private
22
22
  def visit_Arel_Nodes_DeleteStatement(o, collector)
23
+ collector.retryable = false
23
24
  o = prepare_delete_statement(o)
24
25
 
25
26
  if has_join_sources?(o)
@@ -37,6 +38,7 @@ module Arel # :nodoc: all
37
38
  end
38
39
 
39
40
  def visit_Arel_Nodes_UpdateStatement(o, collector)
41
+ collector.retryable = false
40
42
  o = prepare_update_statement(o)
41
43
 
42
44
  collector << "UPDATE "
@@ -49,6 +51,7 @@ module Arel # :nodoc: all
49
51
  end
50
52
 
51
53
  def visit_Arel_Nodes_InsertStatement(o, collector)
54
+ collector.retryable = false
52
55
  collector << "INSERT INTO "
53
56
  collector = visit o.relation, collector
54
57
 
@@ -103,7 +106,7 @@ module Arel # :nodoc: all
103
106
  row.each_with_index do |value, k|
104
107
  collector << ", " unless k == 0
105
108
  case value
106
- when Nodes::SqlLiteral, Nodes::BindParam
109
+ when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
107
110
  collector = visit(value, collector)
108
111
  else
109
112
  collector << quote(value).to_s
@@ -135,6 +138,8 @@ module Arel # :nodoc: all
135
138
  visit_Arel_Nodes_SelectOptions(o, collector)
136
139
  end
137
140
 
141
+ # The Oracle enhanced adapter uses this private method,
142
+ # see https://github.com/rsim/oracle-enhanced/issues/2186
138
143
  def visit_Arel_Nodes_SelectOptions(o, collector)
139
144
  collector = maybe_visit o.limit, collector
140
145
  collector = maybe_visit o.offset, collector
@@ -243,6 +248,13 @@ module Arel # :nodoc: all
243
248
  collector << ")"
244
249
  end
245
250
 
251
+ def visit_Arel_Nodes_Filter(o, collector)
252
+ visit o.left, collector
253
+ collector << " FILTER (WHERE "
254
+ visit o.right, collector
255
+ collector << ")"
256
+ end
257
+
246
258
  def visit_Arel_Nodes_Rows(o, collector)
247
259
  if o.expr
248
260
  collector << "ROWS "
@@ -324,7 +336,7 @@ module Arel # :nodoc: all
324
336
  def visit_Arel_Nodes_HomogeneousIn(o, collector)
325
337
  collector.preparable = false
326
338
 
327
- collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name)
339
+ visit o.left, collector
328
340
 
329
341
  if o.type == :in
330
342
  collector << " IN ("
@@ -341,7 +353,6 @@ module Arel # :nodoc: all
341
353
  end
342
354
 
343
355
  collector << ")"
344
- collector
345
356
  end
346
357
 
347
358
  def visit_Arel_SelectManager(o, collector)
@@ -357,11 +368,23 @@ module Arel # :nodoc: all
357
368
  visit(o.expr, collector) << " DESC"
358
369
  end
359
370
 
371
+ # NullsFirst is available on all but MySQL, where it is redefined.
372
+ def visit_Arel_Nodes_NullsFirst(o, collector)
373
+ visit o.expr, collector
374
+ collector << " NULLS FIRST"
375
+ end
376
+
377
+ def visit_Arel_Nodes_NullsLast(o, collector)
378
+ visit o.expr, collector
379
+ collector << " NULLS LAST"
380
+ end
381
+
360
382
  def visit_Arel_Nodes_Group(o, collector)
361
383
  visit o.expr, collector
362
384
  end
363
385
 
364
386
  def visit_Arel_Nodes_NamedFunction(o, collector)
387
+ collector.retryable = false
365
388
  collector << o.name
366
389
  collector << "("
367
390
  collector << "DISTINCT " if o.distinct
@@ -412,24 +435,48 @@ module Arel # :nodoc: all
412
435
  end
413
436
 
414
437
  def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
438
+ case unboundable?(o.right)
439
+ when 1
440
+ return collector << "1=0"
441
+ when -1
442
+ return collector << "1=1"
443
+ end
415
444
  collector = visit o.left, collector
416
445
  collector << " >= "
417
446
  visit o.right, collector
418
447
  end
419
448
 
420
449
  def visit_Arel_Nodes_GreaterThan(o, collector)
450
+ case unboundable?(o.right)
451
+ when 1
452
+ return collector << "1=0"
453
+ when -1
454
+ return collector << "1=1"
455
+ end
421
456
  collector = visit o.left, collector
422
457
  collector << " > "
423
458
  visit o.right, collector
424
459
  end
425
460
 
426
461
  def visit_Arel_Nodes_LessThanOrEqual(o, collector)
462
+ case unboundable?(o.right)
463
+ when 1
464
+ return collector << "1=1"
465
+ when -1
466
+ return collector << "1=0"
467
+ end
427
468
  collector = visit o.left, collector
428
469
  collector << " <= "
429
470
  visit o.right, collector
430
471
  end
431
472
 
432
473
  def visit_Arel_Nodes_LessThan(o, collector)
474
+ case unboundable?(o.right)
475
+ when 1
476
+ return collector << "1=1"
477
+ when -1
478
+ return collector << "1=0"
479
+ end
433
480
  collector = visit o.left, collector
434
481
  collector << " < "
435
482
  visit o.right, collector
@@ -525,18 +572,25 @@ module Arel # :nodoc: all
525
572
  end
526
573
 
527
574
  def visit_Arel_Table(o, collector)
528
- if o.table_alias
529
- collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
575
+ if Arel::Nodes::Node === o.name
576
+ visit o.name, collector
530
577
  else
531
578
  collector << quote_table_name(o.name)
532
579
  end
580
+
581
+ if o.table_alias
582
+ collector << " " << quote_table_name(o.table_alias)
583
+ end
584
+
585
+ collector
533
586
  end
534
587
 
535
588
  def visit_Arel_Nodes_In(o, collector)
536
- collector.preparable = false
537
589
  attr, values = o.left, o.right
538
590
 
539
591
  if Array === values
592
+ collector.preparable = false
593
+
540
594
  unless values.empty?
541
595
  values.delete_if { |value| unboundable?(value) }
542
596
  end
@@ -549,10 +603,11 @@ module Arel # :nodoc: all
549
603
  end
550
604
 
551
605
  def visit_Arel_Nodes_NotIn(o, collector)
552
- collector.preparable = false
553
606
  attr, values = o.left, o.right
554
607
 
555
608
  if Array === values
609
+ collector.preparable = false
610
+
556
611
  unless values.empty?
557
612
  values.delete_if { |value| unboundable?(value) }
558
613
  end
@@ -569,23 +624,12 @@ module Arel # :nodoc: all
569
624
  end
570
625
 
571
626
  def visit_Arel_Nodes_Or(o, collector)
572
- stack = [o.right, o.left]
573
-
574
- while o = stack.pop
575
- if o.is_a?(Arel::Nodes::Or)
576
- stack.push o.right, o.left
577
- else
578
- visit o, collector
579
- collector << " OR " unless stack.empty?
580
- end
581
- end
582
-
583
- collector
627
+ inject_join o.children, collector, " OR "
584
628
  end
585
629
 
586
630
  def visit_Arel_Nodes_Assignment(o, collector)
587
631
  case o.right
588
- when Arel::Nodes::Node, Arel::Attributes::Attribute
632
+ when Arel::Nodes::Node, Arel::Attributes::Attribute, ActiveModel::Attribute
589
633
  collector = visit o.left, collector
590
634
  collector << " = "
591
635
  visit o.right, collector
@@ -685,6 +729,20 @@ module Arel # :nodoc: all
685
729
  collector << quote_column_name(o.name)
686
730
  end
687
731
 
732
+ def visit_Arel_Nodes_Cte(o, collector)
733
+ collector << quote_table_name(o.name)
734
+ collector << " AS "
735
+
736
+ case o.materialized
737
+ when true
738
+ collector << "MATERIALIZED "
739
+ when false
740
+ collector << "NOT MATERIALIZED "
741
+ end
742
+
743
+ visit o.relation, collector
744
+ end
745
+
688
746
  def visit_Arel_Attributes_Attribute(o, collector)
689
747
  join_name = o.relation.table_alias || o.relation.name
690
748
  collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
@@ -695,15 +753,74 @@ module Arel # :nodoc: all
695
753
 
696
754
  def bind_block; BIND_BLOCK; end
697
755
 
756
+ def visit_ActiveModel_Attribute(o, collector)
757
+ collector.add_bind(o, &bind_block)
758
+ end
759
+
698
760
  def visit_Arel_Nodes_BindParam(o, collector)
699
761
  collector.add_bind(o.value, &bind_block)
700
762
  end
701
763
 
702
764
  def visit_Arel_Nodes_SqlLiteral(o, collector)
703
765
  collector.preparable = false
766
+ collector.retryable = o.retryable
704
767
  collector << o.to_s
705
768
  end
706
769
 
770
+ def visit_Arel_Nodes_BoundSqlLiteral(o, collector)
771
+ collector.retryable = false
772
+ bind_index = 0
773
+
774
+ new_bind = lambda do |value|
775
+ if Arel.arel_node?(value)
776
+ visit value, collector
777
+ elsif value.is_a?(Array)
778
+ if value.empty?
779
+ collector << @connection.quote(nil)
780
+ else
781
+ if value.none? { |v| Arel.arel_node?(v) }
782
+ collector.add_binds(value.map { |v| @connection.cast_bound_value(v) }, &bind_block)
783
+ else
784
+ value.each_with_index do |v, i|
785
+ collector << ", " unless i == 0
786
+ if Arel.arel_node?(v)
787
+ visit v, collector
788
+ else
789
+ collector.add_bind(@connection.cast_bound_value(v), &bind_block)
790
+ end
791
+ end
792
+ end
793
+ end
794
+ else
795
+ collector.add_bind(@connection.cast_bound_value(value), &bind_block)
796
+ end
797
+ end
798
+
799
+ if o.positional_binds
800
+ o.sql_with_placeholders.scan(/\?|([^?]+)/) do
801
+ if $1
802
+ collector << $1
803
+ else
804
+ value = o.positional_binds[bind_index]
805
+ bind_index += 1
806
+
807
+ new_bind.call(value)
808
+ end
809
+ end
810
+ else
811
+ o.sql_with_placeholders.scan(/:(?<!::)([a-zA-Z]\w*)|([^:]+|.)/) do
812
+ if $2
813
+ collector << $2
814
+ else
815
+ value = o.named_binds[$1.to_sym]
816
+ new_bind.call(value)
817
+ end
818
+ end
819
+ end
820
+
821
+ collector
822
+ end
823
+
707
824
  def visit_Integer(o, collector)
708
825
  collector << o.to_s
709
826
  end
@@ -743,6 +860,10 @@ module Arel # :nodoc: all
743
860
  end
744
861
  alias :visit_Set :visit_Array
745
862
 
863
+ def visit_Arel_Nodes_Fragments(o, collector)
864
+ inject_join o.values, collector, " "
865
+ end
866
+
746
867
  def quote(value)
747
868
  return value if Arel::Nodes::SqlLiteral === value
748
869
  @connection.quote value
@@ -793,6 +914,10 @@ module Arel # :nodoc: all
793
914
  o.limit || o.offset || !o.orders.empty?
794
915
  end
795
916
 
917
+ def has_group_by_and_having?(o)
918
+ !o.groups.empty? && !o.havings.empty?
919
+ end
920
+
796
921
  # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
797
922
  # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
798
923
  # an UPDATE statement, so in the MySQL visitor we redefine this to do that.
@@ -802,8 +927,11 @@ module Arel # :nodoc: all
802
927
  stmt.limit = nil
803
928
  stmt.offset = nil
804
929
  stmt.orders = []
805
- stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
930
+ columns = Arel::Nodes::Grouping.new(o.key)
931
+ stmt.wheres = [Nodes::In.new(columns, [build_subselect(o.key, o)])]
806
932
  stmt.relation = o.relation.left if has_join_sources?(o)
933
+ stmt.groups = o.groups unless o.groups.empty?
934
+ stmt.havings = o.havings unless o.havings.empty?
807
935
  stmt
808
936
  else
809
937
  o
@@ -818,6 +946,8 @@ module Arel # :nodoc: all
818
946
  core.froms = o.relation
819
947
  core.wheres = o.wheres
820
948
  core.projections = [key]
949
+ core.groups = o.groups unless o.groups.empty?
950
+ core.havings = o.havings unless o.havings.empty?
821
951
  stmt.limit = o.limit
822
952
  stmt.offset = o.offset
823
953
  stmt.orders = o.orders
@@ -835,18 +965,34 @@ module Arel # :nodoc: all
835
965
  collector = if o.left.class == o.class
836
966
  infix_value_with_paren(o.left, collector, value, true)
837
967
  else
838
- visit o.left, collector
968
+ grouping_parentheses o.left, collector, false
839
969
  end
840
970
  collector << value
841
971
  collector = if o.right.class == o.class
842
972
  infix_value_with_paren(o.right, collector, value, true)
843
973
  else
844
- visit o.right, collector
974
+ grouping_parentheses o.right, collector, false
845
975
  end
846
976
  collector << " )" unless suppress_parens
847
977
  collector
848
978
  end
849
979
 
980
+ # Used by some visitors to enclose select queries in parentheses
981
+ def grouping_parentheses(o, collector, always_wrap_selects = true)
982
+ if o.is_a?(Nodes::SelectStatement) && (always_wrap_selects || require_parentheses?(o))
983
+ collector << "("
984
+ visit o, collector
985
+ collector << ")"
986
+ collector
987
+ else
988
+ visit o, collector
989
+ end
990
+ end
991
+
992
+ def require_parentheses?(o)
993
+ !o.orders.empty? || o.limit || o.offset
994
+ end
995
+
850
996
  def aggregate(name, o, collector)
851
997
  collector << "#{name}("
852
998
  if o.distinct
@@ -877,19 +1023,7 @@ module Arel # :nodoc: all
877
1023
  def collect_ctes(children, collector)
878
1024
  children.each_with_index do |child, i|
879
1025
  collector << ", " unless i == 0
880
-
881
- case child
882
- when Arel::Nodes::As
883
- name = child.left.name
884
- relation = child.right
885
- when Arel::Nodes::TableAlias
886
- name = child.name
887
- relation = child.relation
888
- end
889
-
890
- collector << quote_table_name(name)
891
- collector << " AS "
892
- visit relation, collector
1026
+ visit child.to_cte, collector
893
1027
  end
894
1028
 
895
1029
  collector
@@ -16,8 +16,8 @@ module Arel # :nodoc: all
16
16
 
17
17
  def self.dispatch_cache
18
18
  @dispatch_cache ||= Hash.new do |hash, klass|
19
- hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
20
- end
19
+ hash[klass] = :"visit_#{(klass.name || "").gsub("::", "_")}"
20
+ end.compare_by_identity
21
21
  end
22
22
 
23
23
  def get_dispatch_cache
data/lib/arel.rb CHANGED
@@ -7,6 +7,7 @@ require "arel/factory_methods"
7
7
 
8
8
  require "arel/expressions"
9
9
  require "arel/predications"
10
+ require "arel/filter_predications"
10
11
  require "arel/window_predications"
11
12
  require "arel/math"
12
13
  require "arel/alias_predication"
@@ -29,17 +30,35 @@ module Arel
29
30
 
30
31
  # Wrap a known-safe SQL string for passing to query methods, e.g.
31
32
  #
32
- # Post.order(Arel.sql("length(title)")).last
33
+ # Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
33
34
  #
34
35
  # Great caution should be taken to avoid SQL injection vulnerabilities.
35
36
  # This method should not be used with unsafe values such as request
36
37
  # parameters or model attributes.
37
- def self.sql(raw_sql)
38
- Arel::Nodes::SqlLiteral.new raw_sql
38
+ #
39
+ # Take a look at the {security guide}[https://guides.rubyonrails.org/security.html#sql-injection]
40
+ # for more information.
41
+ #
42
+ # To construct a more complex query fragment, including the possible
43
+ # use of user-provided values, the +sql_string+ may contain <tt>?</tt> and
44
+ # +:key+ placeholders, corresponding to the additional arguments. Note
45
+ # that this behavior only applies when bind value parameters are
46
+ # supplied in the call; without them, the placeholder tokens have no
47
+ # special meaning, and will be passed through to the query as-is.
48
+ #
49
+ # The +:retryable+ option can be used to mark the SQL as safe to retry.
50
+ # Use this option only if the SQL is idempotent, as it could be executed
51
+ # more than once.
52
+ def self.sql(sql_string, *positional_binds, retryable: false, **named_binds)
53
+ if positional_binds.empty? && named_binds.empty?
54
+ Arel::Nodes::SqlLiteral.new(sql_string, retryable: retryable)
55
+ else
56
+ Arel::Nodes::BoundSqlLiteral.new sql_string, positional_binds, named_binds
57
+ end
39
58
  end
40
59
 
41
60
  def self.star # :nodoc:
42
- sql "*"
61
+ sql("*", retryable: true)
43
62
  end
44
63
 
45
64
  def self.arel_node?(value) # :nodoc:
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates an `ApplicationRecord` base class for other models to inherit from.
3
+
4
+ Example:
5
+ `bin/rails generate application_record`
6
+
7
+ This generates the base class. A test is not generated because no
8
+ behaviour is included in `ApplicationRecord` by default.
@@ -1,5 +1,5 @@
1
1
  <% module_namespacing do -%>
2
2
  class ApplicationRecord < ActiveRecord::Base
3
- self.abstract_class = true
3
+ primary_abstract_class
4
4
  end
5
5
  <% end -%>
@@ -12,7 +12,10 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
12
12
  t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
13
13
  <% end -%>
14
14
  <% end -%>
15
- <% if options[:timestamps] %>
15
+ <% unless attributes.empty? -%>
16
+
17
+ <% end -%>
18
+ <% if options[:timestamps] -%>
16
19
  t.timestamps
17
20
  <% end -%>
18
21
  end
@@ -41,11 +41,13 @@ module ActiveRecord
41
41
 
42
42
  def configured_migrate_path
43
43
  return unless database = options[:database]
44
+
44
45
  config = ActiveRecord::Base.configurations.configs_for(
45
46
  env_name: Rails.env,
46
47
  name: database
47
48
  )
48
- config&.migrations_paths
49
+
50
+ Array(config&.migrations_paths).first
49
51
  end
50
52
  end
51
53
  end
@@ -0,0 +1,113 @@
1
+ Description:
2
+ Generates a new model. Pass the model name, either CamelCased or
3
+ under_scored, and an optional list of attribute pairs as arguments.
4
+
5
+ Attribute pairs are field:type arguments specifying the
6
+ model's attributes. Timestamps are added by default, so you don't have to
7
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
8
+
9
+ As a special case, specifying 'password:digest' will generate a
10
+ password_digest field of string type, and configure your generated model and
11
+ tests for use with Active Model has_secure_password (assuming the default ORM
12
+ and test framework are being used).
13
+
14
+ You don't have to think up every attribute up front, but it helps to
15
+ sketch out a few so you can start working with the model immediately.
16
+
17
+ This generator invokes your configured ORM and test framework, which
18
+ defaults to Active Record and TestUnit.
19
+
20
+ Finally, if --parent option is given, it's used as superclass of the
21
+ created model. This allows you create Single Table Inheritance models.
22
+
23
+ If you pass a namespaced model name (e.g. admin/account or Admin::Account)
24
+ then the generator will create a module with a table_name_prefix method
25
+ to prefix the model's table name with the module name (e.g. admin_accounts)
26
+
27
+ Available field types:
28
+
29
+ Just after the field name you can specify a type like text or boolean.
30
+ It will generate the column with the associated SQL type. For instance:
31
+
32
+ `bin/rails generate model post title:string body:text`
33
+
34
+ will generate a title column with a varchar type and a body column with a text
35
+ type. If no type is specified the string type will be used by default.
36
+ You can use the following types:
37
+
38
+ integer
39
+ primary_key
40
+ decimal
41
+ float
42
+ boolean
43
+ binary
44
+ string
45
+ text
46
+ date
47
+ time
48
+ datetime
49
+
50
+ You can also consider `references` as a kind of type. For instance, if you run:
51
+
52
+ `bin/rails generate model photo title:string album:references`
53
+
54
+ It will generate an `album_id` column. You should generate these kinds of fields when
55
+ you will use a `belongs_to` association, for instance. `references` also supports
56
+ polymorphism, you can enable polymorphism like this:
57
+
58
+ `bin/rails generate model product supplier:references{polymorphic}`
59
+
60
+ For integer, string, text and binary fields, an integer in curly braces will
61
+ be set as the limit:
62
+
63
+ `bin/rails generate model user pseudo:string{30}`
64
+
65
+ For decimal, two integers separated by a comma in curly braces will be used
66
+ for precision and scale:
67
+
68
+ `bin/rails generate model product 'price:decimal{10,2}'`
69
+
70
+ You can add a `:uniq` or `:index` suffix for unique or standard indexes
71
+ respectively:
72
+
73
+ `bin/rails generate model user pseudo:string:uniq`
74
+ `bin/rails generate model user pseudo:string:index`
75
+
76
+ You can combine any single curly brace option with the index options:
77
+
78
+ `bin/rails generate model user username:string{30}:uniq`
79
+ `bin/rails generate model product supplier:references{polymorphic}:index`
80
+
81
+ If you require a `password_digest` string column for use with
82
+ has_secure_password, you can specify `password:digest`:
83
+
84
+ `bin/rails generate model user password:digest`
85
+
86
+ If you require a `token` string column for use with
87
+ has_secure_token, you can specify `auth_token:token`:
88
+
89
+ `bin/rails generate model user auth_token:token`
90
+
91
+ Examples:
92
+ `bin/rails generate model account`
93
+
94
+ For Active Record and TestUnit it creates:
95
+
96
+ Model: app/models/account.rb
97
+ Test: test/models/account_test.rb
98
+ Fixtures: test/fixtures/accounts.yml
99
+ Migration: db/migrate/XXX_create_accounts.rb
100
+
101
+ `bin/rails generate model post title:string body:text published:boolean`
102
+
103
+ Creates a Post model with a string title, text body, and published flag.
104
+
105
+ `bin/rails generate model admin/account`
106
+
107
+ For Active Record and TestUnit it creates:
108
+
109
+ Module: app/models/admin.rb
110
+ Model: app/models/admin/account.rb
111
+ Test: test/models/admin/account_test.rb
112
+ Fixtures: test/fixtures/admin/accounts.yml
113
+ Migration: db/migrate/XXX_create_admin_accounts.rb
@@ -11,20 +11,25 @@ module ActiveRecord
11
11
 
12
12
  class_option :migration, type: :boolean
13
13
  class_option :timestamps, type: :boolean
14
- class_option :parent, type: :string, desc: "The parent class for the generated model"
14
+ class_option :parent, type: :string, default: "ApplicationRecord", desc: "The parent class for the generated model"
15
15
  class_option :indexes, type: :boolean, default: true, desc: "Add indexes for references and belongs_to columns"
16
16
  class_option :primary_key_type, type: :string, desc: "The type for primary key"
17
17
  class_option :database, type: :string, aliases: %i(--db), desc: "The database for your model's migration. By default, the current environment's primary database is used."
18
18
 
19
+ Rails::Generators.templates_path.each do |path|
20
+ source_paths << File.join(path, base_name, "migration")
21
+ end
22
+ source_paths << File.expand_path(File.join(base_name, "migration", "templates"), base_root)
23
+
19
24
  # creates the migration file for the model.
20
25
  def create_migration_file
21
26
  return if skip_migration_creation?
22
27
  attributes.each { |a| a.attr_options.delete(:index) if a.reference? && !a.has_index? } if options[:indexes] == false
23
- migration_template "../../migration/templates/create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
28
+ migration_template "create_table_migration.rb", File.join(db_migrate_path, "create_#{table_name}.rb")
24
29
  end
25
30
 
26
31
  def create_model_file
27
- generate_abstract_class if database && !parent
32
+ generate_abstract_class if database && !custom_parent?
28
33
  template "model.rb", File.join("app/models", class_path, "#{file_name}.rb")
29
34
  end
30
35
 
@@ -40,7 +45,7 @@ module ActiveRecord
40
45
  # - options parent is present and database option is not present
41
46
  # - migrations option is nil or false
42
47
  def skip_migration_creation?
43
- parent && !database || !migration
48
+ custom_parent? && !database || !migration
44
49
  end
45
50
 
46
51
  def attributes_with_index
@@ -49,12 +54,12 @@ module ActiveRecord
49
54
 
50
55
  # Used by the migration template to determine the parent name of the model
51
56
  def parent_class_name
52
- if parent
57
+ if custom_parent?
53
58
  parent
54
59
  elsif database
55
60
  abstract_class_name
56
61
  else
57
- "ApplicationRecord"
62
+ parent
58
63
  end
59
64
  end
60
65
 
@@ -77,6 +82,10 @@ module ActiveRecord
77
82
  options[:parent]
78
83
  end
79
84
 
85
+ def custom_parent?
86
+ parent != self.class.class_options[:parent].default
87
+ end
88
+
80
89
  def migration
81
90
  options[:migration]
82
91
  end