activerecord 4.2.9 → 6.1.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (374) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +964 -1382
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +15 -14
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +266 -251
  8. data/lib/active_record/association_relation.rb +40 -15
  9. data/lib/active_record/associations/alias_tracker.rb +40 -43
  10. data/lib/active_record/associations/association.rb +162 -69
  11. data/lib/active_record/associations/association_scope.rb +105 -130
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -65
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  14. data/lib/active_record/associations/builder/association.rb +57 -43
  15. data/lib/active_record/associations/builder/belongs_to.rb +74 -57
  16. data/lib/active_record/associations/builder/collection_association.rb +15 -37
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
  18. data/lib/active_record/associations/builder/has_many.rb +13 -5
  19. data/lib/active_record/associations/builder/has_one.rb +44 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  21. data/lib/active_record/associations/collection_association.rb +148 -287
  22. data/lib/active_record/associations/collection_proxy.rb +252 -150
  23. data/lib/active_record/associations/foreign_association.rb +23 -1
  24. data/lib/active_record/associations/has_many_association.rb +56 -98
  25. data/lib/active_record/associations/has_many_through_association.rb +68 -89
  26. data/lib/active_record/associations/has_one_association.rb +73 -47
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  31. data/lib/active_record/associations/join_dependency.rb +174 -169
  32. data/lib/active_record/associations/preloader/association.rb +108 -115
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  34. data/lib/active_record/associations/preloader.rb +97 -94
  35. data/lib/active_record/associations/singular_association.rb +18 -39
  36. data/lib/active_record/associations/through_association.rb +39 -19
  37. data/lib/active_record/associations.rb +1845 -1598
  38. data/lib/active_record/attribute_assignment.rb +59 -185
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
  40. data/lib/active_record/attribute_methods/dirty.rb +168 -148
  41. data/lib/active_record/attribute_methods/primary_key.rb +93 -83
  42. data/lib/active_record/attribute_methods/query.rb +8 -10
  43. data/lib/active_record/attribute_methods/read.rb +19 -79
  44. data/lib/active_record/attribute_methods/serialization.rb +49 -24
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
  46. data/lib/active_record/attribute_methods/write.rb +24 -55
  47. data/lib/active_record/attribute_methods.rb +149 -154
  48. data/lib/active_record/attributes.rb +234 -78
  49. data/lib/active_record/autosave_association.rb +133 -60
  50. data/lib/active_record/base.rb +46 -46
  51. data/lib/active_record/callbacks.rb +234 -79
  52. data/lib/active_record/coders/json.rb +3 -1
  53. data/lib/active_record/coders/yaml_column.rb +34 -13
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
  59. data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
  60. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
  67. data/lib/active_record/connection_adapters/column.rb +67 -40
  68. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  69. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
  72. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  78. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
  80. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  81. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  101. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  109. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
  110. data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
  111. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  112. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  114. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
  116. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  117. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  118. data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
  119. data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
  120. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  121. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  122. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  123. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
  129. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  130. data/lib/active_record/connection_adapters.rb +52 -0
  131. data/lib/active_record/connection_handling.rb +314 -41
  132. data/lib/active_record/core.rb +458 -241
  133. data/lib/active_record/counter_cache.rb +70 -49
  134. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  135. data/lib/active_record/database_configurations/database_config.rb +80 -0
  136. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  137. data/lib/active_record/database_configurations/url_config.rb +53 -0
  138. data/lib/active_record/database_configurations.rb +272 -0
  139. data/lib/active_record/delegated_type.rb +209 -0
  140. data/lib/active_record/destroy_association_async_job.rb +36 -0
  141. data/lib/active_record/dynamic_matchers.rb +87 -106
  142. data/lib/active_record/enum.rb +211 -92
  143. data/lib/active_record/errors.rb +224 -54
  144. data/lib/active_record/explain.rb +27 -11
  145. data/lib/active_record/explain_registry.rb +4 -2
  146. data/lib/active_record/explain_subscriber.rb +10 -5
  147. data/lib/active_record/fixture_set/file.rb +33 -14
  148. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  149. data/lib/active_record/fixture_set/render_context.rb +17 -0
  150. data/lib/active_record/fixture_set/table_row.rb +152 -0
  151. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  152. data/lib/active_record/fixtures.rb +275 -500
  153. data/lib/active_record/gem_version.rb +6 -4
  154. data/lib/active_record/inheritance.rb +175 -110
  155. data/lib/active_record/insert_all.rb +212 -0
  156. data/lib/active_record/integration.rb +121 -29
  157. data/lib/active_record/internal_metadata.rb +62 -0
  158. data/lib/active_record/legacy_yaml_adapter.rb +27 -5
  159. data/lib/active_record/locale/en.yml +3 -2
  160. data/lib/active_record/locking/optimistic.rb +98 -92
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +93 -31
  163. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector.rb +77 -0
  166. data/lib/active_record/migration/command_recorder.rb +185 -90
  167. data/lib/active_record/migration/compatibility.rb +295 -0
  168. data/lib/active_record/migration/join_table.rb +8 -7
  169. data/lib/active_record/migration.rb +673 -325
  170. data/lib/active_record/model_schema.rb +418 -113
  171. data/lib/active_record/nested_attributes.rb +263 -224
  172. data/lib/active_record/no_touching.rb +15 -2
  173. data/lib/active_record/null_relation.rb +24 -38
  174. data/lib/active_record/persistence.rb +572 -136
  175. data/lib/active_record/query_cache.rb +29 -23
  176. data/lib/active_record/querying.rb +50 -31
  177. data/lib/active_record/railtie.rb +170 -51
  178. data/lib/active_record/railties/console_sandbox.rb +3 -3
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +523 -199
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +454 -291
  183. data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
  184. data/lib/active_record/relation/batches.rb +217 -59
  185. data/lib/active_record/relation/calculations.rb +324 -249
  186. data/lib/active_record/relation/delegation.rb +76 -84
  187. data/lib/active_record/relation/finder_methods.rb +316 -242
  188. data/lib/active_record/relation/from_clause.rb +30 -0
  189. data/lib/active_record/relation/merger.rb +95 -103
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  192. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  193. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
  194. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  195. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  196. data/lib/active_record/relation/predicate_builder.rb +136 -122
  197. data/lib/active_record/relation/query_attribute.rb +50 -0
  198. data/lib/active_record/relation/query_methods.rb +757 -413
  199. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  200. data/lib/active_record/relation/spawn_methods.rb +18 -20
  201. data/lib/active_record/relation/where_clause.rb +239 -0
  202. data/lib/active_record/relation.rb +554 -343
  203. data/lib/active_record/result.rb +91 -47
  204. data/lib/active_record/runtime_registry.rb +6 -4
  205. data/lib/active_record/sanitization.rb +134 -122
  206. data/lib/active_record/schema.rb +21 -24
  207. data/lib/active_record/schema_dumper.rb +141 -92
  208. data/lib/active_record/schema_migration.rb +24 -23
  209. data/lib/active_record/scoping/default.rb +96 -83
  210. data/lib/active_record/scoping/named.rb +78 -36
  211. data/lib/active_record/scoping.rb +45 -27
  212. data/lib/active_record/secure_token.rb +48 -0
  213. data/lib/active_record/serialization.rb +8 -6
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +89 -36
  216. data/lib/active_record/store.rb +128 -43
  217. data/lib/active_record/suppressor.rb +61 -0
  218. data/lib/active_record/table_metadata.rb +81 -0
  219. data/lib/active_record/tasks/database_tasks.rb +364 -130
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +287 -0
  225. data/lib/active_record/timestamp.rb +86 -43
  226. data/lib/active_record/touch_later.rb +65 -0
  227. data/lib/active_record/transactions.rb +182 -163
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  230. data/lib/active_record/type/date.rb +4 -45
  231. data/lib/active_record/type/date_time.rb +4 -49
  232. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  233. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  234. data/lib/active_record/type/internal/timezone.rb +17 -0
  235. data/lib/active_record/type/json.rb +30 -0
  236. data/lib/active_record/type/serialized.rb +27 -15
  237. data/lib/active_record/type/text.rb +2 -2
  238. data/lib/active_record/type/time.rb +21 -16
  239. data/lib/active_record/type/type_map.rb +16 -19
  240. data/lib/active_record/type/unsigned_integer.rb +9 -8
  241. data/lib/active_record/type.rb +84 -23
  242. data/lib/active_record/type_caster/connection.rb +33 -0
  243. data/lib/active_record/type_caster/map.rb +23 -0
  244. data/lib/active_record/type_caster.rb +9 -0
  245. data/lib/active_record/validations/absence.rb +25 -0
  246. data/lib/active_record/validations/associated.rb +12 -4
  247. data/lib/active_record/validations/length.rb +26 -0
  248. data/lib/active_record/validations/numericality.rb +35 -0
  249. data/lib/active_record/validations/presence.rb +14 -13
  250. data/lib/active_record/validations/uniqueness.rb +63 -56
  251. data/lib/active_record/validations.rb +39 -35
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/active_record.rb +42 -29
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes/and.rb +32 -0
  269. data/lib/arel/nodes/ascending.rb +23 -0
  270. data/lib/arel/nodes/binary.rb +126 -0
  271. data/lib/arel/nodes/bind_param.rb +44 -0
  272. data/lib/arel/nodes/case.rb +55 -0
  273. data/lib/arel/nodes/casted.rb +62 -0
  274. data/lib/arel/nodes/comment.rb +29 -0
  275. data/lib/arel/nodes/count.rb +12 -0
  276. data/lib/arel/nodes/delete_statement.rb +45 -0
  277. data/lib/arel/nodes/descending.rb +23 -0
  278. data/lib/arel/nodes/equality.rb +15 -0
  279. data/lib/arel/nodes/extract.rb +24 -0
  280. data/lib/arel/nodes/false.rb +16 -0
  281. data/lib/arel/nodes/full_outer_join.rb +8 -0
  282. data/lib/arel/nodes/function.rb +44 -0
  283. data/lib/arel/nodes/grouping.rb +11 -0
  284. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  285. data/lib/arel/nodes/in.rb +15 -0
  286. data/lib/arel/nodes/infix_operation.rb +92 -0
  287. data/lib/arel/nodes/inner_join.rb +8 -0
  288. data/lib/arel/nodes/insert_statement.rb +37 -0
  289. data/lib/arel/nodes/join_source.rb +20 -0
  290. data/lib/arel/nodes/matches.rb +18 -0
  291. data/lib/arel/nodes/named_function.rb +23 -0
  292. data/lib/arel/nodes/node.rb +51 -0
  293. data/lib/arel/nodes/node_expression.rb +13 -0
  294. data/lib/arel/nodes/ordering.rb +27 -0
  295. data/lib/arel/nodes/outer_join.rb +8 -0
  296. data/lib/arel/nodes/over.rb +15 -0
  297. data/lib/arel/nodes/regexp.rb +16 -0
  298. data/lib/arel/nodes/right_outer_join.rb +8 -0
  299. data/lib/arel/nodes/select_core.rb +67 -0
  300. data/lib/arel/nodes/select_statement.rb +41 -0
  301. data/lib/arel/nodes/sql_literal.rb +19 -0
  302. data/lib/arel/nodes/string_join.rb +11 -0
  303. data/lib/arel/nodes/table_alias.rb +31 -0
  304. data/lib/arel/nodes/terminal.rb +16 -0
  305. data/lib/arel/nodes/true.rb +16 -0
  306. data/lib/arel/nodes/unary.rb +44 -0
  307. data/lib/arel/nodes/unary_operation.rb +20 -0
  308. data/lib/arel/nodes/unqualified_column.rb +22 -0
  309. data/lib/arel/nodes/update_statement.rb +41 -0
  310. data/lib/arel/nodes/values_list.rb +9 -0
  311. data/lib/arel/nodes/window.rb +126 -0
  312. data/lib/arel/nodes/with.rb +11 -0
  313. data/lib/arel/nodes.rb +70 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors/dot.rb +308 -0
  321. data/lib/arel/visitors/mysql.rb +93 -0
  322. data/lib/arel/visitors/postgresql.rb +120 -0
  323. data/lib/arel/visitors/sqlite.rb +38 -0
  324. data/lib/arel/visitors/to_sql.rb +899 -0
  325. data/lib/arel/visitors/visitor.rb +45 -0
  326. data/lib/arel/visitors.rb +13 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +54 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
  332. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
  334. data/lib/rails/generators/active_record/migration.rb +35 -1
  335. data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +172 -65
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_decorators.rb +0 -66
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/attribute_set.rb +0 -81
  352. data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
  353. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  354. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  355. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  356. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  357. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  358. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  359. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  360. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  361. data/lib/active_record/type/big_integer.rb +0 -13
  362. data/lib/active_record/type/binary.rb +0 -50
  363. data/lib/active_record/type/boolean.rb +0 -31
  364. data/lib/active_record/type/decimal.rb +0 -64
  365. data/lib/active_record/type/decorator.rb +0 -14
  366. data/lib/active_record/type/float.rb +0 -19
  367. data/lib/active_record/type/integer.rb +0 -59
  368. data/lib/active_record/type/mutable.rb +0 -16
  369. data/lib/active_record/type/numeric.rb +0 -36
  370. data/lib/active_record/type/string.rb +0 -40
  371. data/lib/active_record/type/time_value.rb +0 -38
  372. data/lib/active_record/type/value.rb +0 -110
  373. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  374. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,140 +1,121 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module DynamicMatchers #:nodoc:
