activerecord 3.2.22.5 → 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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  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 +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  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 +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  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 +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  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 +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  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 +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  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 +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  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 +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  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 +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  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 +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  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 +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  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 +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
4
+
5
+ module ActiveRecord
6
+ # Declare an enum attribute where the values map to integers in the database,
7
+ # but can be queried by name. Example:
8
+ #
9
+ # class Conversation < ActiveRecord::Base
10
+ # enum status: [ :active, :archived ]
11
+ # end
12
+ #
13
+ # # conversation.update! status: 0
14
+ # conversation.active!
15
+ # conversation.active? # => true
16
+ # conversation.status # => "active"
17
+ #
18
+ # # conversation.update! status: 1
19
+ # conversation.archived!
20
+ # conversation.archived? # => true
21
+ # conversation.status # => "archived"
22
+ #
23
+ # # conversation.status = 1
24
+ # conversation.status = "archived"
25
+ #
26
+ # conversation.status = nil
27
+ # conversation.status.nil? # => true
28
+ # conversation.status # => nil
29
+ #
30
+ # Scopes based on the allowed values of the enum field will be provided
31
+ # as well. With the above example:
32
+ #
33
+ # Conversation.active
34
+ # Conversation.archived
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
+ #
42
+ # You can set the default value from the database declaration, like:
43
+ #
44
+ # create_table :conversations do |t|
45
+ # t.column :status, :integer, default: 0
46
+ # end
47
+ #
48
+ # Good practice is to let the first declared status be the default.
49
+ #
50
+ # Finally, it's also possible to explicitly map the relation between attribute and
51
+ # database integer with a hash:
52
+ #
53
+ # class Conversation < ActiveRecord::Base
54
+ # enum status: { active: 0, archived: 1 }
55
+ # end
56
+ #
57
+ # Note that when an array is used, the implicit mapping from the values to database
58
+ # integers is derived from the order the values appear in the array. In the example,
59
+ # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
60
+ # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
61
+ # database.
62
+ #
63
+ # Therefore, once a value is added to the enum array, its position in the array must
64
+ # be maintained, and new values should only be added to the end of the array. To
65
+ # remove unused values, the explicit hash syntax should be used.
66
+ #
67
+ # In rare circumstances you might need to access the mapping directly.
68
+ # The mappings are exposed through a class method with the pluralized attribute
69
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
70
+ #
71
+ # Conversation.statuses[:active] # => 0
72
+ # Conversation.statuses["archived"] # => 1
73
+ #
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:
76
+ #
77
+ # Conversation.where("status <> ?", Conversation.statuses[:archived])
78
+ #
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
+
98
+ module Enum
99
+ def self.extended(base) # :nodoc:
100
+ base.class_attribute(:defined_enums, instance_writer: false, default: {})
101
+ end
102
+
103
+ def inherited(base) # :nodoc:
104
+ base.defined_enums = defined_enums.deep_dup
105
+ super
106
+ end
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
+
151
+ def enum(definitions)
152
+ klass = self
153
+ enum_prefix = definitions.delete(:_prefix)
154
+ enum_suffix = definitions.delete(:_suffix)
155
+ definitions.each do |name, values|
156
+ # statuses = { }
157
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
158
+ name = name.to_s
159
+
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
164
+
165
+ detect_enum_conflict!(name, name)
166
+ detect_enum_conflict!(name, "#{name}=")
167
+
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
172
+
173
+ _enum_methods_module.module_eval do
174
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
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
186
+
187
+ value_method_name = "#{prefix}#{label}#{suffix}"
188
+ enum_values[label] = value
189
+ label = label.to_s
190
+
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 }
194
+
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) }
202
+ end
203
+ end
204
+ enum_values.freeze
205
+ end
206
+ end
207
+
208
+ private
209
+ def _enum_methods_module
210
+ @_enum_methods_module ||= begin
211
+ mod = Module.new
212
+ include mod
213
+ mod
214
+ end
215
+ end
216
+
217
+ ENUM_CONFLICT_MESSAGE = \
218
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
219
+ "this will generate a %{type} method \"%{method}\", which is already defined " \
220
+ "by %{source}."
221
+
222
+ def detect_enum_conflict!(enum_name, method_name, klass_method = false)
223
+ if klass_method && dangerous_class_method?(method_name)
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)
227
+ elsif !klass_method && dangerous_attribute_method?(method_name)
228
+ raise_conflict_error(enum_name, method_name)
229
+ elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
230
+ raise_conflict_error(enum_name, method_name, source: "another enum")
231
+ end
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
243
+ end
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.
@@ -22,7 +25,7 @@ module ActiveRecord
22
25
  # end
