activerecord 3.2.19 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,234 @@
1
+ require 'active_support/core_ext/object/deep_dup'
2
+
3
+ module ActiveRecord
4
+ # Declare an enum attribute where the values map to integers in the database,
5
+ # but can be queried by name. Example:
6
+ #
7
+ # class Conversation < ActiveRecord::Base
8
+ # enum status: [ :active, :archived ]
9
+ # end
10
+ #
11
+ # # conversation.update! status: 0
12
+ # conversation.active!
13
+ # conversation.active? # => true
14
+ # conversation.status # => "active"
15
+ #
16
+ # # conversation.update! status: 1
17
+ # conversation.archived!
18
+ # conversation.archived? # => true
19
+ # conversation.status # => "archived"
20
+ #
21
+ # # conversation.status = 1
22
+ # conversation.status = "archived"
23
+ #
24
+ # conversation.status = nil
25
+ # conversation.status.nil? # => true
26
+ # conversation.status # => nil
27
+ #
28
+ # Scopes based on the allowed values of the enum field will be provided
29
+ # as well. With the above example:
30
+ #
31
+ # Conversation.active
32
+ # Conversation.archived
33
+ #
34
+ # Of course, you can also query them directly if the scopes don't fit your
35
+ # needs:
36
+ #
37
+ # Conversation.where(status: [:active, :archived])
38
+ # Conversation.where.not(status: :active)
39
+ #
40
+ # You can set the default value from the database declaration, like:
41
+ #
42
+ # create_table :conversations do |t|
43
+ # t.column :status, :integer, default: 0
44
+ # end
45
+ #
46
+ # Good practice is to let the first declared status be the default.
47
+ #
48
+ # Finally, it's also possible to explicitly map the relation between attribute and
49
+ # database integer with a hash:
50
+ #
51
+ # class Conversation < ActiveRecord::Base
52
+ # enum status: { active: 0, archived: 1 }
53
+ # end
54
+ #
55
+ # Note that when an array is used, the implicit mapping from the values to database
56
+ # integers is derived from the order the values appear in the array. In the example,
57
+ # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
58
+ # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
59
+ # database.
60
+ #
61
+ # Therefore, once a value is added to the enum array, its position in the array must
62
+ # be maintained, and new values should only be added to the end of the array. To
63
+ # remove unused values, the explicit hash syntax should be used.
64
+ #
65
+ # In rare circumstances you might need to access the mapping directly.
66
+ # The mappings are exposed through a class method with the pluralized attribute
67
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
68
+ #
69
+ # Conversation.statuses[:active] # => 0
70
+ # Conversation.statuses["archived"] # => 1
71
+ #
72
+ # Use that class method when you need to know the ordinal value of an enum.
73
+ # For example, you can use that when manually building SQL strings:
74
+ #
75
+ # Conversation.where("status <> ?", Conversation.statuses[:archived])
76
+ #
77
+ # You can use the +:_prefix+ or +:_suffix+ options when you need to define
78
+ # multiple enums with same values. If the passed value is +true+, the methods
79
+ # are prefixed/suffixed with the name of the enum. It is also possible to
80
+ # supply a custom value:
81
+ #
82
+ # class Conversation < ActiveRecord::Base
83
+ # enum status: [:active, :archived], _suffix: true
84
+ # enum comments_status: [:active, :inactive], _prefix: :comments
85
+ # end
86
+ #
87
+ # With the above example, the bang and predicate methods along with the
88
+ # associated scopes are now prefixed and/or suffixed accordingly:
89
+ #
90
+ # conversation.active_status!
91
+ # conversation.archived_status? # => false
92
+ #
93
+ # conversation.comments_inactive!
94
+ # conversation.comments_active? # => false
95
+
96
+ module Enum
97
+ def self.extended(base) # :nodoc:
98
+ base.class_attribute(:defined_enums, instance_writer: false)
99
+ base.defined_enums = {}
100
+ end
101
+
102
+ def inherited(base) # :nodoc:
103
+ base.defined_enums = defined_enums.deep_dup
104
+ super
105
+ end
106
+
107
+ class EnumType < Type::Value # :nodoc:
108
+ def initialize(name, mapping, subtype)
109
+ @name = name
110
+ @mapping = mapping
111
+ @subtype = subtype
112
+ end
113
+
114
+ def cast(value)
115
+ return if value.blank?
116
+
117
+ if mapping.has_key?(value)
118
+ value.to_s
119
+ elsif mapping.has_value?(value)
120
+ mapping.key(value)
121
+ else
122
+ assert_valid_value(value)
123
+ end
124
+ end
125
+
126
+ def deserialize(value)
127
+ return if value.nil?
128
+ mapping.key(subtype.deserialize(value))
129
+ end
130
+
131
+ def serialize(value)
132
+ mapping.fetch(value, value)
133
+ end
134
+
135
+ def assert_valid_value(value)
136
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
137
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
138
+ end
139
+ end
140
+
141
+ protected
142
+
143
+ attr_reader :name, :mapping, :subtype
144
+ end
145
+
146
+ def enum(definitions)
147
+ klass = self
148
+ enum_prefix = definitions.delete(:_prefix)
149
+ enum_suffix = definitions.delete(:_suffix)
150
+ definitions.each do |name, values|
151
+ # statuses = { }
152
+ enum_values = ActiveSupport::HashWithIndifferentAccess.new
153
+ name = name.to_sym
154
+
155
+ # def self.statuses() statuses end
156
+ detect_enum_conflict!(name, name.to_s.pluralize, true)
157
+ klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
158
+
159
+ detect_enum_conflict!(name, name)
160
+ detect_enum_conflict!(name, "#{name}=")
161
+
162
+ decorate_attribute_type(name, :enum) do |subtype|
163
+ EnumType.new(name, enum_values, subtype)
164
+ end
165
+
166
+ _enum_methods_module.module_eval do
167
+ pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
168
+ pairs.each do |value, i|
169
+ if enum_prefix == true
170
+ prefix = "#{name}_"
171
+ elsif enum_prefix
172
+ prefix = "#{enum_prefix}_"
173
+ end
174
+ if enum_suffix == true
175
+ suffix = "_#{name}"
176
+ elsif enum_suffix
177
+ suffix = "_#{enum_suffix}"
178
+ end
179
+
180
+ value_method_name = "#{prefix}#{value}#{suffix}"
181
+ enum_values[value] = i
182
+
183
+ # def active?() status == 0 end
184
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
185
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
186
+
187
+ # def active!() update! status: :active end
188
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
189
+ define_method("#{value_method_name}!") { update! name => value }
190
+
191
+ # scope :active, -> { where status: 0 }
192
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
193
+ klass.scope value_method_name, -> { where(name => value) }
194
+ end
195
+ end
196
+ defined_enums[name.to_s] = enum_values
197
+ end
198
+ end
199
+
200
+ private
201
+ def _enum_methods_module
202
+ @_enum_methods_module ||= begin
203
+ mod = Module.new
204
+ include mod
205
+ mod
206
+ end
207
+ end
208
+
209
+ ENUM_CONFLICT_MESSAGE = \
210
+ "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
211
+ "this will generate a %{type} method \"%{method}\", which is already defined " \
212
+ "by %{source}."
213
+
214
+ def detect_enum_conflict!(enum_name, method_name, klass_method = false)
215
+ if klass_method && dangerous_class_method?(method_name)
216
+ raise_conflict_error(enum_name, method_name, type: 'class')
217
+ elsif !klass_method && dangerous_attribute_method?(method_name)
218
+ raise_conflict_error(enum_name, method_name)
219
+ elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
220
+ raise_conflict_error(enum_name, method_name, source: 'another enum')
221
+ end
222
+ end
223
+
224
+ def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
225
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
226
+ enum: enum_name,
227
+ klass: self.name,
228
+ type: type,
229
+ method: method_name,
230
+ source: source
231
+ }
232
+ end
233
+ end
234
+ end
@@ -7,8 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  # 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:
10
+ # (for example due to improper usage of column that
11
+ # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
12
+ # points to).
13
+ class SubclassNotFound < ActiveRecordError
12
14
  end