3
- # This code in this file seems to have a lot of indirection, but the indirection
4
- # is there to provide extension points for the activerecord-deprecated_finders
5
- # gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
6
- # then we can remove the indirection.
7
-
8
- def respond_to?(name, include_private = false)
9
- if self == Base
10
- super
11
- else
5
+ private
6
+ def respond_to_missing?(name, _)
7
+ if self == Base
8
+ super
9
+ else
10
+ match = Method.match(self, name)
11
+ match && match.valid? || super
12
+ end
13
+ end
14
+
15
+ def method_missing(name, *arguments, &block)
12
16
  match = Method.match(self, name)
13
- match && match.valid? || super
17
+
18
+ if match && match.valid?
19
+ match.define
20
+ send(name, *arguments, &block)
21
+ else
22
+ super
23
+ end
14
24
  end
15
- end
16
25
 
17
- private
26
+ class Method
27
+ @matchers = []
18
28
 
19
- def method_missing(name, *arguments, &block)
20
- match = Method.match(self, name)
29
+ class << self
30
+ attr_reader :matchers
21
31
 
22
- if match && match.valid?
23
- match.define
24
- send(name, *arguments, &block)
25
- else
26
- super
27
- end
28
- end
32
+ def match(model, name)
33
+ klass = matchers.find { |k| k.pattern.match?(name) }
34
+ klass.new(model, name) if klass
35
+ end
29
36
 
