activerecord 5.0.7.2 → 6.1.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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  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.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -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.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  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 → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,121 +1,121 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module DynamicMatchers #:nodoc:
3
- def respond_to?(name, include_private = false)
4
- if self == Base
5
- super
6
- 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)
7
16
  match = Method.match(self, name)
8
- match && match.valid? || super
17
+
18
+ if match && match.valid?
19
+ match.define
20
+ send(name, *arguments, &block)
21
+ else
22
+ super
23
+ end
9
24
  end
10
- end
11
25
 
12
- private
26
+ class Method
27
+ @matchers = []
13
28
 
14
- def method_missing(name, *arguments, &block)
15
- match = Method.match(self, name)
29
+ class << self
30
+ attr_reader :matchers
16
31
 
17
- if match && match.valid?
18
- match.define
19
- send(name, *arguments, &block)
20
- else
21
- super
22
- end
23
- end
32
+ def match(model, name)
33
+ klass = matchers.find { |k| k.pattern.match?(name) }
34
+ klass.new(model, name) if klass
35
+ end
24
36
 
25
- class Method
26
- @matchers = []
37
+ def pattern
38
+ @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
39
+ end
27
40
 
28
- class << self
29
- attr_reader :matchers
41
+ def prefix
42
+ raise NotImplementedError
43
+ end
30
44
 
31
- def match(model, name)
32
- klass = matchers.find { |k| name =~ k.pattern }
33
- klass.new(model, name) if klass
45
+ def suffix
46
+ ""
47
+ end
34
48
  end
35
49
 
36
- def pattern
37
- @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
38
- end
50
+ attr_reader :model, :name, :attribute_names
39
51
 
40
- def prefix
41
- 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 }
42
57
  end
43
58
 
44
- def suffix
45
- ''
59
+ def valid?
60
+ attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
46
61
  end
47
- end
48
62
 
49
- attr_reader :model, :name, :attribute_names
50
-
51
- def initialize(model, name)
52
- @model = model
53
- @name = name.to_s
54
- @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
55
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
56
- end
57
-
58
- def valid?
59
- attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
60
- 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
61
70
 
62
- def define
63
- model.class_eval <<-CODE, __FILE__, __LINE__ + 1
64
- def self.#{name}(#{signature})
65
- #{body}
71
+ private
72
+ def body
73
+ "#{finder}(#{attributes_hash})"
66
74
  end
67
- CODE
68
- end
69
-
70
- private
71
-
72
- def body
73
- "#{finder}(#{attributes_hash})"
74
- end
75
75
 
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
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
81
81
 
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
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
87
87
 
88
- def finder
89
- raise NotImplementedError
88
+ def finder
89
+ raise NotImplementedError
90
+ end
90
91
  end
91
- end
92
92
 
93
- class FindBy < Method
94
- Method.matchers << self
93
+ class FindBy < Method
94
+ Method.matchers << self
95
95
 
96
- def self.prefix
97
- "find_by"
98
- end
96
+ def self.prefix
97
+ "find_by"
98
+ end
99
99
 
100
- def finder
101
- "find_by"
100
+ def finder
101
+ "find_by"
102
+ end
102
103
  end
103
- end
104
104
 
105
- class FindByBang < Method
106
- Method.matchers << self
105
+ class FindByBang < Method
106
+ Method.matchers << self
107
107
 
108
- def self.prefix
109
- "find_by"
110
- end
108
+ def self.prefix
109
+ "find_by"
110
+ end
111
111
 
112
- def self.suffix
113
- "!"
114
- end
112
+ def self.suffix
113
+ "!"
114
+ end
115
115
 
116
- def finder
117
- "find_by!"
116
+ def finder
117
+ "find_by!"
118
+ end
118
119
  end
119
- end
120
120
  end
121
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,7 +31,9 @@ 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
33
37
  #
34
38
  # Of course, you can also query them directly if the scopes don't fit your
35
39
  # needs:
@@ -37,13 +41,20 @@ module ActiveRecord
37
41
  # Conversation.where(status: [:active, :archived])
38
42
  # Conversation.where.not(status: :active)
39
43
  #
40
- # You can set the default value from the database declaration, like:
44
+ # Defining scopes can be disabled by setting +:_scopes+ to +false+.
45
+ #
46
+ # class Conversation < ActiveRecord::Base
47
+ # enum status: [ :active, :archived ], _scopes: false
48
+ # end
49
+ #
50
+ # You can set the default enum value by setting +:_default+, like:
41
51
  #
