activerecord 4.2.9 → 5.2.8

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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +614 -1572
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -89
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  52. data/lib/active_record/callbacks.rb +97 -57
  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/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  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 +50 -58
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +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 +9 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  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 +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  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 +107 -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 +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -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 +67 -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 +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +135 -88
  133. data/lib/active_record/errors.rb +179 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +94 -32
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -340
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +199 -124
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +24 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -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,
@@ -31,6 +33,12 @@ module ActiveRecord
31
33
  # Conversation.active
32
34
  # Conversation.archived
33
35
  #
36
+ # Of course, you can also query them directly if the scopes don't fit your
37
+ # needs:
38
+ #
39
+ # Conversation.where(status: [:active, :archived])
40
+ # Conversation.where.not(status: :active)
41
+ #
34
42
  # You can set the default value from the database declaration, like:
35
43
  #
36
44
  # create_table :conversations do |t|
@@ -40,13 +48,13 @@ module ActiveRecord
40
48
  # Good practice is to let the first declared status be the default.
41
49
  #
42
50
  # Finally, it's also possible to explicitly map the relation between attribute and
43
- # database integer with a +Hash+:
51
+ # database integer with a hash:
44
52
  #
45
53
  # class Conversation < ActiveRecord::Base
46
54
  # enum status: { active: 0, archived: 1 }
47
55
  # end
48
56
  #
49
- # Note that when an +Array+ is used, the implicit mapping from the values to database
57
+ # Note that when an array is used, the implicit mapping from the values to database
50
58
  # integers is derived from the order the values appear in the array. In the example,
51
59
  # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
52
60
  # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
@@ -54,23 +62,42 @@ module ActiveRecord
54
62
  #
55
63
  # Therefore, once a value is added to the enum array, its position in the array must
56
64
  # be maintained, and new values should only be added to the end of the array. To
57
- # remove unused values, the explicit +Hash+ syntax should be used.
65
+ # remove unused values, the explicit hash syntax should be used.
58
66
  #
59
67
  # In rare circumstances you might need to access the mapping directly.
60
68
  # The mappings are exposed through a class method with the pluralized attribute
61
- # name:
69
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
62
70
  #
63
- # Conversation.statuses # => { "active" => 0, "archived" => 1 }
71
+ # Conversation.statuses[:active] # => 0
72
+ # Conversation.statuses["archived"] # => 1
64
73
  #
65
- # Use that class method when you need to know the ordinal value of an enum:
74
+ # Use that class method when you need to know the ordinal value of an enum.
75
+ # For example, you can use that when manually building SQL strings:
66
76
  #
67
77
  # Conversation.where("status <> ?", Conversation.statuses[:archived])
68
78
  #
69
- # Where conditions on an enum attribute must use the ordinal value of an enum.
79
+ # You can use the +:_prefix+ or +:_suffix+ options when you need to define
80
+ # multiple enums with same values. If the passed value is +true+, the methods
81
+ # are prefixed/suffixed with the name of the enum. It is also possible to
82
+ # supply a custom value:
83
+ #
84
+ # class Conversation < ActiveRecord::Base
85
+ # enum status: [:active, :archived], _suffix: true
86
+ # enum comments_status: [:active, :inactive], _prefix: :comments
87
+ # end
88
+ #
89
+ # With the above example, the bang and predicate methods along with the
90
+ # associated scopes are now prefixed and/or suffixed accordingly:
91
+ #
92
+ # conversation.active_status!
93
+ # conversation.archived_status? # => false
94
+ #
95
+ # conversation.comments_inactive!
96
+ # conversation.comments_active? # => false
97
+
70
98
  module Enum
71
99
  def self.extended(base) # :nodoc:
72
- base.class_attribute(:defined_enums, instance_writer: false)
73
- base.defined_enums = {}
100
+ base.class_attribute(:defined_enums, instance_writer: false, default: {})
74
101
  end
75
102
 
76
103
  def inherited(base) # :nodoc:
@@ -78,84 +105,110 @@ module ActiveRecord
78
105
  super