30
- class Method
31
- @matchers = []
37
+ def pattern
38
+ @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
39
+ end
32
40
 
33
- class << self
34
- attr_reader :matchers
41
+ def prefix
42
+ raise NotImplementedError
43
+ end
35
44
 
36
- def match(model, name)
37
- klass = matchers.find { |k| name =~ k.pattern }
38
- klass.new(model, name) if klass
45
+ def suffix
46
+ ""
47
+ end
39
48
  end
40
49
 
41
- def pattern
42
- @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
43
- end
50
+ attr_reader :model, :name, :attribute_names
44
51
 
45
- def prefix
46
- raise NotImplementedError
52
+ def initialize(model, method_name)
53
+ @model = model
54
+ @name = method_name.to_s
55
+ @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
56
+ @attribute_names.map! { |name| @model.attribute_aliases[name] || name }
47
57
  end
48
58
 
49
- def suffix
50
- ''
59
+ def valid?
60
+ attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
51
61
  end
52
- end
53
62
 
54
- attr_reader :model, :name, :attribute_names
55
-
56
- def initialize(model, name)
57
- @model = model
58
- @name = name.to_s
59
- @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
60
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
61
- end
62
-
63
- def valid?
64
- attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
65
- end
63
+ def define
64
+ model.class_eval <<-CODE, __FILE__, __LINE__ + 1
65
+ def self.#{name}(#{signature})
66
+ #{body}
67
+ end
68
+ CODE
69
+ end
66
70
 