23
26
  #
24
27
  # # Comments are not patches, this assignment raises AssociationTypeMismatch.
25
- # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
28
+ # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
26
29
  class AssociationTypeMismatch < ActiveRecordError
27
30
  end
28
31
 
@@ -30,50 +33,81 @@ module ActiveRecord
30
33
  class SerializationTypeMismatch < ActiveRecordError
31
34
  end
32
35
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
36
+ # Raised when adapter not specified on connection (or configuration file
37
+ # +config/database.yml+ misses adapter field).
35
38
  class AdapterNotSpecified < ActiveRecordError
36
39
  end
37
40
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
41
+ # Raised when Active Record cannot find database adapter specified in
42
+ # +config/database.yml+ or programmatically.
39
43
  class AdapterNotFound < ActiveRecordError
40
44
  end
41
45
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
43
- # 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).
44
49
  class ConnectionNotEstablished < ActiveRecordError
45
50
  end
46
51
 
47
- # 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.
48
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
49
63
  end
50
64
 
51
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
52
- # 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.
53
68
  class RecordNotSaved < ActiveRecordError
69
+ attr_reader :record
70
+
71
+ def initialize(message = nil, record = nil)
72
+ @record = record
73
+ super(message)
74
+ end
54
75
  end
55
76
 
56
- # Raised when SQL statement cannot be executed by the database (for example, it's often the case for
57
- # MySQL when Ruby driver used is too old).
58
- class StatementInvalid < ActiveRecordError
77
+ # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
78
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
79
+ # would return false.
80
+ #
81
+ # begin
82
+ # complex_operation_that_internally_calls_destroy!
83
+ # rescue ActiveRecord::RecordNotDestroyed => invalid
84
+ # puts invalid.record.errors
85
+ # end
86
+ #
87
+ class RecordNotDestroyed < ActiveRecordError
88
+ attr_reader :record
89
+
90
+ def initialize(message = nil, record = nil)
91
+ @record = record
92
+ super(message)
93
+ end
59
94
  end
60
95
 
61
- # Raised when SQL statement is invalid and the application gets a blank result.
62
- class ThrowResult < ActiveRecordError
96
+ # Superclass for all database execution errors.
97
+ #
98
+ # Wraps the underlying database error as +cause+.
99
+ class StatementInvalid < ActiveRecordError
100
+ def initialize(message = nil)
101
+ super(message || $!.try(:message))
102
+ end
63
103
  end
64
104
 
65
- # Parent class for all specific exceptions which wrap database driver exceptions
66
- # provides access to the original exception also.
105
+ # Defunct wrapper class kept for compatibility.
106
+ # StatementInvalid wraps the original exception now.
67
107
  class WrappedDatabaseException < StatementInvalid
68
- attr_reader :original_exception
69
-
70
- def initialize(message, original_exception)
71
- super(message)
72
- @original_exception = original_exception
73
- end
74
108
  end
75
109
 
76
- # 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.
77
111
  class RecordNotUnique < WrappedDatabaseException
78
112
  end
79
113
 
@@ -81,38 +115,96 @@ module ActiveRecord
81
115
  class InvalidForeignKey < WrappedDatabaseException
82
116
  end
83
117
 
84
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
85
- # when using +find+ method)
86
- # does not match number of expected variables.
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
+
164
+ # Raised when number of bind variables in statement given to +:condition+ key
165
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
166
+ # does not match number of expected values supplied.
87
167
  #
88
- # For example, in
168
+ # For example, when there are two placeholders with only one value supplied:
89
169
  #
90
170
  # Location.where("lat = ? AND lng = ?", 53.7362)
