activerecord 4.2.0 → 6.0.0

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