67
- def define
68
- model.class_eval <<-CODE, __FILE__, __LINE__ + 1
69
- def self.#{name}(#{signature})
70
- #{body}
71
+ private
72
+ def body
73
+ "#{finder}(#{attributes_hash})"
71
74
  end
72
- CODE
73
- end
74
-
75
- def body
76
- raise NotImplementedError
77
- end
78
- end
79
-
80
- module Finder
81
- # Extended in activerecord-deprecated_finders
82
- def body
83
- result
84
- end
85
75
 
86
- # Extended in activerecord-deprecated_finders
87
- def result
88
- "#{finder}(#{attributes_hash})"
89
- end
90
-
91
- # The parameters in the signature may have reserved Ruby words, in order
92
- # to prevent errors, we start each param name with `_`.
93
- #
94
- # Extended in activerecord-deprecated_finders
95
- def signature
96
- attribute_names.map { |name| "_#{name}" }.join(', ')
97
- end
76
+ # The parameters in the signature may have reserved Ruby words, in order
77
+ # to prevent errors, we start each param name with `_`.
78
+ def signature
79
+ attribute_names.map { |name| "_#{name}" }.join(", ")
80
+ end
98
81
 
99
- # Given that the parameters starts with `_`, the finder needs to use the
100
- # same parameter name.
101
- def attributes_hash
102
- "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
103
- end
82
+ # Given that the parameters starts with `_`, the finder needs to use the
83
+ # same parameter name.
84
+ def attributes_hash
85
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
86
+ end
104
87
 