91
- #
92
- # two placeholders are given but only one variable to fill them.
93
171
  class PreparedStatementInvalid < ActiveRecordError
94
172
  end
95
173
 
174
+ # Raised when a given database does not exist.
175
+ class NoDatabaseError < StatementInvalid
176
+ end
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
+
96
183
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
97
184
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
98
185
  # the page before the other.
99
186
  #
100
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
187
+ # Read more about optimistic locking in ActiveRecord::Locking module
188
+ # documentation.
101
189
  class StaleObjectError < ActiveRecordError
102
190
  attr_reader :record, :attempted_action
103
191
 
104
- def initialize(record, attempted_action)
105
- @record = record
106
- @attempted_action = attempted_action
107
- end
108
-
109
- def message
110
- "Attempted to #{attempted_action} a stale object: #{record.class.name}"
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
111
200
  end
112
201
  end
113
202
 
114
- # Raised when association is being configured improperly or
115
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
203
+ # Raised when association is being configured improperly or user tries to use
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]
207
+ # associations.
116
208
  class ConfigurationError < ActiveRecordError
117
209
  end
118
210
 
@@ -120,9 +212,10 @@ module ActiveRecord
120
212
  class ReadOnlyRecord < ActiveRecordError
121
213
  end
122
214
 
123
- # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
124
- # to distinguish a deliberate rollback from other exceptional situations.
125
- # 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
126
219
  # the database transaction *and* pass on the exception. But if you raise an
127
220
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
128
221
  # without passing on the exception.
@@ -150,46 +243,138 @@ module ActiveRecord
150
243
  class Rollback < ActiveRecordError
151
244
  end
152
245
 
153
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
246
+ # Raised when attribute has a name reserved by Active Record (when attribute
247
+ # has name of one of Active Record instance methods).
154
248
  class DangerousAttributeError < ActiveRecordError
155
249
  end
156
250
 
157
251
  # Raised when unknown attributes are supplied via mass assignment.
158
- class UnknownAttributeError < NoMethodError
159
- end
252
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
160
253
 
161
254
  # Raised when an error occurred while doing a mass assignment to an attribute through the
162
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
163
- # 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.
164
257
  class AttributeAssignmentError < ActiveRecordError
165
258
  attr_reader :exception, :attribute
166
- def initialize(message, exception, attribute)
259
+
260
+ def initialize(message = nil, exception = nil, attribute = nil)
261
+ super(message)
167
262
  @exception = exception
168
263
  @attribute = attribute
169
- @message = message
170
264
  end
171
265
  end
172
266
 
173
- # 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=]
174
269
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
175
270
  # objects, each corresponding to the error while assigning to an attribute.
176
271
  class MultiparameterAssignmentErrors < ActiveRecordError
177
272
  attr_reader :errors
178
- def initialize(errors)
273
+
274
+ def initialize(errors = nil)
179
275
  @errors = errors
180
276
  end
181
277
  end
182
278
 
183
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
279
+ # Raised when a primary key is needed, but not specified in the schema or model.
184
280
  class UnknownPrimaryKey < ActiveRecordError
185
281
  attr_reader :model
186
282
 
187
- def initialize(model)
188
- @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
189
292
  end
293
+ end
190
294
 
191
- def message
192
- "Unknown primary key for table #{model.table_name} in model #{model}."
193
- end
295
+ # Raised when a relation cannot be mutated because it's already loaded.
296
+ #
297
+ # class Task < ActiveRecord::Base
298
+ # end
299
+ #
300
+ # relation = Task.all
301
+ # relation.loaded? # => true
302
+ #
303
+ # # Methods which try to mutate a loaded relation fail.
304
+ # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
305
+ # relation.limit!(5) # => ActiveRecord::ImmutableRelation
306
+ class ImmutableRelation < ActiveRecordError
307
+ end
308
+
309
+ # TransactionIsolationError will be raised under the following conditions:
310
+ #
311
+ # * The adapter does not support setting the isolation level
312
+ # * You are joining an existing open transaction
313
+ # * You are creating a nested (savepoint) transaction
314
+ #
315
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
316
+ class TransactionIsolationError < ActiveRecordError
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
194
379
  end
195
380
  end