42
- # create_table :conversations do |t|
43
- # t.column :status, :integer, default: 0
52
+ # class Conversation < ActiveRecord::Base
53
+ # enum status: [ :active, :archived ], _default: "active"
44
54
  # end
45
55
  #
46
- # Good practice is to let the first declared status be the default.
56
+ # conversation = Conversation.new
57
+ # conversation.status # => "active"
47
58
  #
48
59
  # Finally, it's also possible to explicitly map the relation between attribute and
49
60
  # database integer with a hash:
@@ -95,8 +106,7 @@ module ActiveRecord
95
106
 
96
107
  module Enum
97
108
  def self.extended(base) # :nodoc:
98
- base.class_attribute(:defined_enums, instance_writer: false)
99
- base.defined_enums = {}
109
+ base.class_attribute(:defined_enums, instance_writer: false, default: {})
100
110
  end
101
111
 
102
112
  def inherited(base) # :nodoc:
@@ -114,124 +124,189 @@ module ActiveRecord
114
124
  end
115
125
 
116
126
  def cast(value)
117
- return if value.blank?
118
-
119
127
  if mapping.has_key?(value)
120
128
  value.to_s
121
129
  elsif mapping.has_value?(value)
122
130
  mapping.key(value)
131
+ elsif value.blank?
132
+ nil
123
133
  else
124
134
  assert_valid_value(value)
125
135
  end
126
136
  end
127
137
 
128
138
  def deserialize(value)
129
- return if value.nil?
130
139
  mapping.key(subtype.deserialize(value))
131
140
  end
132
141
 
142
+ def serializable?(value)
143
+ (value.blank? || mapping.has_key?(value) || mapping.has_value?(value)) && super
144
+ end
145
+
133
146
  def serialize(value)
134
147
  mapping.fetch(value, value)
135
148
  end
136
149
 
137
150
  def assert_valid_value(value)
138
- unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
151
+ unless serializable?(value)
139
152
  raise ArgumentError, "'#{value}' is not a valid #{name}"
140
153
  end
141
154
  end
142
155
 
143
- protected
144
-
145
- attr_reader :name, :mapping, :subtype
156
+ private
157
+ attr_reader :name, :mapping, :subtype
146
158
  end
147
159
 
148
160
  def enum(definitions)
149
- klass = self
150
161
  enum_prefix = definitions.delete(:_prefix)
151
162
  enum_suffix = definitions.delete(:_suffix)
163
+ enum_scopes = definitions.delete(:_scopes)
164
+
165
+ default = {}
166
+ default[:default] = definitions.delete(:_default) if definitions.key?(:_default)
167
+
152
168
  definitions.each do |name, values|
169
+ assert_valid_enum_definition_values(values)
153
170
  # statuses = { }
154
171
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
155
- name = name.to_sym
172
+ name = name.to_s
156
173
 
157
174
  # def self.statuses() statuses end
158
- detect_enum_conflict!(name, name.to_s.pluralize, true)
159
- klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
175
+ detect_enum_conflict!(name, name.pluralize, true)
176
+ singleton_class.define_method(name.pluralize) { enum_values }
177
+ defined_enums[name] = enum_values
160
178
 
161
179
  detect_enum_conflict!(name, name)
162
180
  detect_enum_conflict!(name, "#{name}=")
163
181
 
164
182
  attr = attribute_alias?(name) ? attribute_alias(name) : name
165
- decorate_attribute_type(attr, :enum) do |subtype|
183
+
184
+ decorate_attribute_type(attr, **default) do |subtype|
166
185
  EnumType.new(attr, enum_values, subtype)
167
186
  end
168
187
 
188
+ value_method_names = []
169
189
  _enum_methods_module.module_eval do
190
+ enum_prefix = name if enum_prefix == true
191
+ prefix = "#{enum_prefix}_" if enum_prefix
192
+
193
+ enum_suffix = name if enum_suffix == true
194
+ suffix = "_#{enum_suffix}" if enum_suffix
195
+
170
196
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
171
- pairs.each do |value, i|
172
- if enum_prefix == true
173
- prefix = "#{name}_"
174
- elsif enum_prefix
175
- prefix = "#{enum_prefix}_"
176
- end
177
- if enum_suffix == true
178
- suffix = "_#{name}"
179
- elsif enum_suffix
180
- suffix = "_#{enum_suffix}"
197
+ pairs.each do |label, value|
198
+ label = label.to_s
199
+ enum_values[label] = value
200
+
201
+ value_method_name = "#{prefix}#{label}#{suffix}"
202
+ value_method_names << value_method_name
203
+ define_enum_methods(name, value_method_name, label, enum_scopes)
204
+
205
+ method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
206
+ value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
207
+
208
+ if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
209
+ value_method_names << value_method_alias
210
+ define_enum_methods(name, value_method_alias, label, enum_scopes)
181
211
  end