105
- def finder
106
- raise NotImplementedError
88
+ def finder
89
+ raise NotImplementedError
90
+ end
107
91
  end
108
- end
109
92
 
110
- class FindBy < Method
111
- Method.matchers << self
112
- include Finder
93
+ class FindBy < Method
94
+ Method.matchers << self
113
95
 
114
- def self.prefix
115
- "find_by"
116
- end
96
+ def self.prefix
97
+ "find_by"
98
+ end
117
99
 
118
- def finder
119
- "find_by"
100
+ def finder
101
+ "find_by"
102
+ end
120
103
  end
121
- end
122
104
 
123
- class FindByBang < Method
124
- Method.matchers << self
125
- include Finder
105
+ class FindByBang < Method
106
+ Method.matchers << self
126
107
 
127
- def self.prefix
128
- "find_by"
129
- end
108
+ def self.prefix
109
+ "find_by"
110
+ end
130
111
 
131
- def self.suffix
132
- "!"
133
- end
112
+ def self.suffix
113
+ "!"
114
+ end
134
115
 
135
- def finder
136
- "find_by!"
116
+ def finder
117
+ "find_by!"
118
+ end
137
119
  end
138
- end
139
120
  end
140
121
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/object/deep_dup'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
2
4
 
3
5
  module ActiveRecord
4
6
  # Declare an enum attribute where the values map to integers in the database,
@@ -29,24 +31,39 @@ module ActiveRecord
29
31
  # as well. With the above example:
30
32
  #
31
33
  # Conversation.active
34
+ # Conversation.not_active
32
35
  # Conversation.archived
36
+ # Conversation.not_archived
37
+ #
38
+ # Of course, you can also query them directly if the scopes don't fit your
39
+ # needs:
40
+ #
41
+ # Conversation.where(status: [:active, :archived])
42
+ # Conversation.where.not(status: :active)
33
43
  #
34
- # You can set the default value from the database declaration, like:
44
+ # Defining scopes can be disabled by setting +:_scopes+ to +false+.
35
45
  #
36
- # create_table :conversations do |t|
37
- # t.column :status, :integer, default: 0
46
+ # class Conversation < ActiveRecord::Base
47
+ # enum status: [ :active, :archived ], _scopes: false
38
48
  # end
39
49
  #
