activerecord 4.2.11.1 → 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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -37,23 +37,22 @@ module ActiveRecord
37
37
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
38
38
  counter_name = reflection.counter_cache_column
39
39
 
40
- stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
41
- arel_table[counter_name] => object.send(counter_association).count(:all)
42
- }, primary_key)
43
- connection.update stmt
40
+ unscoped.where(primary_key => object.id).update_all(
41
+ counter_name => object.send(counter_association).count(:all)
42
+ )
44
43
  end
45
44
  return true
46
45
  end
47
46
 
48
47
  # A generic "counter updater" implementation, intended primarily to be
49
- # used by increment_counter and decrement_counter, but which may also
48
+ # used by #increment_counter and #decrement_counter, but which may also
50
49
  # be useful on its own. It simply does a direct SQL update for the record
51
50
  # with the given ID, altering the given hash of counters by the amount
52
51
  # given by the corresponding value:
53
52
  #
54
53
  # ==== Parameters
55
54
  #
56
- # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
55
+ # * +id+ - The id of the object you wish to update a counter on or an array of ids.
57
56
  # * +counters+ - A Hash containing the names of the fields
58
57
  # to update as keys and the amount to update the field by as values.
59
58
  #
@@ -87,52 +86,42 @@ module ActiveRecord
87
86
  # Increment a numeric field by one, via a direct SQL update.
88
87
  #
89
88
  # This method is used primarily for maintaining counter_cache columns that are
90
- # used to store aggregate values. For example, a DiscussionBoard may cache
89
+ # used to store aggregate values. For example, a +DiscussionBoard+ may cache
91
90
  # posts_count and comments_count to avoid running an SQL query to calculate the
92
91
  # number of posts and comments there are, each time it is displayed.
93
92
  #
94
93
  # ==== Parameters
95
94
  #
96
95
  # * +counter_name+ - The name of the field that should be incremented.
97
- # * +id+ - The id of the object that should be incremented or an Array of ids.
96
+ # * +id+ - The id of the object that should be incremented or an array of ids.
98
97
  #
99
98
  # ==== Examples
100
99
  #
101
- # # Increment the post_count column for the record with an id of 5
102
- # DiscussionBoard.increment_counter(:post_count, 5)
100
+ # # Increment the posts_count column for the record with an id of 5
101
+ # DiscussionBoard.increment_counter(:posts_count, 5)
103
102
  def increment_counter(counter_name, id)
104
103
  update_counters(id, counter_name => 1)
105
104
  end
106
105
 
107
106
  # Decrement a numeric field by one, via a direct SQL update.
108
107
  #
109
- # This works the same as increment_counter but reduces the column value by
108
+ # This works the same as #increment_counter but reduces the column value by
110
109
  # 1 instead of increasing it.
111
110
  #
112
111
  # ==== Parameters
113
112
  #
114
113
  # * +counter_name+ - The name of the field that should be decremented.
115
- # * +id+ - The id of the object that should be decremented or an Array of ids.
114
+ # * +id+ - The id of the object that should be decremented or an array of ids.
116
115
  #
117
116
  # ==== Examples
118
117
  #
119
- # # Decrement the post_count column for the record with an id of 5
120
- # DiscussionBoard.decrement_counter(:post_count, 5)
118
+ # # Decrement the posts_count column for the record with an id of 5
119
+ # DiscussionBoard.decrement_counter(:posts_count, 5)
121
120
  def decrement_counter(counter_name, id)
122
121
  update_counters(id, counter_name => -1)
123
122
  end
124
123
  end
125
124
 
126
- protected
127
-
128
- def actually_destroyed?
129
- @_actually_destroyed
130
- end
131
-
132
- def clear_destroy_state
133
- @_actually_destroyed = nil
134
- end
135
-
136
125
  private
137
126
 
138
127
  def _create_record(*)
@@ -1,10 +1,5 @@
1
1
  module ActiveRecord
2
2
  module DynamicMatchers #:nodoc:
3
- # This code in this file seems to have a lot of indirection, but the indirection
4
- # is there to provide extension points for the activerecord-deprecated_finders
5
- # gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
6
- # then we can remove the indirection.
7
-
8
3
  def respond_to?(name, include_private = false)
9
4
  if self == Base
10
5
  super
@@ -72,26 +67,14 @@ module ActiveRecord
72
67
  CODE
73
68
  end
74
69
 