13
15
 
14
16
  # Raised when an object assigned to an association has an incorrect type.
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  # end
23
25
  #
24
26
  # # Comments are not patches, this assignment raises AssociationTypeMismatch.
25
- # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
27
+ # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
26
28
  class AssociationTypeMismatch < ActiveRecordError
27
29
  end
28
30
 
@@ -30,49 +32,91 @@ module ActiveRecord
30
32
  class SerializationTypeMismatch < ActiveRecordError
31
33
  end
32
34
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
35
+ # Raised when adapter not specified on connection (or configuration file
36
+ # +config/database.yml+ misses adapter field).
35
37
  class AdapterNotSpecified < ActiveRecordError
36
38
  end
37
39
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
40
+ # Raised when Active Record cannot find database adapter specified in
41
+ # +config/database.yml+ or programmatically.
39
42
  class AdapterNotFound < ActiveRecordError
40
43
  end
41
44
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
45
+ # Raised when connection to the database could not been established (for example when
46
+ # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
43
47
  # is given a nil object).
44
48
  class ConnectionNotEstablished < ActiveRecordError
45
49
  end
46
50
 
47
- # Raised when Active Record cannot find record by given id or set of ids.
51
+ # Raised when Active Record cannot find a record by given id or set of ids.
48
52
  class RecordNotFound < ActiveRecordError