212
+ end
213
+ end
214
+ detect_negative_enum_conditions!(value_method_names) if enum_scopes != false
215
+ enum_values.freeze
216
+ end
217
+ end
218
+
219
+ private
220
+ class EnumMethods < Module # :nodoc:
221
+ def initialize(klass)
222
+ @klass = klass
223
+ end
182
224
 
183
- value_method_name = "#{prefix}#{value}#{suffix}"
184
- enum_values[value] = i
225
+ private
226
+ attr_reader :klass
185
227
 
186
- # def active?() status == 0 end
228
+ def define_enum_methods(name, value_method_name, label, enum_scopes)
229
+ # def active?() status == "active" end
187
230
  klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
188
- define_method("#{value_method_name}?") { self[attr] == value.to_s }
231
+ define_method("#{value_method_name}?") { self[name] == label }
189
232
 
190
- # def active!() update! status: :active end
233
+ # def active!() update!(status: 0) end
191
234
  klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
192
- define_method("#{value_method_name}!") { update!(attr => value) }
235
+ define_method("#{value_method_name}!") { update!(name => label) }
193
236
 
194
- # scope :active, -> { where status: 0 }
195
- klass.send(:detect_enum_conflict!, name, value_method_name, true)
196
- klass.scope value_method_name, -> { where(attr => value) }
237
+ # scope :active, -> { where(status: 0) }
238
+ # scope :not_active, -> { where.not(status: 0) }
239
+ if enum_scopes != false
240
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
241
+ klass.scope value_method_name, -> { where(name => label) }
242
+
243
+ klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
244
+ klass.scope "not_#{value_method_name}", -> { where.not(name => label) }
245
+ end
197
246
  end
198
- end
199
- defined_enums[name.to_s] = enum_values
200
247
  end
201
- end
248
+ private_constant :EnumMethods
202
249
 
203
- private
204
250
  def _enum_methods_module
205
251
  @_enum_methods_module ||= begin
206
- mod = Module.new
252
+ mod = EnumMethods.new(self)
207
253
  include mod
208
254
  mod
209
255
  end
210
256
  end
211
257
 
258
+ def assert_valid_enum_definition_values(values)
259
+ unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
260
+ error_message = <<~MSG
261
+ Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
262
+ MSG
263
+ raise ArgumentError, error_message
264
+ end
265
+
266
+ if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
267
+ raise ArgumentError, "Enum label name must not be blank."
268
+ end
269
+ end
270
+
212
271
  ENUM_CONFLICT_MESSAGE = \
213
272
  "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
214
273
  "this will generate a %{type} method \"%{method}\", which is already defined " \
215
274
  "by %{source}."
275
+ private_constant :ENUM_CONFLICT_MESSAGE
216
276
 
217
277
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
218
278
  if klass_method && dangerous_class_method?(method_name)
219
- raise_conflict_error(enum_name, method_name, type: 'class')
279
+ raise_conflict_error(enum_name, method_name, type: "class")
280
+ elsif klass_method && method_defined_within?(method_name, Relation)
281
+ raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
220
282
  elsif !klass_method && dangerous_attribute_method?(method_name)
221
283
  raise_conflict_error(enum_name, method_name)
222
284
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
223
- raise_conflict_error(enum_name, method_name, source: 'another enum')
285
+ raise_conflict_error(enum_name, method_name, source: "another enum")
224
286
  end
225
287
  end
226
288
 
227
- def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
289
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
228
290
  raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
229
291
  enum: enum_name,
230
- klass: self.name,
292
+ klass: name,
231
293
  type: type,
232
294
  method: method_name,
233
295
  source: source
234
296
  }
235
297
  end
298
+
299
+ def detect_negative_enum_conditions!(method_names)
300
+ return unless logger
301
+
302
+ method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
303
+ inverted_form = potential_not.sub("not_", "")
304
+ if method_names.include?(inverted_form)
305
+ logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
306
+ " This has caused a conflict with auto generated negative scopes." \
307
+ " Avoid using enum elements starting with 'not' where the positive form is also an element."
308
+ end
309
+ end
310
+ end
236
311
  end
237
312
  end