40
- # Good practice is to let the first declared status be the default.
50
+ # You can set the default enum value by setting +:_default+, like:
51
+ #
52
+ # class Conversation < ActiveRecord::Base
53
+ # enum status: [ :active, :archived ], _default: "active"
54
+ # end
55
+ #
56
+ # conversation = Conversation.new
57
+ # conversation.status # => "active"
41
58
  #
42
59
  # Finally, it's also possible to explicitly map the relation between attribute and
43
- # database integer with a +Hash+:
60
+ # database integer with a hash:
44
61
  #
45
62
  # class Conversation < ActiveRecord::Base
46
63
  # enum status: { active: 0, archived: 1 }
47
64
  # end
48
65
  #
49
- # Note that when an +Array+ is used, the implicit mapping from the values to database
66
+ # Note that when an array is used, the implicit mapping from the values to database
50
67
  # integers is derived from the order the values appear in the array. In the example,
51
68
  # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
52
69
  # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
@@ -54,23 +71,42 @@ module ActiveRecord
54
71
  #
55
72
  # Therefore, once a value is added to the enum array, its position in the array must
56
73
  # be maintained, and new values should only be added to the end of the array. To
57
- # remove unused values, the explicit +Hash+ syntax should be used.
74
+ # remove unused values, the explicit hash syntax should be used.
58
75
  #
59
76
  # In rare circumstances you might need to access the mapping directly.
60
77
  # The mappings are exposed through a class method with the pluralized attribute
61
- # name:
78
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
62
79
  #
63
- # Conversation.statuses # => { "active" => 0, "archived" => 1 }
80
+ # Conversation.statuses[:active] # => 0
81
+ # Conversation.statuses["archived"] # => 1
64
82
  #
65
- # Use that class method when you need to know the ordinal value of an enum:
83
+ # Use that class method when you need to know the ordinal value of an enum.
84
+ # For example, you can use that when manually building SQL strings:
66
85
  #
67
86
  # Conversation.where("status <> ?", Conversation.statuses[:archived])
68
87
  #
69
- # Where conditions on an enum attribute must use the ordinal value of an enum.
88
+ # You can use the +:_prefix+ or +:_suffix+ options when you need to define
89
+ # multiple enums with same values. If the passed value is +true+, the methods
90
+ # are prefixed/suffixed with the name of the enum. It is also possible to
91
+ # supply a custom value:
92
+ #
93
+ # class Conversation < ActiveRecord::Base
94
+ # enum status: [:active, :archived], _suffix: true
95
+ # enum comments_status: [:active, :inactive], _prefix: :comments
96
+ # end
97
+ #
98
+ # With the above example, the bang and predicate methods along with the
99
+ # associated scopes are now prefixed and/or suffixed accordingly:
100
+ #
101
+ # conversation.active_status!
102
+ # conversation.archived_status? # => false
103
+ #
104
+ # conversation.comments_inactive!
105
+ # conversation.comments_active? # => false
106
+
70
107
  module Enum
71
108
  def self.extended(base) # :nodoc:
72
- base.class_attribute(:defined_enums, instance_writer: false)
73
- base.defined_enums = {}
109
+ base.class_attribute(:defined_enums, instance_writer: false, default: {})
74
110
  end
75
111
 
76
112
  def inherited(base) # :nodoc:
@@ -78,119 +114,202 @@ module ActiveRecord
78
114
  super
79
115
  end
80
116
 
117
+ class EnumType < Type::Value # :nodoc:
118
+ delegate :type, to: :subtype
119
+
120
+ def initialize(name, mapping, subtype)
121
+ @name = name
122
+ @mapping = mapping
123
+ @subtype = subtype
124
+ end
125
+
126
+ def cast(value)
127
+ if mapping.has_key?(value)
128
+ value.to_s
129
+ elsif mapping.has_value?(value)
130
+ mapping.key(value)
131
+ elsif value.blank?
132
+ nil
133
+ else
134
+ assert_valid_value(value)
135
+ end
136
+ end
137
+
138
+ def deserialize(value)
139
+ mapping.key(subtype.deserialize(value))
140
+ end
141
+
142
+ def serialize(value)
143
+ mapping.fetch(value, value)
144
+ end
145
+
146
+ def assert_valid_value(value)
147
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
148
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
149
+ end
150
+ end
151
+
152
+ attr_reader :subtype
153
+
154
+ private
155
+ attr_reader :name, :mapping
156
+ end
157
+
81
158
  def enum(definitions)