53
+ attr_reader :model, :primary_key, :id
54
+
55
+ def initialize(message = nil, model = nil, primary_key = nil, id = nil)
56
+ @primary_key = primary_key
57
+ @model = model
58
+ @id = id
59
+
60
+ super(message)
61
+ end
49
62
  end
50
63
 
51
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
52
- # saved because record is invalid.
64
+ # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
65
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
66
+ # methods when a record is invalid and can not be saved.
53
67
  class RecordNotSaved < ActiveRecordError
54
- end
68
+ attr_reader :record
55
69
 
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
70
+ def initialize(message = nil, record = nil)
71
+ @record = record
72
+ super(message)
73
+ end
59
74
  end
60
75
 
61
- # Raised when SQL statement is invalid and the application gets a blank result.
62
- class ThrowResult < ActiveRecordError
76
+ # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
77
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
78
+ # would return false.
79
+ #
80
+ # begin
81
+ # complex_operation_that_internally_calls_destroy!
82
+ # rescue ActiveRecord::RecordNotDestroyed => invalid
83
+ # puts invalid.record.errors
84
+ # end
85
+ #
86
+ class RecordNotDestroyed < ActiveRecordError
87
+ attr_reader :record
88
+
89
+ def initialize(message = nil, record = nil)
90
+ @record = record
91
+ super(message)
92
+ end
63
93
  end
64
94
 
65
- # Parent class for all specific exceptions which wrap database driver exceptions
66
- # provides access to the original exception also.
67
- class WrappedDatabaseException < StatementInvalid
68
- attr_reader :original_exception
95
+ # Superclass for all database execution errors.
96
+ #
97
+ # Wraps the underlying database error as +cause+.
98
+ class StatementInvalid < ActiveRecordError
69
99
 
70
- def initialize(message, original_exception)
71
- super(message)
72
- @original_exception = original_exception
100
+ def initialize(message = nil, original_exception = nil)
101
+ if original_exception
102
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
103
+ "Exceptions will automatically capture the original exception.", caller)
104
+ end
105
+
106
+ super(message || $!.try(:message))
107
+ end
108
+
109
+ def original_exception
110
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
111
+ cause
73
112
  end
74
113
  end
75
114
 
115
+ # Defunct wrapper class kept for compatibility.
116
+ # StatementInvalid wraps the original exception now.
117
+ class WrappedDatabaseException < StatementInvalid
118
+ end
119
+
76
120
  # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
77
121
  class RecordNotUnique < WrappedDatabaseException
78
122
  end
@@ -81,38 +125,55 @@ module ActiveRecord
81
125
  class InvalidForeignKey < WrappedDatabaseException
82
126
  end
83
127
 
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.
128
+ # Raised when a record cannot be inserted or updated because a value too long for a column type.
129
+ class ValueTooLong < StatementInvalid
130
+ end
131
+
132
+ # Raised when number of bind variables in statement given to +:condition+ key
133
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
134
+ # does not match number of expected values supplied.
87
135
  #
88
- # For example, in
136
+ # For example, when there are two placeholders with only one value supplied:
89
137
  #
90
138
  # Location.where("lat = ? AND lng = ?", 53.7362)
91
- #
92
- # two placeholders are given but only one variable to fill them.
93
139
  class PreparedStatementInvalid < ActiveRecordError
94
140
  end
95
141
 
142
+ # Raised when a given database does not exist.
143
+ class NoDatabaseError < StatementInvalid
144
+ end
145
+
146
+ # Raised when Postgres returns 'cached plan must not change result type' and
147
+ # we cannot retry gracefully (e.g. inside a transaction)
148
+ class PreparedStatementCacheExpired < StatementInvalid
149
+ end
150
+
96
151
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
97
152
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
98
153
  # the page before the other.
99
154
  #
100
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
155
+ # Read more about optimistic locking in ActiveRecord::Locking module
156
+ # documentation.
101
157
  class StaleObjectError < ActiveRecordError
