activerecord 4.2.0 → 6.0.5.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 (373) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +852 -801
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +267 -249
  9. data/lib/active_record/association_relation.rb +26 -6
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +137 -55
  12. data/lib/active_record/associations/association_scope.rb +110 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +67 -54
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -29
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +150 -275
  23. data/lib/active_record/associations/collection_proxy.rb +253 -152
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +35 -84
  26. data/lib/active_record/associations/has_many_through_association.rb +62 -80
  27. data/lib/active_record/associations/has_one_association.rb +62 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +159 -162
  33. data/lib/active_record/associations/preloader/association.rb +102 -113
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +96 -95
  36. data/lib/active_record/associations/singular_association.rb +18 -45
  37. data/lib/active_record/associations/through_association.rb +49 -24
  38. data/lib/active_record/associations.rb +1737 -1596
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -134
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
  48. data/lib/active_record/attribute_methods/write.rb +33 -56
  49. data/lib/active_record/attribute_methods.rb +124 -143
  50. data/lib/active_record/attributes.rb +213 -74
  51. data/lib/active_record/autosave_association.rb +125 -54
  52. data/lib/active_record/base.rb +60 -49
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +36 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
  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 -57
  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 +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  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 +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  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/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +299 -364
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +277 -233
  130. data/lib/active_record/counter_cache.rb +71 -50
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +172 -89
  138. data/lib/active_record/errors.rb +189 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +11 -6
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +225 -497
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +99 -98
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +636 -290
  165. data/lib/active_record/model_schema.rb +344 -112
  166. data/lib/active_record/nested_attributes.rb +265 -215
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +559 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +166 -47
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +341 -202
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +461 -302
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +270 -249
  182. data/lib/active_record/relation/delegation.rb +76 -84
  183. data/lib/active_record/relation/finder_methods.rb +287 -255
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +86 -68
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +112 -92
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +612 -392
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +18 -17
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +533 -340
  201. data/lib/active_record/result.rb +79 -43
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -20
  207. data/lib/active_record/scoping/default.rb +98 -82
  208. data/lib/active_record/scoping/named.rb +91 -33
  209. data/lib/active_record/scoping.rb +45 -27
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +309 -99
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +243 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +222 -146
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -41
  228. data/lib/active_record/type/date_time.rb +4 -38
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +29 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +21 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +43 -46
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +44 -21
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +174 -63
  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 -149
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -30
  362. data/lib/active_record/type/decimal.rb +0 -40
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -55
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -36
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -101
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  373. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,
@@ -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,174 @@ 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
88
160
 
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 }
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
165
+
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
+ value_method_names = []
177
+ pairs.each do |label, value|
178
+ if enum_prefix == true
179
+ prefix = "#{name}_"
180
+ elsif enum_prefix
181
+ prefix = "#{enum_prefix}_"
182
+ end
183
+ if enum_suffix == true
184
+ suffix = "_#{name}"
185
+ elsif enum_suffix
186
+ suffix = "_#{enum_suffix}"
106
187
  end
107
- }
108
-
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] }
112
188
 
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] }
189
+ value_method_name = "#{prefix}#{label}#{suffix}"
190
+ value_method_names << value_method_name
191
+ enum_values[label] = value
192
+ label = label.to_s
116
193
 
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
194
+ # def active?() status == "active" end
195
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
196
+ define_method("#{value_method_name}?") { self[attr] == label }
120
197
 
121
- # def active?() status == 0 end
122
- klass.send(:detect_enum_conflict!, name, "#{value}?")
123
- define_method("#{value}?") { self[name] == i }
198
+ # def active!() update!(status: 0) end
199
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
200
+ define_method("#{value_method_name}!") { update!(attr => value) }
124
201
 
125
- # def active!() update! status: :active end
126
- klass.send(:detect_enum_conflict!, name, "#{value}!")
127
- define_method("#{value}!") { update! name => value }
202
+ # scope :active, -> { where(status: 0) }
203
+ # scope :not_active, -> { where.not(status: 0) }
204
+ if enum_scopes != false
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
212
+ klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
133
213
  end
134
- defined_enums[name.to_s] = enum_values
214
+ enum_values.freeze
135
215
  end
136
216
  end
137
217
 
138
218
  private
139
219
  def _enum_methods_module
140
220
  @_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
221
+ mod = Module.new
160
222
  include mod
161
223
  mod
162
224
  end
163
225
  end
164
226
 
227
+ def assert_valid_enum_definition_values(values)
228
+ unless values.is_a?(Hash) || values.all? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
229
+ error_message = <<~MSG
230
+ Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
231
+ MSG
232
+ raise ArgumentError, error_message
233
+ end
234
+
235
+ if values.is_a?(Hash) && values.keys.any?(&:blank?) || values.is_a?(Array) && values.any?(&:blank?)
236
+ raise ArgumentError, "Enum label name must not be blank."
237
+ end
238
+ end
239
+
165
240
  ENUM_CONFLICT_MESSAGE = \
166
241
  "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
167
242
  "this will generate a %{type} method \"%{method}\", which is already defined " \
168
243
  "by %{source}."
244
+ private_constant :ENUM_CONFLICT_MESSAGE
169
245
 
170
246
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
171
247
  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
- }
248
+ raise_conflict_error(enum_name, method_name, type: "class")
249
+ elsif klass_method && method_defined_within?(method_name, Relation)
250
+ raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
179
251
  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
- }
252
+ raise_conflict_error(enum_name, method_name)
187
253
  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
- }
254
+ raise_conflict_error(enum_name, method_name, source: "another enum")
255
+ end
256
+ end
257
+
258
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
259
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
260
+ enum: enum_name,
261
+ klass: name,
262
+ type: type,
263
+ method: method_name,
264
+ source: source
265
+ }
266
+ end
267
+
268
+ def detect_negative_enum_conditions!(method_names)
269
+ return unless logger
270
+
271
+ method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
272
+ inverted_form = potential_not.sub("not_", "")
273
+ if method_names.include?(inverted_form)
274
+ logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
275
+ " This has caused a conflict with auto generated negative scopes." \
276
+ " Avoid using enum elements starting with 'not' where the positive form is also an element."
277
+ end
195
278
  end
196
279
  end
197
280
  end