82
- klass = self
159
+ enum_prefix = definitions.delete(:_prefix)
160
+ enum_suffix = definitions.delete(:_suffix)
161
+ enum_scopes = definitions.delete(:_scopes)
162
+
163
+ default = {}
164
+ default[:default] = definitions.delete(:_default) if definitions.key?(:_default)
165
+
83
166
  definitions.each do |name, values|
167
+ assert_valid_enum_definition_values(values)
84
168
  # statuses = { }
85
169
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
86
- name = name.to_sym
170
+ name = name.to_s
87
171
 
88
- # def self.statuses statuses end
89
- detect_enum_conflict!(name, name.to_s.pluralize, true)
90
- klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
172
+ # def self.statuses() statuses end
173
+ detect_enum_conflict!(name, name.pluralize, true)
174
+ singleton_class.define_method(name.pluralize) { enum_values }
175
+ defined_enums[name] = enum_values
91
176
 
92
- _enum_methods_module.module_eval do
93
- # def status=(value) self[:status] = statuses[value] end
94
- klass.send(:detect_enum_conflict!, name, "#{name}=")
95
- define_method("#{name}=") { |value|
96
- if enum_values.has_key?(value) || value.blank?
97
- self[name] = enum_values[value]
98
- elsif enum_values.has_value?(value)
99
- # Assigning a value directly is not a end-user feature, hence it's not documented.
100
- # This is used internally to make building objects from the generated scopes work
101
- # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
102
- self[name] = value
103
- else
104
- raise ArgumentError, "'#{value}' is not a valid #{name}"
105
- end
106
- }
177
+ detect_enum_conflict!(name, name)
178
+ detect_enum_conflict!(name, "#{name}=")
179
+
180
+ attr = attribute_alias?(name) ? attribute_alias(name) : name
181
+
182
+ decorate_attribute_type(attr, **default) do |subtype|
183
+ EnumType.new(attr, enum_values, subtype)
184
+ end
107
185
 
108
- # def status() statuses.key self[:status] end
109
- klass.send(:detect_enum_conflict!, name, name)
110
- define_method(name) { enum_values.key self[name] }
186
+ value_method_names = []
187
+ _enum_methods_module.module_eval do
188
+ prefix = if enum_prefix == true
189
+ "#{name}_"
190
+ elsif enum_prefix
191
+ "#{enum_prefix}_"
192
+ end
111
193
 
112
- # def status_before_type_cast() statuses.key self[:status] end
113
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
114
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
194
+ suffix = if enum_suffix == true
195
+ "_#{name}"
196
+ elsif enum_suffix
197
+ "_#{enum_suffix}"
198
+ end
115
199
 
116
200
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
117
- pairs.each do |value, i|
118
- enum_values[value] = i
201
+ pairs.each do |label, value|
202
+ enum_values[label] = value
203
+ label = label.to_s
119
204
 
120
- # def active?() status == 0 end
121
- klass.send(:detect_enum_conflict!, name, "#{value}?")
122
- define_method("#{value}?") { self[name] == i }
205
+ value_method_name = "#{prefix}#{label}#{suffix}"
206
+ value_method_names << value_method_name
207
+ define_enum_methods(name, value_method_name, value, enum_scopes)
123
208
 
124
- # def active!() update! status: :active end
125
- klass.send(:detect_enum_conflict!, name, "#{value}!")
126
- define_method("#{value}!") { update! name => value }
209
+ method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
210
+ value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
127
211
 
128
- # scope :active, -> { where status: 0 }
129
- klass.send(:detect_enum_conflict!, name, value, true)
130
- klass.scope value, -> { klass.where name => i }
212
+ if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
213
+ value_method_names << value_method_alias
214
+ define_enum_methods(name, value_method_alias, value, enum_scopes)
215
+ end
131
216
  end
132
217
  end