79
106
  end
80
107
 
108
+ class EnumType < Type::Value # :nodoc:
109
+ delegate :type, to: :subtype
110
+
111
+ def initialize(name, mapping, subtype)
112
+ @name = name
113
+ @mapping = mapping
114
+ @subtype = subtype
115
+ end
116
+
117
+ def cast(value)
118
+ return if value.blank?
119
+
120
+ if mapping.has_key?(value)
121
+ value.to_s
122
+ elsif mapping.has_value?(value)
123
+ mapping.key(value)
124
+ else
125
+ assert_valid_value(value)
126
+ end
127
+ end
128
+
129
+ def deserialize(value)
130
+ return if value.nil?
131
+ mapping.key(subtype.deserialize(value))
132
+ end
133
+
134
+ def serialize(value)
135
+ mapping.fetch(value, value)
136
+ end
137
+
138
+ def assert_valid_value(value)
139
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
140
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
141
+ end
142
+ end
143
+
144
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
145
+ # Workaround for Ruby 2.2 "private attribute?" warning.
146
+ protected
147
+
148
+ attr_reader :name, :mapping, :subtype
149
+ end
150
+
81
151
  def enum(definitions)
82
152
  klass = self
153
+ enum_prefix = definitions.delete(:_prefix)
154
+ enum_suffix = definitions.delete(:_suffix)
83
155
  definitions.each do |name, values|
84
156
  # statuses = { }
85
157
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
86
- name = name.to_sym
158
+ name = name.to_s
87
159
 
88
- # def self.statuses statuses end
89
- detect_enum_conflict!(name, name.to_s.pluralize, true)
90
- klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
160
+ # def self.statuses() statuses end
161
+ detect_enum_conflict!(name, name.pluralize, true)
162
+ singleton_class.send(:define_method, name.pluralize) { enum_values }
163
+ defined_enums[name] = enum_values
91
164
 
92
- _enum_methods_module.module_eval do
93
- # def status=(value) self[:status] = statuses[value] end
94
- klass.send(:detect_enum_conflict!, name, "#{name}=")
95
- define_method("#{name}=") { |value|
96
- if enum_values.has_key?(value) || value.blank?
97
- self[name] = enum_values[value]
98
- elsif enum_values.has_value?(value)
99
- # Assigning a value directly is not a end-user feature, hence it's not documented.
100
- # This is used internally to make building objects from the generated scopes work
101
- # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
102
- self[name] = value
103
- else
104
- raise ArgumentError, "'#{value}' is not a valid #{name}"
105
- end
106
- }
165
+ detect_enum_conflict!(name, name)
166
+ detect_enum_conflict!(name, "#{name}=")
107
167
 
108
- # def status() statuses.key self[:status] end
109
- klass.send(:detect_enum_conflict!, name, name)
110
- define_method(name) { enum_values.key self[name] }
111
-
112
- # def status_before_type_cast() statuses.key self[:status] end
113
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
114
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
168
+ attr = attribute_alias?(name) ? attribute_alias(name) : name
169
+ decorate_attribute_type(attr, :enum) do |subtype|
170
+ EnumType.new(attr, enum_values, subtype)
171
+ end
115
172
 
173
+ _enum_methods_module.module_eval do
116
174
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
117
- pairs.each do |value, i|
118
- enum_values[value] = i
175
+ pairs.each do |label, value|
176
+ if enum_prefix == true
177
+ prefix = "#{name}_"
178
+ elsif enum_prefix
179
+ prefix = "#{enum_prefix}_"
180
+ end
181
+ if enum_suffix == true
182
+ suffix = "_#{name}"
183
+ elsif enum_suffix
184
+ suffix = "_#{enum_suffix}"
185
+ end
119
186
 
120
- # def active?() status == 0 end
121
- klass.send(:detect_enum_conflict!, name, "#{value}?")
122
- define_method("#{value}?") { self[name] == i }
187
+ value_method_name = "#{prefix}#{label}#{suffix}"
188
+ enum_values[label] = value
189
+ label = label.to_s
123
190
 