75
- def body
76
- raise NotImplementedError
77
- end
78
- end
70
+ private
79
71
 
80
- module Finder
81
- # Extended in activerecord-deprecated_finders
82
72
  def body
83
- result
84
- end
85
-
86
- # Extended in activerecord-deprecated_finders
87
- def result
88
73
  "#{finder}(#{attributes_hash})"
89
74
  end
90
75
 
91
76
  # The parameters in the signature may have reserved Ruby words, in order
92
77
  # to prevent errors, we start each param name with `_`.
93
- #
94
- # Extended in activerecord-deprecated_finders
95
78
  def signature
96
79
  attribute_names.map { |name| "_#{name}" }.join(', ')
97
80
  end
@@ -109,7 +92,6 @@ module ActiveRecord
109
92
 
110
93
  class FindBy < Method
111
94
  Method.matchers << self
112
- include Finder
113
95
 
114
96
  def self.prefix
115
97
  "find_by"
@@ -122,7 +104,6 @@ module ActiveRecord
122
104
 
123
105
  class FindByBang < Method
124
106
  Method.matchers << self
125
- include Finder
126
107
 
127
108
  def self.prefix
128
109
  "find_by"
@@ -31,6 +31,12 @@ module ActiveRecord
31
31
  # Conversation.active
32
32
  # Conversation.archived
33
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
+ #
34
40
  # You can set the default value from the database declaration, like:
35
41
  #
36
42
  # create_table :conversations do |t|
@@ -40,13 +46,13 @@ module ActiveRecord
40
46
  # Good practice is to let the first declared status be the default.
41
47
  #
42
48
  # Finally, it's also possible to explicitly map the relation between attribute and
43
- # database integer with a +Hash+:
49
+ # database integer with a hash:
44
50
  #
45
51
  # class Conversation < ActiveRecord::Base
46
52
  # enum status: { active: 0, archived: 1 }
47
53
  # end
48
54
  #
49
- # Note that when an +Array+ is used, the implicit mapping from the values to database
55
+ # Note that when an array is used, the implicit mapping from the values to database
50
56
  # integers is derived from the order the values appear in the array. In the example,
51
57
  # <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
52
58
  # is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
@@ -54,19 +60,39 @@ module ActiveRecord
54
60
  #
55
61
  # Therefore, once a value is added to the enum array, its position in the array must
56
62
  # be maintained, and new values should only be added to the end of the array. To
57
- # remove unused values, the explicit +Hash+ syntax should be used.
63
+ # remove unused values, the explicit hash syntax should be used.
58
64
  #
59
65
  # In rare circumstances you might need to access the mapping directly.
60
66
  # The mappings are exposed through a class method with the pluralized attribute
61
- # name:
67
+ # name, which return the mapping in a +HashWithIndifferentAccess+:
62
68
  #
63
- # Conversation.statuses # => { "active" => 0, "archived" => 1 }
69
+ # Conversation.statuses[:active] # => 0
70
+ # Conversation.statuses["archived"] # => 1
64
71
  #
65
- # Use that class method when you need to know the ordinal value of an enum:
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:
66
74
  #
67
75
  # Conversation.where("status <> ?", Conversation.statuses[:archived])
68
76
  #
69
- # Where conditions on an enum attribute must use the ordinal value of an enum.
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
+
70
96
  module Enum
71
97
  def self.extended(base) # :nodoc:
72
98
  base.class_attribute(:defined_enums, instance_writer: false)
@@ -78,56 +104,93 @@ module ActiveRecord
78
104
  super
79
105
  end
80
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
+
81
146
  def enum(definitions)
82
147
  klass = self
148
+ enum_prefix = definitions.delete(:_prefix)
149
+ enum_suffix = definitions.delete(:_suffix)
83
150
  definitions.each do |name, values|
84
151
  # statuses = { }
85
152
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
86
153
  name = name.to_sym
87
154
 
88
- # def self.statuses statuses end
155
+ # def self.statuses() statuses end
89
156
  detect_enum_conflict!(name, name.to_s.pluralize, true)
90
157
  klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
91
158
 
