activerecord 4.2.11.3 → 5.0.0.beta1

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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -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,22 +60,42 @@ 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
- base.class_attribute(:defined_enums, instance_writer: false)
98
+ base.class_attribute(:defined_enums)
73
99
  base.defined_enums = {}
74
100
  end
75
101
 
@@ -78,8 +104,48 @@ module ActiveRecord
78
104
  super
79
105
  end
80
106
 
107
+ class EnumType < Type::Value # :nodoc:
108
+ def initialize(name, mapping)
109
+ @name = name
110
+ @mapping = mapping
111
+ end
112
+
113
+ def cast(value)
114
+ return if value.blank?
115
+
116
+ if mapping.has_key?(value)
117
+ value.to_s
118
+ elsif mapping.has_value?(value)
119
+ mapping.key(value)
120
+ else
121
+ assert_valid_value(value)
122
+ end
123
+ end
124
+
125
+ def deserialize(value)
126
+ return if value.nil?
127
+ mapping.key(value.to_i)
128
+ end
129
+
130
+ def serialize(value)
131
+ mapping.fetch(value, value)
132
+ end
133
+
134
+ def assert_valid_value(value)
135
+ unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
136
+ raise ArgumentError, "'#{value}' is not a valid #{name}"
137
+ end
138
+ end
139
+
140
+ protected
141
+
142
+ attr_reader :name, :mapping
143
+ end
144
+
81
145
  def enum(definitions)
82
146
  klass = self
147
+ enum_prefix = definitions.delete(:_prefix)
148
+ enum_suffix = definitions.delete(:_suffix)
83
149
  definitions.each do |name, values|
84
150
  # statuses = { }
85
151
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
@@ -89,45 +155,39 @@ module ActiveRecord
89
155
  detect_enum_conflict!(name, name.to_s.pluralize, true)
90
156
  klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
91
157
 
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
- }
158
+ detect_enum_conflict!(name, name)
159
+ detect_enum_conflict!(name, "#{name}=")
107
160
 
108
- # def status() statuses.key self[:status] end
109
- klass.send(:detect_enum_conflict!, name, name)
110
- define_method(name) { enum_values.key self[name] }
111
-
112
- # def status_before_type_cast() statuses.key self[:status] end
113
- klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
114
- define_method("#{name}_before_type_cast") { enum_values.key self[name] }
161
+ attribute name, EnumType.new(name, enum_values)
115
162
 
163
+ _enum_methods_module.module_eval do
116
164
  pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
117
165
  pairs.each do |value, i|
166
+ if enum_prefix == true
167
+ prefix = "#{name}_"
168
+ elsif enum_prefix
169
+ prefix = "#{enum_prefix}_"
170
+ end
171
+ if enum_suffix == true
172
+ suffix = "_#{name}"
173
+ elsif enum_suffix
174
+ suffix = "_#{enum_suffix}"
175
+ end
176
+
177
+ value_method_name = "#{prefix}#{value}#{suffix}"
118
178
  enum_values[value] = i
119
179
 
120
180
  # def active?() status == 0 end
121
- klass.send(:detect_enum_conflict!, name, "#{value}?")
122
- define_method("#{value}?") { self[name] == i }
181
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
182
+ define_method("#{value_method_name}?") { self[name] == value.to_s }
123
183
 
124
184
  # def active!() update! status: :active end
125
- klass.send(:detect_enum_conflict!, name, "#{value}!")
126
- define_method("#{value}!") { update! name => value }
185
+ klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
186
+ define_method("#{value_method_name}!") { update! name => value }
127
187
 
128
188
  # scope :active, -> { where status: 0 }
129
- klass.send(:detect_enum_conflict!, name, value, true)
130
- klass.scope value, -> { klass.where name => i }
189
+ klass.send(:detect_enum_conflict!, name, value_method_name, true)
190
+ klass.scope value_method_name, -> { klass.where name => value }
131
191
  end
132
192
  end
133
193
  defined_enums[name.to_s] = enum_values
@@ -137,25 +197,7 @@ module ActiveRecord
137
197
  private
138
198
  def _enum_methods_module
139
199
  @_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