124
- # def active!() update! status: :active end
125
- klass.send(:detect_enum_conflict!, name, "#{value}!")
126
- define_method("#{value}!") { update! name => value }
191
+ # def active?() status == "active" end
192
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
193
+ define_method("#{value_method_name}?") { self[attr] == label }
127
194
 
128
- # scope :active, -> { where status: 0 }
129
- klass.send(:detect_enum_conflict!, name, value, true)
130
- klass.scope value, -> { klass.where name => i }
195
+ # def active!() update!(status: 0) end
196
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
197
+ define_method("#{value_method_name}!") { update!(attr => value) }
198
+
199
+ # scope :active, -> { where(status: 0) }
200
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
201
+ klass.scope value_method_name, -> { where(attr => value) }
131
202
  end
132
203
  end
133
- defined_enums[name.to_s] = enum_values
204
+ enum_values.freeze
134
205
  end
135
206
  end
136
207
 
137
208
  private
138
209
  def _enum_methods_module
139
210
  @_enum_methods_module ||= begin
140
- mod = Module.new do
141
- private
142
- def save_changed_attribute(attr_name, old)
143
- if (mapping = self.class.defined_enums[attr_name.to_s])
144
- value = _read_attribute(attr_name)
145
- if attribute_changed?(attr_name)
146
- if mapping[old] == value
147
- clear_attribute_changes([attr_name])
148
- end
149
- else
150
- if old != value
151
- set_attribute_was(attr_name, mapping.key(old))
152
- end
153
- end
154
- else
155
- super
156
- end
157
- end
158
- end
211
+ mod = Module.new
159
212
  include mod
160
213
  mod
161
214
  end
@@ -168,30 +221,24 @@ module ActiveRecord
168
221
 
169
222
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
170
223
  if klass_method && dangerous_class_method?(method_name)
171
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
172
- enum: enum_name,
173
- klass: self.name,
174
- type: 'class',
175
- method: method_name,
176
- source: 'Active Record'
177
- }
224
+ raise_conflict_error(enum_name, method_name, type: "class")
225
+ elsif klass_method && method_defined_within?(method_name, Relation)
226
+ raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
178
227
  elsif !klass_method && dangerous_attribute_method?(method_name)
179
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
180
- enum: enum_name,
181
- klass: self.name,
182
- type: 'instance',
183
- method: method_name,
184
- source: 'Active Record'
185
- }
228
+ raise_conflict_error(enum_name, method_name)
186
229
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
187
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
188
- enum: enum_name,
189
- klass: self.name,
190
- type: 'instance',
191
- method: method_name,
192
- source: 'another enum'
193
- }
230
+ raise_conflict_error(enum_name, method_name, source: "another enum")
194
231
  end
195
232
  end
233
+
234
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
235
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
236
+ enum: enum_name,
237
+ klass: name,
238
+ type: type,
239
+ method: method_name,
240
+ source: source
241
+ }
242
+ end
196
243
  end
197
244
  end
@@ -1,5 +1,6 @@
1
- module ActiveRecord
1
+ # frozen_string_literal: true
2
2
 
3
+ module ActiveRecord
3
4
  # = Active Record Errors
4
5
  #
5
6
  # Generic Active Record exception class.
@@ -7,8 +8,10 @@ module ActiveRecord
7
8
  end
8
9
 
9
10
  # Raised when the single-table inheritance mechanism fails to locate the subclass
10
- # (for example due to improper usage of column that +inheritance_column+ points to).
11
- class SubclassNotFound < ActiveRecordError #:nodoc:
11
+ # (for example due to improper usage of column that
12
+ # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
13
+ # points to).
14
+ class SubclassNotFound < ActiveRecordError
12
15
  end
13
16
 
14
17
  # Raised when an object assigned to an association has an incorrect type.
@@ -40,27 +43,40 @@ module ActiveRecord
40
43
  class AdapterNotFound < ActiveRecordError
41
44
  end
42
45
 