92
- _enum_methods_module.module_eval do
93
- # def status=(value) self[:status] = statuses[value] end
94
- klass.send(:detect_enum_conflict!, name, "#{name}=")
95
- define_method("#{name}=") { |value|
96
- if enum_values.has_key?(value) || value.blank?
97
- self[name] = enum_values[value]
98
- elsif enum_values.has_value?(value)
99
- # Assigning a value directly is not a end-user feature, hence it's not documented.
100
- # This is used internally to make building objects from the generated scopes work
101
- # as expected, i.e. +Conversation.archived.build.archived?+ should be true.
102
- self[name] = value
103
- else
104
- raise ArgumentError, "'#{value}' is not a valid #{name}"
105
- end
106
- }
107
-
108
- # def status() statuses.key self[:status] end
109
- klass.send(:detect_enum_conflict!, name, name)
110
- define_method(name) { enum_values.key self[name] }
159
+ detect_enum_conflict!(name, name)
160
+ detect_enum_conflict!(name, "#{name}=")
111
161
 
112
- # def status_before_type_cast() statuses.key self[:status] end
113
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
114
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
162
+ decorate_attribute_type(name, :enum) do |subtype|
163
+ EnumType.new(name, enum_values, subtype)
164
+ end
115
165
 
166
+ _enum_methods_module.module_eval do
116
167
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
117
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}"
118
181
  enum_values[value] = i
119
182
 
120
183
  # def active?() status == 0 end
121
- klass.send(:detect_enum_conflict!, name, "#{value}?")
122
- define_method("#{value}?") { self[name] == i }
184
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
185
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
123
186
 
124
187
  # def active!() update! status: :active end
125
- klass.send(:detect_enum_conflict!, name, "#{value}!")
126
- define_method("#{value}!") { update! name => value }
188
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
189
+ define_method("#{value_method_name}!") { update! name => value }
127
190
 
128
191
  # scope :active, -> { where status: 0 }
129
- klass.send(:detect_enum_conflict!, name, value, true)
130
- klass.scope value, -> { klass.where name => i }
192
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
193
+ klass.scope value_method_name, -> { where(name => value) }
131
194
  end
132
195
  end
133
196
  defined_enums[name.to_s] = enum_values
@@ -137,25 +200,7 @@ module ActiveRecord
137
200
  private
138
201
  def _enum_methods_module
139
202
  @_enum_methods_module ||= begin
140
- mod = Module.new do
141
- private
142
- def save_changed_attribute(attr_name, old)
143
- if (mapping = self.class.defined_enums[attr_name.to_s])
144
- value = _read_attribute(attr_name)
145
- if attribute_changed?(attr_name)
146
- if mapping[old] == value
147
- clear_attribute_changes([attr_name])
148
- end
149
- else
150
- if old != value
151
- set_attribute_was(attr_name, mapping.key(old))
152
- end
153
- end
154
- else
155
- super
156
- end
157
- end
158
- end
203
+ mod = Module.new
159
204
  include mod
160
205
  mod
161
206
  end
@@ -168,30 +213,22 @@ module ActiveRecord
168
213
 
169
214
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
170
215
  if klass_method && dangerous_class_method?(method_name)
171
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
172
- enum: enum_name,
173
- klass: self.name,
174
- type: 'class',
175
- method: method_name,
176
- source: 'Active Record'
177
- }
216
+ raise_conflict_error(enum_name, method_name, type: 'class')
178
217
  elsif !klass_method && dangerous_attribute_method?(method_name)
179
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
180
- enum: enum_name,
181
- klass: self.name,
182
- type: 'instance',
183
- method: method_name,
184
- source: 'Active Record'
185
- }
218
+ raise_conflict_error(enum_name, method_name)
186
219
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
187
- raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
188
- enum: enum_name,
189
- klass: self.name,
190
- type: 'instance',
191
- method: method_name,
192
- source: 'another enum'
193
- }
220
+ raise_conflict_error(enum_name, method_name, source: 'another enum')
194
221
  end
195
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
196
233
  end
197
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.
@@ -40,27 +42,40 @@ module ActiveRecord
40
42
  class AdapterNotFound < ActiveRecordError
41
43
  end
42
44
 
43
- # Raised when connection to the database could not been established (for
44
- # example when +connection=+ is given a nil object).
45
+ # Raised when connection to the database could not been established (for example when
46
+ # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
47
+ # is given a nil object).
45
48
  class ConnectionNotEstablished < ActiveRecordError
46
49
  end
47
50
 
48
- # 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.
49
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
50
62
  end
51
63
 
52
- # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
53
- # 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.
54
67
  class RecordNotSaved < ActiveRecordError
55
68
  attr_reader :record
56
69
 
57
- def initialize(message, record = nil)
70
+ def initialize(message = nil, record = nil)
58
71
  @record = record