102
158
  attr_reader :record, :attempted_action
103
159
 
104
- def initialize(record, attempted_action)
105
- @record = record
106
- @attempted_action = attempted_action
160
+ def initialize(record = nil, attempted_action = nil)
161
+ if record && attempted_action
162
+ @record = record
163
+ @attempted_action = attempted_action
164
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}.")
165
+ else
166
+ super("Stale object error.")
167
+ end
107
168
  end
108
169
 
109
- def message
110
- "Attempted to #{attempted_action} a stale object: #{record.class.name}"
111
- end
112
170
  end
113
171
 
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.
172
+ # Raised when association is being configured improperly or user tries to use
173
+ # offset and limit together with
174
+ # {ActiveRecord::Base.has_many}[rdoc-ref:Associations::ClassMethods#has_many] or
175
+ # {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many]
176
+ # associations.
116
177
  class ConfigurationError < ActiveRecordError
117
178
  end
118
179
 
@@ -120,9 +181,10 @@ module ActiveRecord
120
181
  class ReadOnlyRecord < ActiveRecordError
121
182
  end
122
183
 
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
184
+ # {ActiveRecord::Base.transaction}[rdoc-ref:Transactions::ClassMethods#transaction]
185
+ # uses this exception to distinguish a deliberate rollback from other exceptional situations.
186
+ # Normally, raising an exception will cause the
187
+ # {.transaction}[rdoc-ref:Transactions::ClassMethods#transaction] method to rollback
126
188
  # the database transaction *and* pass on the exception. But if you raise an
127
189
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
128
190
  # without passing on the exception.
@@ -150,46 +212,81 @@ module ActiveRecord
150
212
  class Rollback < ActiveRecordError
151
213
  end
152
214
 
153
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
215
+ # Raised when attribute has a name reserved by Active Record (when attribute
216
+ # has name of one of Active Record instance methods).
154
217
  class DangerousAttributeError < ActiveRecordError
155
218
  end
156
219
 
157
220
  # Raised when unknown attributes are supplied via mass assignment.
158
- class UnknownAttributeError < NoMethodError
159
- end
221
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
160
222
 
161
223
  # 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.
224
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
225
+ # The exception has an +attribute+ property that is the name of the offending attribute.
164
226
  class AttributeAssignmentError < ActiveRecordError
165
227
  attr_reader :exception, :attribute
166
- def initialize(message, exception, attribute)
228
+
229
+ def initialize(message = nil, exception = nil, attribute = nil)
230
+ super(message)
167
231
  @exception = exception
168
232
  @attribute = attribute
169
- @message = message
170
233
  end
171
234
  end
172
235
 
173
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
236
+ # Raised when there are multiple errors while doing a mass assignment through the
237
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
174
238
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
175
239
  # objects, each corresponding to the error while assigning to an attribute.
176
240
  class MultiparameterAssignmentErrors < ActiveRecordError
177
241
  attr_reader :errors
178
- def initialize(errors)
242
+
243
+ def initialize(errors = nil)
179
244
  @errors = errors
180
245
  end
181
246
  end
182
247
 
183
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
248
+ # Raised when a primary key is needed, but not specified in the schema or model.
184
249
  class UnknownPrimaryKey < ActiveRecordError
185
250
  attr_reader :model
186
251
 
187
- def initialize(model)
188
- @model = model
252
+ def initialize(model = nil, description = nil)
253
+ if model
254
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
255
+ message += "\n#{description}" if description
256
+ @model = model
257
+ super(message)
258
+ else
259
+ super("Unknown primary key.")
260
+ end
189
261
  end
262
+ end
190
263
 
191
- def message
192
- "Unknown primary key for table #{model.table_name} in model #{model}."
193
- end
264
+ # Raised when a relation cannot be mutated because it's already loaded.
265
+ #
266
+ # class Task < ActiveRecord::Base
267
+ # end
268
+ #
269
+ # relation = Task.all
270
+ # relation.loaded? # => true
271
+ #
272
+ # # Methods which try to mutate a loaded relation fail.
273
+ # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
274
+ # relation.limit!(5) # => ActiveRecord::ImmutableRelation
275
+ class ImmutableRelation < ActiveRecordError
276
+ end
277
+
278
+ # TransactionIsolationError will be raised under the following conditions:
279
+ #
280
+ # * The adapter does not support setting the isolation level
281
+ # * You are joining an existing open transaction
282
+ # * You are creating a nested (savepoint) transaction
283
+ #
284
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
285
+ class TransactionIsolationError < ActiveRecordError
286
+ end
287
+
288
+ # IrreversibleOrderError is raised when a relation's order is too complex for
289
+ # +reverse_order+ to automatically reverse.
290
+ class IrreversibleOrderError < ActiveRecordError
194
291
  end