43
- # Raised when connection to the database could not been established (for
44
- # example when +connection=+ is given a nil object).
46
+ # Raised when connection to the database could not been established (for example when
47
+ # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
48
+ # is given a +nil+ object).
45
49
  class ConnectionNotEstablished < ActiveRecordError
46
50
  end
47
51
 
48
- # Raised when Active Record cannot find record by given id or set of ids.
52
+ # Raised when Active Record cannot find a record by given id or set of ids.
49
53
  class RecordNotFound < ActiveRecordError
54
+ attr_reader :model, :primary_key, :id
55
+
56
+ def initialize(message = nil, model = nil, primary_key = nil, id = nil)
57
+ @primary_key = primary_key
58
+ @model = model
59
+ @id = id
60
+
61
+ super(message)
62
+ end
50
63
  end
51
64
 
52
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
53
- # saved because record is invalid.
65
+ # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
66
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
67
+ # methods when a record is invalid and can not be saved.
54
68
  class RecordNotSaved < ActiveRecordError
55
69
  attr_reader :record
56
70
 
57
- def initialize(message, record = nil)
71
+ def initialize(message = nil, record = nil)
58
72
  @record = record
59
73
  super(message)
60
74
  end
61
75
  end
62
76
 
63
- # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
77
+ # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
78
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
79
+ # would return false.
64
80
  #
65
81
  # begin
66
82
  # complex_operation_that_internally_calls_destroy!
@@ -71,7 +87,7 @@ module ActiveRecord
71
87
  class RecordNotDestroyed < ActiveRecordError
72
88
  attr_reader :record
73
89
 
74
- def initialize(message, record = nil)
90
+ def initialize(message = nil, record = nil)
75
91
  @record = record
76
92
  super(message)
77
93
  end
@@ -79,22 +95,19 @@ module ActiveRecord
79
95
 
80
96
  # Superclass for all database execution errors.
81
97
  #
82
- # Wraps the underlying database error as +original_exception+.
98
+ # Wraps the underlying database error as +cause+.
83
99
  class StatementInvalid < ActiveRecordError
84
- attr_reader :original_exception
85
-
86
- def initialize(message, original_exception = nil)
87
- super(message)
88
- @original_exception = original_exception
100
+ def initialize(message = nil)
101
+ super(message || $!.try(:message))
89
102
  end
90
103
  end
91
104
 
92
105
  # Defunct wrapper class kept for compatibility.
93
- # +StatementInvalid+ wraps the original exception now.
106
+ # StatementInvalid wraps the original exception now.
94
107
  class WrappedDatabaseException < StatementInvalid
95
108
  end
96
109
 
97
- # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
110
+ # Raised when a record cannot be inserted or updated because it would violate a uniqueness constraint.
98
111
  class RecordNotUnique < WrappedDatabaseException
99
112
  end
100
113
 
@@ -102,9 +115,55 @@ module ActiveRecord
102
115
  class InvalidForeignKey < WrappedDatabaseException
103
116
  end
104
117
 
118
+ # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
119
+ class MismatchedForeignKey < StatementInvalid
120
+ def initialize(
121
+ adapter = nil,
122
+ message: nil,
123
+ sql: nil,
124
+ binds: nil,
125
+ table: nil,
126
+ foreign_key: nil,
127
+ target_table: nil,
128
+ primary_key: nil,
129
+ primary_key_column: nil
130
+ )
131
+ if table
132
+ type = primary_key_column.bigint? ? :bigint : primary_key_column.type
133
+ msg = <<-EOM.squish
134
+ Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
135
+ which has type `#{primary_key_column.sql_type}`.
136
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
137
+ (For example `t.#{type} :#{foreign_key}`).
138
+ EOM
139
+ else
140
+ msg = <<-EOM.squish
141
+ There is a mismatch between the foreign key and primary key column types.
142
+ Verify that the foreign key column type and the primary key of the associated table match types.
143
+ EOM
144
+ end
145
+ if message
146
+ msg << "\nOriginal message: #{message}"
147
+ end
148
+ super(msg)
149
+ end
150
+ end
151
+
152
+ # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
153
+ class NotNullViolation < StatementInvalid
154
+ end
155
+
156
+ # Raised when a record cannot be inserted or updated because a value too long for a column type.
157
+ class ValueTooLong < StatementInvalid
158
+ end
159
+
160
+ # Raised when values that executed are out of range.
161
+ class RangeError < StatementInvalid
162
+ end
163
+
105
164
  # Raised when number of bind variables in statement given to +:condition+ key