133
- defined_enums[name.to_s] = enum_values
218
+ detect_negative_enum_conditions!(value_method_names) if enum_scopes != false
219
+ enum_values.freeze
134
220
  end
135
221
  end
136
222
 
137
223
  private
224
+ class EnumMethods < Module # :nodoc:
225
+ def initialize(klass)
226
+ @klass = klass
227
+ end
228
+
229
+ private
230
+ attr_reader :klass
231
+
232
+ def define_enum_methods(name, value_method_name, value, enum_scopes)
233
+ # def active?() status_for_database == 0 end
234
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
235
+ define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
236
+
237
+ # def active!() update!(status: 0) end
238
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
239
+ define_method("#{value_method_name}!") { update!(name => value) }
240
+
241
+ # scope :active, -> { where(status: 0) }
242
+ # scope :not_active, -> { where.not(status: 0) }
243
+ if enum_scopes != false
244
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
245
+ klass.scope value_method_name, -> { where(name => value) }
246
+
247
+ klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
248
+ klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
249
+ end
250
+ end
251
+ end
252
+ private_constant :EnumMethods
253
+
138
254
  def _enum_methods_module
139
255
  @_enum_methods_module ||= begin
140
- mod = Module.new do
141
- private
142
- def save_changed_attribute(attr_name, old)
143
- if (mapping = self.class.defined_enums[attr_name.to_s])
144
- value = _read_attribute(attr_name)
145
- if attribute_changed?(attr_name)
146
- if mapping[old] == value
147
- clear_attribute_changes([attr_name])
148
- end
149
- else
150
- if old != value
151
- set_attribute_was(attr_name, mapping.key(old))
152
- end
153
- end
154
- else
155
- super
156
- end
157
- end
158
- end
256
+ mod = EnumMethods.new(self)
159
257
  include mod
160
258
  mod
161
259
  end
162
260
  end
163
261
 
262
+ def assert_valid_enum_definition_values(values)
263
+ unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
264
+ error_message = <<~MSG
265
+ Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
266
+ MSG
267
+ raise ArgumentError, error_message
268
+ end
269
+
270
+ if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
271
+ raise ArgumentError, "Enum label name must not be blank."
272
+ end
273
+ end
274
+
164
275
  ENUM_CONFLICT_MESSAGE = \
165
276
  "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
166
277
  "this will generate a %{type} method \"%{method}\", which is already defined " \
167
278
  "by %{source}."
279
+ private_constant :ENUM_CONFLICT_MESSAGE
168
280
 
169
281
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
170
282
  if klass_method && dangerous_class_method?(method_name)
171
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
172
- enum: enum_name,
173
- klass: self.name,
174
- type: 'class',
175
- method: method_name,
176
- source: 'Active Record'
177
- }
283
+ raise_conflict_error(enum_name, method_name, type: "class")
284
+ elsif klass_method && method_defined_within?(method_name, Relation)
285
+ raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
178
286
  elsif !klass_method && dangerous_attribute_method?(method_name)
179
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
180
- enum: enum_name,
181
- klass: self.name,
182
- type: 'instance',
183
- method: method_name,
184
- source: 'Active Record'
185
- }
287
+ raise_conflict_error(enum_name, method_name)
186
288
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
187
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
188
- enum: enum_name,
189
- klass: self.name,
190
- type: 'instance',
191
- method: method_name,
192
- source: 'another enum'
193
- }
289
+ raise_conflict_error(enum_name, method_name, source: "another enum")
290
+ end
291
+ end
292
+
293
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
294
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
295
+ enum: enum_name,
296
+ klass: name,
297
+ type: type,
298
+ method: method_name,
299
+ source: source
300
+ }
301
+ end
302
+
303
+ def detect_negative_enum_conditions!(method_names)
304
+ return unless logger
305
+
306
+ method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
307
+ inverted_form = potential_not.sub("not_", "")
308
+ if method_names.include?(inverted_form)
309
+ logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
310
+ " This has caused a conflict with auto generated negative scopes." \
311
+ " Avoid using enum elements starting with 'not' where the positive form is also an element."
312
+ end
194
313
  end
195
314
  end
196
315
  end