activerecord 4.2.0 → 5.2.8.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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  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 +264 -247
  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 +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  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 +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  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 +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -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 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  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 +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  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 +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  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 +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -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 +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  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 +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -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 +50 -57
  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 +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 +9 -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 +7 -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 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  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 +466 -280
  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 +439 -330
  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 -324
  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 +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  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 +136 -90
  133. data/lib/active_record/errors.rb +180 -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 +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  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 +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  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 +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -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 +117 -35
  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 +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  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 +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  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 +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  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 -339
  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 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  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 +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  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 +208 -123
  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 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  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 +30 -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 +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  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 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  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.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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
@@ -32,6 +33,12 @@ module ActiveRecord
32
33
  # Conversation.active
33
34
  # Conversation.archived
34
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
+ #
35
42
  # You can set the default value from the database declaration, like:
36
43
  #
37
44
  # create_table :conversations do |t|
@@ -41,13 +48,13 @@ module ActiveRecord
41
48
  # Good practice is to let the first declared status be the default.
42
49
  #
43
50
  # Finally, it's also possible to explicitly map the relation between attribute and
44
- # database integer with a +Hash+:
51
+ # database integer with a hash:
45
52
  #
46
53
  # class Conversation < ActiveRecord::Base
47
54
  # enum status: { active: 0, archived: 1 }
48
55
  # end
49
56
  #
50
- # 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
51
58
  # integers is derived from the order the values appear in the array. In the example,
52
59
  # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
53
60
  # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
@@ -55,23 +62,42 @@ module ActiveRecord
55
62
  #
56
63
  # Therefore, once a value is added to the enum array, its position in the array must
57
64
  # 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.
65
+ # remove unused values, the explicit hash syntax should be used.
59
66
  #
60
67
  # In rare circumstances you might need to access the mapping directly.
61
68
  # The mappings are exposed through a class method with the pluralized attribute
62
- # name:
69
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
63
70
  #
64
- # Conversation.statuses # => { "active" => 0, "archived" => 1 }
71
+ # Conversation.statuses[:active] # => 0
72
+ # Conversation.statuses["archived"] # => 1
65
73
  #
66
- # 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:
67
76
  #
68
77
  # Conversation.where("status <> ?", Conversation.statuses[:archived])
69
78
  #
70
- # 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
+
71
98
  module Enum
72
99
  def self.extended(base) # :nodoc:
73
- base.class_attribute(:defined_enums)
74
- base.defined_enums = {}
100
+ base.class_attribute(:defined_enums, instance_writer: false, default: {})
75
101
  end
76
102
 
77
103
  def inherited(base) # :nodoc:
@@ -79,84 +105,110 @@ module ActiveRecord
79
105
  super
80
106
  end
81
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
+
82
151
  def enum(definitions)
83
152
  klass = self
153
+ enum_prefix = definitions.delete(:_prefix)
154
+ enum_suffix = definitions.delete(:_suffix)
84
155
  definitions.each do |name, values|
85
156
  # statuses = { }
86
157
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
87
- name = name.to_sym
158
+ name = name.to_s
88
159
 
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 }
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
92
164
 
93
- _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}"
106
- end
107
- }
165
+ detect_enum_conflict!(name, name)
166
+ detect_enum_conflict!(name, "#{name}=")
108
167
 
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
-
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] }
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
116
172
 
173
+ _enum_methods_module.module_eval do
117
174
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
118
- pairs.each do |value, i|
119
- 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
120
186
 
121
- # def active?() status == 0 end
122
- klass.send(:detect_enum_conflict!, name, "#{value}?")
123
- define_method("#{value}?") { self[name] == i }
187
+ value_method_name = "#{prefix}#{label}#{suffix}"
188
+ enum_values[label] = value
189
+ label = label.to_s
124
190
 
125
- # def active!() update! status: :active end
126
- klass.send(:detect_enum_conflict!, name, "#{value}!")
127
- 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 }
128
194
 
129
- # scope :active, -> { where status: 0 }
130
- klass.send(:detect_enum_conflict!, name, value, true)
131
- 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) }
132
202
  end
133
203
  end
134
- defined_enums[name.to_s] = enum_values
204
+ enum_values.freeze
135
205
  end
136
206
  end
137
207
 
138
208
  private
139
209
  def _enum_methods_module
140
210
  @_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
211
+ mod = Module.new
160
212
  include mod
161
213
  mod
162
214
  end
@@ -169,30 +221,24 @@ module ActiveRecord
169
221
 
170
222
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
171
223
  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
- }
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)
179
227
  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
- }
228
+ raise_conflict_error(enum_name, method_name)
187
229
  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
- }
230
+ raise_conflict_error(enum_name, method_name, source: "another enum")
195
231
  end
196
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
197
243
  end
198
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,30 +87,27 @@ module ActiveRecord
71
87
  class RecordNotDestroyed < ActiveRecordError
72
88
  attr_reader :record
73
89
 
74
- def initialize(record)
90
+ def initialize(message = nil, record = nil)
75
91
  @record = record
76
- super()
92
+ super(message)
77
93
  end
78
94
  end
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,16 @@ module ActiveRecord
219
280
  class UnknownPrimaryKey < ActiveRecordError
220
281
  attr_reader :model
221
282
 
222
- def initialize(model)
223
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
224
- @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
225
292
  end
226
-
227
293
  end
228
294
 
229
295
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -246,7 +312,69 @@ module ActiveRecord
246
312
  # * You are joining an existing open transaction
247
313
  # * You are creating a nested (savepoint) transaction
248
314
  #
249
- # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
315
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
250
316
  class TransactionIsolationError < ActiveRecordError
251
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
252
380
  end