195
292
  end
@@ -1,62 +1,22 @@
1
- require 'active_support/core_ext/class/attribute'
1
+ require 'active_support/lazy_load_hooks'
2
+ require 'active_record/explain_registry'
2
3
 
3
4
  module ActiveRecord
4
5
  module Explain
5
- def self.extended(base)
6
- base.class_eval do
7
- # If a query takes longer than these many seconds we log its query plan
8
- # automatically. nil disables this feature.
9
- class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false
10
- self.auto_explain_threshold_in_seconds = nil
11
- end
12
- end
13
-
14
- # If the database adapter supports explain and auto explain is enabled,
15
- # this method triggers EXPLAIN logging for the queries triggered by the
16
- # block if it takes more than the threshold as a whole. That is, the
17
- # threshold is not checked against each individual query, but against the
18
- # duration of the entire block. This approach is convenient for relations.
19
-
20
- #
21
- # The available_queries_for_explain thread variable collects the queries
22
- # to be explained. If the value is nil, it means queries are not being
23
- # currently collected. A false value indicates collecting is turned
24
- # off. Otherwise it is an array of queries.
25
- def logging_query_plan # :nodoc:
26
- return yield unless logger
27
-
28
- threshold = auto_explain_threshold_in_seconds
29
- current = Thread.current
30
- if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
31
- begin
32
- queries = current[:available_queries_for_explain] = []
33
- start = Time.now
34
- result = yield
35
- logger.warn(exec_explain(queries)) if Time.now - start > threshold
36
- result
37
- ensure
38
- current[:available_queries_for_explain] = nil
39
- end
40
- else
41
- yield
42
- end
43
- end
44
-
45
- # Relation#explain needs to be able to collect the queries regardless of
46
- # whether auto explain is enabled. This method serves that purpose.
6
+ # Executes the block with the collect flag enabled. Queries are collected
7
+ # asynchronously by the subscriber and returned.
47
8
  def collecting_queries_for_explain # :nodoc:
48
- current = Thread.current
49
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
50
- return yield, current[:available_queries_for_explain]
9
+ ExplainRegistry.collect = true
10
+ yield
11
+ ExplainRegistry.queries
51
12
  ensure
52
- # Note that the return value above does not depend on this assigment.
53
- current[:available_queries_for_explain] = original
13
+ ExplainRegistry.reset
54
14
  end
55
15
 
56
16
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
57
17
  # Returns a formatted string ready to be logged.
58
18
  def exec_explain(queries) # :nodoc:
59
- queries && queries.map do |sql, bind|
19
+ str = queries.map do |sql, bind|
60
20
  [].tap do |msg|
61
21
  msg << "EXPLAIN for: #{sql}"
62
22
  unless bind.empty?
@@ -66,21 +26,13 @@ module ActiveRecord
66
26
  msg << connection.explain(sql, bind)
67
27
  end.join("\n")
68
28
  end.join("\n")
69
- end
70
29
 
71
- # Silences automatic EXPLAIN logging for the duration of the block.
72
- #
73
- # This has high priority, no EXPLAINs will be run even if downwards
74
- # the threshold is set to 0.
75
- #
76
- # As the name of the method suggests this only applies to automatic
77
- # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
78
- def silence_auto_explain
79
- current = Thread.current
80
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
81
- yield
82
- ensure
83
- current[:available_queries_for_explain] = original
30
+ # Overriding inspect to be more human readable, especially in the console.
31
+ def str.inspect
32
+ self
33
+ end
34
+
35
+ str
84
36
  end
85
37
  end
86
38
  end
@@ -0,0 +1,30 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for EXPLAIN. For example
5
+ #
6
+ # ActiveRecord::ExplainRegistry.queries
7
+ #
8
+ # returns the collected queries local to the current thread.
9
+ #
10
+ # See the documentation of ActiveSupport::PerThreadRegistry
11
+ # for further details.
12
+ class ExplainRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :queries, :collect
16
+
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ def collect?
22
+ @collect
23
+ end
24
+
25
+ def reset
26
+ @collect = false
27
+ @queries = []
28
+ end
29
+ end
30
+ end