200
+ mod = Module.new
159
201
  include mod
160
202
  mod
161
203
  end
@@ -168,30 +210,22 @@ module ActiveRecord
168
210
 
169
211
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
170
212
  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
- }
213
+ raise_conflict_error(enum_name, method_name, type: 'class')
178
214
  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
- }
215
+ raise_conflict_error(enum_name, method_name)
186
216
  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
- }
217
+ raise_conflict_error(enum_name, method_name, source: 'another enum')
194
218
  end
195
219
  end
220
+
221
+ def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
222
+ raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
223
+ enum: enum_name,
224
+ klass: self.name,
225
+ type: type,
226
+ method: method_name,
227
+ source: source
228
+ }
229
+ end
196
230
  end
197
231
  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
 
@@ -103,8 +126,8 @@ module ActiveRecord
103
126
  end
104
127
 
105
128
  # 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.
129
+ # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
130
+ # does not match number of expected values supplied.
108
131
  #
109
132
  # For example, when there are two placeholders with only one value supplied:
110
133
  #
@@ -125,16 +148,22 @@ module ActiveRecord
125
148
  class StaleObjectError < ActiveRecordError
126
149
  attr_reader :record, :attempted_action
127
150
 
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
151
+ def initialize(record = nil, attempted_action = nil)
152
+ if record && attempted_action
153
+ @record = record
154
+ @attempted_action = attempted_action
155
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}.")
156
+ else
157
+ super("Stale object error.")
158
+ end
132
159
  end
133
160
 
134
161
  end
135
162
 
136
163
  # 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+
164
+ # offset and limit together with
165
+ # {ActiveRecord::Base.has_many}[rdoc-ref:Associations::ClassMethods#has_many] or
166
+ # {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many]
138
167
  # associations.
139
168
  class ConfigurationError < ActiveRecordError
140
169
  end
@@ -143,9 +172,10 @@ module ActiveRecord
143
172
  class ReadOnlyRecord < ActiveRecordError
144
173
  end
145
174
 
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
175
+ # {ActiveRecord::Base.transaction}[rdoc-ref:Transactions::ClassMethods#transaction]
176
+ # uses this exception to distinguish a deliberate rollback from other exceptional situations.
177
+ # Normally, raising an exception will cause the
178
+ # {.transaction}[rdoc-ref:Transactions::ClassMethods#transaction] method to rollback
149
179
  # the database transaction *and* pass on the exception. But if you raise an
150
180
  # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
151
181
  # without passing on the exception.
@@ -179,38 +209,29 @@ module ActiveRecord
179
209
  end
180
210
 
181
211
  # 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
212
+ UnknownAttributeError = ActiveModel::UnknownAttributeError
193
213
 
194
214
  # 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.
215
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
216
+ # The exception has an +attribute+ property that is the name of the offending attribute.
197
217
  class AttributeAssignmentError < ActiveRecordError
198
218
  attr_reader :exception, :attribute
199
219
 
200
- def initialize(message, exception, attribute)
220
+ def initialize(message = nil, exception = nil, attribute = nil)
201
221
  super(message)
202
222
  @exception = exception
203
223
  @attribute = attribute
204
224
  end
205
225
  end
206
226
 
207
- # Raised when there are multiple errors while doing a mass assignment through the +attributes+
227
+ # Raised when there are multiple errors while doing a mass assignment through the
228
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
208
229
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
209
230
  # objects, each corresponding to the error while assigning to an attribute.
210
231
  class MultiparameterAssignmentErrors < ActiveRecordError
211
232
  attr_reader :errors
212
233
 
213
- def initialize(errors)
234
+ def initialize(errors = nil)
214
235
  @errors = errors
215
236
  end
216
237
  end
@@ -219,11 +240,15 @@ module ActiveRecord
219
240
  class UnknownPrimaryKey < ActiveRecordError
220
241
  attr_reader :model
221
242
 
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
243
+ def initialize(model = nil, description = nil)
244
+ if model
245
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
246
+ message += "\n#{description}" if description
247
+ @model = model
248
+ super(message)
249
+ else
250
+ super("Unknown primary key.")
251
+ end
227
252
  end
228
253
  end
229
254