59
72
  super(message)
60
73
  end
61
74
  end
62
75
 
63
- # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
76
+ # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
77
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
78
+ # would return false.
64
79
  #
65
80
  # begin
66
81
  # complex_operation_that_internally_calls_destroy!
@@ -71,7 +86,7 @@ module ActiveRecord
71
86
  class RecordNotDestroyed < ActiveRecordError
72
87
  attr_reader :record
73
88
 
74
- def initialize(message, record = nil)
89
+ def initialize(message = nil, record = nil)
75
90
  @record = record
76
91
  super(message)
77
92
  end
@@ -79,18 +94,26 @@ module ActiveRecord
79
94
 
80
95
  # Superclass for all database execution errors.
81
96
  #
82
- # Wraps the underlying database error as +original_exception+.
97
+ # Wraps the underlying database error as +cause+.
83
98
  class StatementInvalid < ActiveRecordError
84
- attr_reader :original_exception
85
99
 
86
- def initialize(message, original_exception = nil)
87
- super(message)
88
- @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
89
112
  end
90
113
  end
91
114
 
92
115
  # Defunct wrapper class kept for compatibility.
93
- # +StatementInvalid+ wraps the original exception now.
116
+ # StatementInvalid wraps the original exception now.
94
117
  class WrappedDatabaseException < StatementInvalid
95
118
  end
96
119
 
@@ -102,9 +125,13 @@ module ActiveRecord
102
125
  class InvalidForeignKey < WrappedDatabaseException
103
126
  end
104
127
 
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
+
105
132
  # 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.
133
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
134
+ # does not match number of expected values supplied.
108
135
  #
109
136
  # For example, when there are two placeholders with only one value supplied:
110
137
  #
@@ -116,6 +143,11 @@ module ActiveRecord
116
143
  class NoDatabaseError < StatementInvalid
117
144
  end
118
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
+
119
151
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
120
152
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
121
153
  # the page before the other.
@@ -125,16 +157,22 @@ module ActiveRecord
125
157
  class StaleObjectError < ActiveRecordError
126
158
  attr_reader :record, :attempted_action
127
159
 
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
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
132
168
  end
133
169
 
134
170
  end
135
171
 
136
172
  # 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+
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]
138
176
  # associations.
139
177
  class ConfigurationError < ActiveRecordError
140
178
  end
@@ -143,9 +181,10 @@ module ActiveRecord
143
181
  class ReadOnlyRecord < ActiveRecordError
144
182
  end
145
183
 
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
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
149
188
  # the database transaction *and* pass on the exception. But if you raise an
150
189
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
151
190
  # without passing on the exception.
@@ -179,38 +218,29 @@ module ActiveRecord
179
218
  end
180
219
 
181
220
  # 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
221
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
193
222
 
194
223
  # 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.
224
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
225
+ # The exception has an +attribute+ property that is the name of the offending attribute.
197
226
  class AttributeAssignmentError < ActiveRecordError
198
227
  attr_reader :exception, :attribute
199
228
 
200
- def initialize(message, exception, attribute)
229
+ def initialize(message = nil, exception = nil, attribute = nil)
201
230
  super(message)
202
231
  @exception = exception
203
232
  @attribute = attribute
204
233
  end
205
234
  end
206
235
 
207
- # 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=]
208
238
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
209
239
  # objects, each corresponding to the error while assigning to an attribute.
210
240
  class MultiparameterAssignmentErrors < ActiveRecordError
211
241
  attr_reader :errors
212
242
 
213
- def initialize(errors)
243
+ def initialize(errors = nil)
214
244
  @errors = errors
215
245
  end
216
246
  end
@@ -219,11 +249,15 @@ module ActiveRecord
219
249
  class UnknownPrimaryKey < ActiveRecordError
220
250
  attr_reader :model
221
251
 
222
- def initialize(model, description = nil)
223
- message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
- message += "\n#{description}" if description
225
- super(message)
226
- @model = model
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
227
261
  end
228
262
  end
229
263
 
@@ -247,7 +281,12 @@ module ActiveRecord
247
281
  # * You are joining an existing open transaction
248
282
  # * You are creating a nested (savepoint) transaction
249
283
  #
250
- # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
284
+ # The mysql2 and postgresql adapters support setting the transaction isolation level.
251
285
  class TransactionIsolationError < ActiveRecordError
252
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
291
+ end
253
292
  end