106
- # (for example, when using +find+ method) does not match number of expected
107
- # values supplied.
165
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
166
+ # does not match number of expected values supplied.
108
167
  #
109
168
  # For example, when there are two placeholders with only one value supplied:
110
169
  #
@@ -116,6 +175,11 @@ module ActiveRecord
116
175
  class NoDatabaseError < StatementInvalid
117
176
  end
118
177
 
178
+ # Raised when PostgreSQL returns 'cached plan must not change result type' and
179
+ # we cannot retry gracefully (e.g. inside a transaction)
180
+ class PreparedStatementCacheExpired < StatementInvalid
181
+ end
182
+
119
183
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
120
184
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
121
185
  # the page before the other.
@@ -125,16 +189,21 @@ module ActiveRecord
125
189
  class StaleObjectError < ActiveRecordError
126
190
  attr_reader :record, :attempted_action
127
191
 
128
- def initialize(record, attempted_action)
129
- super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
130
- @record = record
131
- @attempted_action = attempted_action
192
+ def initialize(record = nil, attempted_action = nil)
193
+ if record && attempted_action
194
+ @record = record
195
+ @attempted_action = attempted_action
196
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}.")
197
+ else
198
+ super("Stale object error.")
199
+ end
132
200
  end
133
-
134
201
  end
135
202
 
136
203
  # Raised when association is being configured improperly or user tries to use
137
- # offset and limit together with +has_many+ or +has_and_belongs_to_many+
204
+ # offset and limit together with
205
+ # {ActiveRecord::Base.has_many}[rdoc-ref:Associations::ClassMethods#has_many] or
206
+ # {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many]
138
207
  # associations.
139
208
  class ConfigurationError < ActiveRecordError
140
209
  end
@@ -143,9 +212,10 @@ module ActiveRecord
143
212
  class ReadOnlyRecord < ActiveRecordError
144
213
  end
145
214
 
146
- # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
147
- # to distinguish a deliberate rollback from other exceptional situations.
148
- # Normally, raising an exception will cause the +transaction+ method to rollback
215
+ # {ActiveRecord::Base.transaction}[rdoc-ref:Transactions::ClassMethods#transaction]
216
+ # uses this exception to distinguish a deliberate rollback from other exceptional situations.
217
+ # Normally, raising an exception will cause the
218
+ # {.transaction}[rdoc-ref:Transactions::ClassMethods#transaction] method to rollback
149
219
  # the database transaction *and* pass on the exception. But if you raise an
150
220
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
151
221
  # without passing on the exception.
@@ -179,38 +249,29 @@ module ActiveRecord
179
249
  end
180
250
 
181
251
  # Raised when unknown attributes are supplied via mass assignment.
182
- class UnknownAttributeError < NoMethodError
183
-
184
- attr_reader :record, :attribute
185
-
186
- def initialize(record, attribute)
187
- @record = record
188
- @attribute = attribute.to_s
189
- super("unknown attribute '#{attribute}' for #{@record.class}.")
190
- end
191
-
192
- end
252
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
193
253
 
194
254
  # Raised when an error occurred while doing a mass assignment to an attribute through the
195
- # +attributes=+ method. The exception has an +attribute+ property that is the name of the
196
- # offending attribute.
255
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
256
+ # The exception has an +attribute+ property that is the name of the offending attribute.
197
257
  class AttributeAssignmentError < ActiveRecordError
198
258
  attr_reader :exception, :attribute
199
259
 
200
- def initialize(message, exception, attribute)
260
+ def initialize(message = nil, exception = nil, attribute = nil)
201
261
  super(message)
202
262
  @exception = exception
203
263
  @attribute = attribute
204
264
  end
205
265
  end
206
266
 
207
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
267
+ # Raised when there are multiple errors while doing a mass assignment through the
268
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
208
269
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
209
270
  # objects, each corresponding to the error while assigning to an attribute.
210
271
  class MultiparameterAssignmentErrors < ActiveRecordError
211
272
  attr_reader :errors
212
273
 
213
- def initialize(errors)
274
+ def initialize(errors = nil)
214
275
  @errors = errors
215
276
  end
216
277
  end
@@ -219,11 +280,15 @@ module ActiveRecord
219
280
  class UnknownPrimaryKey < ActiveRecordError
220
281
  attr_reader :model
221
282
 
222
- def initialize(model, description = nil)
223
- message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
- message += "\n#{description}" if description
225
- super(message)
226
- @model = model
283
+ def initialize(model = nil, description = nil)
284
+ if model
285
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
286
+ message += "\n#{description}" if description
287
+ @model = model
288
+ super(message)
289
+ else
290
+ super("Unknown primary key.")
291
+ end
227
292
  end
228
293
  end
229
294
 
@@ -247,7 +312,69 @@ module ActiveRecord
247
312
  # * You are joining an existing open transaction
248
313
  # * You are creating a nested (savepoint) transaction
249
314
  #
250
- # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
315
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
251
316
  class TransactionIsolationError < ActiveRecordError
252
317
  end
318
+
319
+ # TransactionRollbackError will be raised when a transaction is rolled
320
+ # back by the database due to a serialization failure or a deadlock.
321
+ #
322
+ # See the following:
323
+ #
324
+ # * https://www.postgresql.org/docs/current/static/transaction-iso.html
325
+ # * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
326
+ class TransactionRollbackError < StatementInvalid
327
+ end
328
+
329
+ # SerializationFailure will be raised when a transaction is rolled
330
+ # back by the database due to a serialization failure.
331
+ class SerializationFailure < TransactionRollbackError
332
+ end
333
+
334
+ # Deadlocked will be raised when a transaction is rolled
335
+ # back by the database when a deadlock is encountered.
336
+ class Deadlocked < TransactionRollbackError
337
+ end
338
+
339
+ # IrreversibleOrderError is raised when a relation's order is too complex for
340
+ # +reverse_order+ to automatically reverse.
341
+ class IrreversibleOrderError < ActiveRecordError
342
+ end
343
+
344
+ # LockWaitTimeout will be raised when lock wait timeout exceeded.
345
+ class LockWaitTimeout < StatementInvalid
346
+ end
347
+
348
+ # StatementTimeout will be raised when statement timeout exceeded.
349
+ class StatementTimeout < StatementInvalid
350
+ end
351
+
352
+ # QueryCanceled will be raised when canceling statement due to user request.
353
+ class QueryCanceled < StatementInvalid
354
+ end
355
+
356
+ # UnknownAttributeReference is raised when an unknown and potentially unsafe
357
+ # value is passed to a query method when allow_unsafe_raw_sql is set to
358
+ # :disabled. For example, passing a non column name value to a relation's
359
+ # #order method might cause this exception.
360
+ #
361
+ # When working around this exception, caution should be taken to avoid SQL
362
+ # injection vulnerabilities when passing user-provided values to query
363
+ # methods. Known-safe values can be passed to query methods by wrapping them
364
+ # in Arel.sql.
365
+ #
366
+ # For example, with allow_unsafe_raw_sql set to :disabled, the following
367
+ # code would raise this exception:
368
+ #
369
+ # Post.order("length(title)").first
370
+ #
371
+ # The desired result can be accomplished by wrapping the known-safe string
372
+ # in Arel.sql:
373
+ #
374
+ # Post.order(Arel.sql("length(title)")).first
375
+ #
376
+ # Again, such a workaround should *not* be used when passing user-provided
377
+ # values, such as request parameters or model attributes to query methods.
378
+ class UnknownAttributeReference < ActiveRecordError
379
+ end
253
380
  end