activemodel 4.2.11.3 → 5.0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +149 -56
- data/MIT-LICENSE +1 -1
- data/README.rdoc +8 -16
- data/lib/active_model/attribute_assignment.rb +52 -0
- data/lib/active_model/attribute_methods.rb +16 -16
- data/lib/active_model/callbacks.rb +3 -3
- data/lib/active_model/conversion.rb +5 -5
- data/lib/active_model/dirty.rb +41 -40
- data/lib/active_model/errors.rb +175 -68
- data/lib/active_model/forbidden_attributes_protection.rb +3 -2
- data/lib/active_model/gem_version.rb +5 -5
- data/lib/active_model/lint.rb +32 -28
- data/lib/active_model/locale/en.yml +2 -1
- data/lib/active_model/model.rb +3 -4
- data/lib/active_model/naming.rb +5 -4
- data/lib/active_model/secure_password.rb +2 -9
- data/lib/active_model/serialization.rb +36 -9
- data/lib/active_model/type/big_integer.rb +13 -0
- data/lib/active_model/type/binary.rb +50 -0
- data/lib/active_model/type/boolean.rb +21 -0
- data/lib/active_model/type/date.rb +54 -0
- data/lib/active_model/type/date_time.rb +44 -0
- data/lib/active_model/type/decimal.rb +66 -0
- data/lib/active_model/type/decimal_without_scale.rb +11 -0
- data/lib/active_model/type/float.rb +25 -0
- data/lib/active_model/type/helpers/accepts_multiparameter_time.rb +35 -0
- data/lib/active_model/type/helpers/mutable.rb +18 -0
- data/lib/active_model/type/helpers/numeric.rb +34 -0
- data/lib/active_model/type/helpers/time_value.rb +77 -0
- data/lib/active_model/type/helpers.rb +4 -0
- data/lib/active_model/type/immutable_string.rb +29 -0
- data/lib/active_model/type/integer.rb +66 -0
- data/lib/active_model/type/registry.rb +64 -0
- data/lib/active_model/type/string.rb +24 -0
- data/lib/active_model/type/text.rb +11 -0
- data/lib/active_model/type/time.rb +42 -0
- data/lib/active_model/type/unsigned_integer.rb +15 -0
- data/lib/active_model/type/value.rb +116 -0
- data/lib/active_model/type.rb +59 -0
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +57 -9
- data/lib/active_model/validations/callbacks.rb +3 -3
- data/lib/active_model/validations/clusivity.rb +4 -3
- data/lib/active_model/validations/confirmation.rb +16 -4
- data/lib/active_model/validations/exclusion.rb +3 -1
- data/lib/active_model/validations/format.rb +1 -1
- data/lib/active_model/validations/helper_methods.rb +13 -0
- data/lib/active_model/validations/inclusion.rb +3 -3
- data/lib/active_model/validations/length.rb +49 -18
- data/lib/active_model/validations/numericality.rb +20 -11
- data/lib/active_model/validations/validates.rb +1 -1
- data/lib/active_model/validations/with.rb +0 -10
- data/lib/active_model/validations.rb +34 -1
- data/lib/active_model/validator.rb +5 -5
- data/lib/active_model/version.rb +1 -1
- data/lib/active_model.rb +4 -2
- metadata +31 -22
- data/lib/active_model/serializers/xml.rb +0 -238
data/lib/active_model/dirty.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'active_support/hash_with_indifferent_access'
|
2
2
|
require 'active_support/core_ext/object/duplicable'
|
3
|
-
require 'active_support/core_ext/string/filters'
|
4
3
|
|
5
4
|
module ActiveModel
|
6
5
|
# == Active \Model \Dirty
|
@@ -13,7 +12,7 @@ module ActiveModel
|
|
13
12
|
# * <tt>include ActiveModel::Dirty</tt> in your object.
|
14
13
|
# * Call <tt>define_attribute_methods</tt> passing each method you want to
|
15
14
|
# track.
|
16
|
-
# * Call <tt>
|
15
|
+
# * Call <tt>[attr_name]_will_change!</tt> before each change to the tracked
|
17
16
|
# attribute.
|
18
17
|
# * Call <tt>changes_applied</tt> after the changes are persisted.
|
19
18
|
# * Call <tt>clear_changes_information</tt> when you want to reset the changes
|
@@ -81,9 +80,11 @@ module ActiveModel
|
|
81
80
|
#
|
82
81
|
# Reset the changes:
|
83
82
|
#
|
84
|
-
# person.previous_changes
|
83
|
+
# person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
|
84
|
+
# person.name_previously_changed? # => true
|
85
|
+
# person.name_previous_change # => ["Uncle Bob", "Bill"]
|
85
86
|
# person.reload!
|
86
|
-
# person.previous_changes
|
87
|
+
# person.previous_changes # => {}
|
87
88
|
#
|
88
89
|
# Rollback the changes:
|
89
90
|
#
|
@@ -105,10 +106,10 @@ module ActiveModel
|
|
105
106
|
# person.changes # => {"name" => ["Bill", "Bob"]}
|
106
107
|
#
|
107
108
|
# If an attribute is modified in-place then make use of
|
108
|
-
#
|
109
|
-
# Otherwise Active Model can't track changes to in-place attributes. Note
|
109
|
+
# <tt>[attribute_name]_will_change!</tt> to mark that the attribute is changing.
|
110
|
+
# Otherwise \Active \Model can't track changes to in-place attributes. Note
|
110
111
|
# that Active Record can detect in-place modifications automatically. You do
|
111
|
-
# not need to call
|
112
|
+
# not need to call <tt>[attribute_name]_will_change!</tt> on Active Record models.
|
112
113
|
#
|
113
114
|
# person.name_will_change!
|
114
115
|
# person.name_change # => ["Bill", "Bill"]
|
@@ -118,13 +119,16 @@ module ActiveModel
|
|
118
119
|
extend ActiveSupport::Concern
|
119
120
|
include ActiveModel::AttributeMethods
|
120
121
|
|
122
|
+
OPTION_NOT_GIVEN = Object.new # :nodoc:
|
123
|
+
private_constant :OPTION_NOT_GIVEN
|
124
|
+
|
121
125
|
included do
|
122
126
|
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
|
123
|
-
|
127
|
+
attribute_method_suffix '_previously_changed?', '_previous_change'
|
124
128
|
attribute_method_affix prefix: 'restore_', suffix: '!'
|
125
129
|
end
|
126
130
|
|
127
|
-
# Returns +true+ if any
|
131
|
+
# Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise.
|
128
132
|
#
|
129
133
|
# person.changed? # => false
|
130
134
|
# person.name = 'bob'
|
@@ -172,19 +176,23 @@ module ActiveModel
|
|
172
176
|
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
|
173
177
|
end
|
174
178
|
|
175
|
-
#
|
176
|
-
def attribute_changed?(attr,
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
result
|
179
|
+
# Handles <tt>*_changed?</tt> for +method_missing+.
|
180
|
+
def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc:
|
181
|
+
!!changes_include?(attr) &&
|
182
|
+
(to == OPTION_NOT_GIVEN || to == __send__(attr)) &&
|
183
|
+
(from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
|
181
184
|
end
|
182
185
|
|
183
|
-
#
|
186
|
+
# Handles <tt>*_was</tt> for +method_missing+.
|
184
187
|
def attribute_was(attr) # :nodoc:
|
185
188
|
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
|
186
189
|
end
|
187
190
|
|
191
|
+
# Handles <tt>*_previously_changed?</tt> for +method_missing+.
|
192
|
+
def attribute_previously_changed?(attr) #:nodoc:
|
193
|
+
previous_changes_include?(attr)
|
194
|
+
end
|
195
|
+
|
188
196
|
# Restore all previous data of the provided attributes.
|
189
197
|
def restore_attributes(attributes = changed)
|
190
198
|
attributes.each { |attr| restore_attribute! attr }
|
@@ -192,38 +200,41 @@ module ActiveModel
|
|
192
200
|
|
193
201
|
private
|
194
202
|
|
203
|
+
# Returns +true+ if attr_name is changed, +false+ otherwise.
|
195
204
|
def changes_include?(attr_name)
|
196
205
|
attributes_changed_by_setter.include?(attr_name)
|
197
206
|
end
|
198
207
|
alias attribute_changed_by_setter? changes_include?
|
199
208
|
|
209
|
+
# Returns +true+ if attr_name were changed before the model was saved,
|
210
|
+
# +false+ otherwise.
|
211
|
+
def previous_changes_include?(attr_name)
|
212
|
+
previous_changes.include?(attr_name)
|
213
|
+
end
|
214
|
+
|
200
215
|
# Removes current changes and makes them accessible through +previous_changes+.
|
201
216
|
def changes_applied # :doc:
|
202
217
|
@previously_changed = changes
|
203
218
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
204
219
|
end
|
205
220
|
|
206
|
-
#
|
221
|
+
# Clears all dirty data: current changes and previous changes.
|
207
222
|
def clear_changes_information # :doc:
|
208
223
|
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
209
224
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
210
225
|
end
|
211
226
|
|
212
|
-
|
213
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
214
|
-
`#reset_changes` is deprecated and will be removed on Rails 5.
|
215
|
-
Please use `#clear_changes_information` instead.
|
216
|
-
MSG
|
217
|
-
|
218
|
-
clear_changes_information
|
219
|
-
end
|
220
|
-
|
221
|
-
# Handle <tt>*_change</tt> for +method_missing+.
|
227
|
+
# Handles <tt>*_change</tt> for +method_missing+.
|
222
228
|
def attribute_change(attr)
|
223
229
|
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
224
230
|
end
|
225
231
|
|
226
|
-
#
|
232
|
+
# Handles <tt>*_previous_change</tt> for +method_missing+.
|
233
|
+
def attribute_previous_change(attr)
|
234
|
+
previous_changes[attr] if attribute_previously_changed?(attr)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Handles <tt>*_will_change!</tt> for +method_missing+.
|
227
238
|
def attribute_will_change!(attr)
|
228
239
|
return if attribute_changed?(attr)
|
229
240
|
|
@@ -236,17 +247,7 @@ module ActiveModel
|
|
236
247
|
set_attribute_was(attr, value)
|
237
248
|
end
|
238
249
|
|
239
|
-
#
|
240
|
-
def reset_attribute!(attr)
|
241
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
242
|
-
`#reset_#{attr}!` is deprecated and will be removed on Rails 5.
|
243
|
-
Please use `#restore_#{attr}!` instead.
|
244
|
-
MSG
|
245
|
-
|
246
|
-
restore_attribute!(attr)
|
247
|
-
end
|
248
|
-
|
249
|
-
# Handle <tt>restore_*!</tt> for +method_missing+.
|
250
|
+
# Handles <tt>restore_*!</tt> for +method_missing+.
|
250
251
|
def restore_attribute!(attr)
|
251
252
|
if attribute_changed?(attr)
|
252
253
|
__send__("#{attr}=", changed_attributes[attr])
|
@@ -255,7 +256,7 @@ module ActiveModel
|
|
255
256
|
end
|
256
257
|
|
257
258
|
# This is necessary because `changed_attributes` might be overridden in
|
258
|
-
# other
|
259
|
+
# other implementations (e.g. in `ActiveRecord`)
|
259
260
|
alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
|
260
261
|
|
261
262
|
# Force an attribute to have a particular "before" value
|
data/lib/active_model/errors.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
1
|
require 'active_support/core_ext/array/conversions'
|
4
2
|
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'active_support/core_ext/object/deep_dup'
|
4
|
+
require 'active_support/core_ext/string/filters'
|
5
5
|
|
6
6
|
module ActiveModel
|
7
7
|
# == Active \Model \Errors
|
@@ -23,7 +23,7 @@ module ActiveModel
|
|
23
23
|
# attr_reader :errors
|
24
24
|
#
|
25
25
|
# def validate!
|
26
|
-
# errors.add(:name, "cannot be nil") if name.nil?
|
26
|
+
# errors.add(:name, :blank, message: "cannot be nil") if name.nil?
|
27
27
|
# end
|
28
28
|
#
|
29
29
|
# # The following methods are needed to be minimally implemented
|
@@ -32,20 +32,20 @@ module ActiveModel
|
|
32
32
|
# send(attr)
|
33
33
|
# end
|
34
34
|
#
|
35
|
-
# def
|
35
|
+
# def self.human_attribute_name(attr, options = {})
|
36
36
|
# attr
|
37
37
|
# end
|
38
38
|
#
|
39
|
-
# def
|
39
|
+
# def self.lookup_ancestors
|
40
40
|
# [self]
|
41
41
|
# end
|
42
42
|
# end
|
43
43
|
#
|
44
|
-
# The last three methods are required in your object for Errors to be
|
44
|
+
# The last three methods are required in your object for +Errors+ to be
|
45
45
|
# able to generate error messages correctly and also handle multiple
|
46
|
-
# languages. Of course, if you extend your object with ActiveModel::Translation
|
46
|
+
# languages. Of course, if you extend your object with <tt>ActiveModel::Translation</tt>
|
47
47
|
# you will not need to implement the last two. Likewise, using
|
48
|
-
# ActiveModel::Validations will handle the validation related methods
|
48
|
+
# <tt>ActiveModel::Validations</tt> will handle the validation related methods
|
49
49
|
# for you.
|
50
50
|
#
|
51
51
|
# The above allows you to do:
|
@@ -58,8 +58,9 @@ module ActiveModel
|
|
58
58
|
include Enumerable
|
59
59
|
|
60
60
|
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
|
61
|
+
MESSAGE_OPTIONS = [:message]
|
61
62
|
|
62
|
-
attr_reader :messages
|
63
|
+
attr_reader :messages, :details
|
63
64
|
|
64
65
|
# Pass in the instance of the object that is using the errors object.
|
65
66
|
#
|
@@ -70,14 +71,28 @@ module ActiveModel
|
|
70
71
|
# end
|
71
72
|
def initialize(base)
|
72
73
|
@base = base
|
73
|
-
@messages = {}
|
74
|
+
@messages = apply_default_array({})
|
75
|
+
@details = apply_default_array({})
|
74
76
|
end
|
75
77
|
|
76
78
|
def initialize_dup(other) # :nodoc:
|
77
79
|
@messages = other.messages.dup
|
80
|
+
@details = other.details.deep_dup
|
78
81
|
super
|
79
82
|
end
|
80
83
|
|
84
|
+
# Copies the errors from <tt>other</tt>.
|
85
|
+
#
|
86
|
+
# other - The ActiveModel::Errors instance.
|
87
|
+
#
|
88
|
+
# Examples
|
89
|
+
#
|
90
|
+
# person.errors.copy!(other)
|
91
|
+
def copy!(other) # :nodoc:
|
92
|
+
@messages = other.messages.dup
|
93
|
+
@details = other.details.dup
|
94
|
+
end
|
95
|
+
|
81
96
|
# Clear the error messages.
|
82
97
|
#
|
83
98
|
# person.errors.full_messages # => ["name cannot be nil"]
|
@@ -85,6 +100,7 @@ module ActiveModel
|
|
85
100
|
# person.errors.full_messages # => []
|
86
101
|
def clear
|
87
102
|
messages.clear
|
103
|
+
details.clear
|
88
104
|
end
|
89
105
|
|
90
106
|
# Returns +true+ if the error messages include an error for the given key
|
@@ -94,37 +110,48 @@ module ActiveModel
|
|
94
110
|
# person.errors.include?(:name) # => true
|
95
111
|
# person.errors.include?(:age) # => false
|
96
112
|
def include?(attribute)
|
97
|
-
messages[attribute].present?
|
113
|
+
messages.key?(attribute) && messages[attribute].present?
|
98
114
|
end
|
99
|
-
# aliases include?
|
100
115
|
alias :has_key? :include?
|
101
|
-
# aliases include?
|
102
116
|
alias :key? :include?
|
103
117
|
|
104
118
|
# Get messages for +key+.
|
105
119
|
#
|
106
120
|
# person.errors.messages # => {:name=>["cannot be nil"]}
|
107
121
|
# person.errors.get(:name) # => ["cannot be nil"]
|
108
|
-
# person.errors.get(:age) # =>
|
122
|
+
# person.errors.get(:age) # => []
|
109
123
|
def get(key)
|
124
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
125
|
+
ActiveModel::Errors#get is deprecated and will be removed in Rails 5.1.
|
126
|
+
|
127
|
+
To achieve the same use model.errors[:#{key}].
|
128
|
+
MESSAGE
|
129
|
+
|
110
130
|
messages[key]
|
111
131
|
end
|
112
132
|
|
113
133
|
# Set messages for +key+ to +value+.
|
114
134
|
#
|
115
|
-
# person.errors
|
135
|
+
# person.errors[:name] # => ["cannot be nil"]
|
116
136
|
# person.errors.set(:name, ["can't be nil"])
|
117
|
-
# person.errors
|
137
|
+
# person.errors[:name] # => ["can't be nil"]
|
118
138
|
def set(key, value)
|
139
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
140
|
+
ActiveModel::Errors#set is deprecated and will be removed in Rails 5.1.
|
141
|
+
|
142
|
+
Use model.errors.add(:#{key}, #{value.inspect}) instead.
|
143
|
+
MESSAGE
|
144
|
+
|
119
145
|
messages[key] = value
|
120
146
|
end
|
121
147
|
|
122
148
|
# Delete messages for +key+. Returns the deleted messages.
|
123
149
|
#
|
124
|
-
# person.errors
|
150
|
+
# person.errors[:name] # => ["cannot be nil"]
|
125
151
|
# person.errors.delete(:name) # => ["cannot be nil"]
|
126
|
-
# person.errors
|
152
|
+
# person.errors[:name] # => []
|
127
153
|
def delete(key)
|
154
|
+
details.delete(key)
|
128
155
|
messages.delete(key)
|
129
156
|
end
|
130
157
|
|
@@ -133,8 +160,17 @@ module ActiveModel
|
|
133
160
|
#
|
134
161
|
# person.errors[:name] # => ["cannot be nil"]
|
135
162
|
# person.errors['name'] # => ["cannot be nil"]
|
163
|
+
#
|
164
|
+
# Note that, if you try to get errors of an attribute which has
|
165
|
+
# no errors associated with it, this method will instantiate
|
166
|
+
# an empty error list for it and +keys+ will return an array
|
167
|
+
# of error keys which includes this attribute.
|
168
|
+
#
|
169
|
+
# person.errors.keys # => []
|
170
|
+
# person.errors[:name] # => []
|
171
|
+
# person.errors.keys # => [:name]
|
136
172
|
def [](attribute)
|
137
|
-
|
173
|
+
messages[attribute.to_sym]
|
138
174
|
end
|
139
175
|
|
140
176
|
# Adds to the supplied attribute the supplied error message.
|
@@ -142,38 +178,45 @@ module ActiveModel
|
|
142
178
|
# person.errors[:name] = "must be set"
|
143
179
|
# person.errors[:name] # => ['must be set']
|
144
180
|
def []=(attribute, error)
|
145
|
-
|
181
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
182
|
+
ActiveModel::Errors#[]= is deprecated and will be removed in Rails 5.1.
|
183
|
+
|
184
|
+
Use model.errors.add(:#{attribute}, #{error.inspect}) instead.
|
185
|
+
MESSAGE
|
186
|
+
|
187
|
+
messages[attribute.to_sym] << error
|
146
188
|
end
|
147
189
|
|
148
190
|
# Iterates through each error key, value pair in the error messages hash.
|
149
191
|
# Yields the attribute and the error for that attribute. If the attribute
|
150
192
|
# has more than one error message, yields once for each error message.
|
151
193
|
#
|
152
|
-
# person.errors.add(:name, "can't be blank")
|
194
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
153
195
|
# person.errors.each do |attribute, error|
|
154
196
|
# # Will yield :name and "can't be blank"
|
155
197
|
# end
|
156
198
|
#
|
157
|
-
# person.errors.add(:name, "must be specified")
|
199
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
158
200
|
# person.errors.each do |attribute, error|
|
159
201
|
# # Will yield :name and "can't be blank"
|
160
202
|
# # then yield :name and "must be specified"
|
161
203
|
# end
|
162
204
|
def each
|
163
205
|
messages.each_key do |attribute|
|
164
|
-
|
206
|
+
messages[attribute].each { |error| yield attribute, error }
|
165
207
|
end
|
166
208
|
end
|
167
209
|
|
168
210
|
# Returns the number of error messages.
|
169
211
|
#
|
170
|
-
# person.errors.add(:name, "can't be blank")
|
212
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
171
213
|
# person.errors.size # => 1
|
172
|
-
# person.errors.add(:name, "must be specified")
|
214
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
173
215
|
# person.errors.size # => 2
|
174
216
|
def size
|
175
217
|
values.flatten.size
|
176
218
|
end
|
219
|
+
alias :count :size
|
177
220
|
|
178
221
|
# Returns all message values.
|
179
222
|
#
|
@@ -191,40 +234,20 @@ module ActiveModel
|
|
191
234
|
messages.keys
|
192
235
|
end
|
193
236
|
|
194
|
-
# Returns an array of error messages, with the attribute name included.
|
195
|
-
#
|
196
|
-
# person.errors.add(:name, "can't be blank")
|
197
|
-
# person.errors.add(:name, "must be specified")
|
198
|
-
# person.errors.to_a # => ["name can't be blank", "name must be specified"]
|
199
|
-
def to_a
|
200
|
-
full_messages
|
201
|
-
end
|
202
|
-
|
203
|
-
# Returns the number of error messages.
|
204
|
-
#
|
205
|
-
# person.errors.add(:name, "can't be blank")
|
206
|
-
# person.errors.count # => 1
|
207
|
-
# person.errors.add(:name, "must be specified")
|
208
|
-
# person.errors.count # => 2
|
209
|
-
def count
|
210
|
-
to_a.size
|
211
|
-
end
|
212
|
-
|
213
237
|
# Returns +true+ if no errors are found, +false+ otherwise.
|
214
238
|
# If the error message is a string it can be empty.
|
215
239
|
#
|
216
240
|
# person.errors.full_messages # => ["name cannot be nil"]
|
217
241
|
# person.errors.empty? # => false
|
218
242
|
def empty?
|
219
|
-
|
243
|
+
size.zero?
|
220
244
|
end
|
221
|
-
|
222
|
-
alias_method :blank?, :empty?
|
245
|
+
alias :blank? :empty?
|
223
246
|
|
224
247
|
# Returns an xml formatted representation of the Errors hash.
|
225
248
|
#
|
226
|
-
# person.errors.add(:name, "can't be blank")
|
227
|
-
# person.errors.add(:name, "must be specified")
|
249
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
250
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
228
251
|
# person.errors.to_xml
|
229
252
|
# # =>
|
230
253
|
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
@@ -257,21 +280,24 @@ module ActiveModel
|
|
257
280
|
messages[attribute] = array.map { |message| full_message(attribute, message) }
|
258
281
|
end
|
259
282
|
else
|
260
|
-
self.messages
|
283
|
+
without_default_proc(self.messages)
|
261
284
|
end
|
262
285
|
end
|
263
286
|
|
264
|
-
# Adds +message+ to the error messages on +attribute+.
|
265
|
-
# can be added to the same +attribute+.
|
266
|
-
# <tt>:invalid</tt> is assumed.
|
287
|
+
# Adds +message+ to the error messages and used validator type to +details+ on +attribute+.
|
288
|
+
# More than one error can be added to the same +attribute+.
|
289
|
+
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
|
267
290
|
#
|
268
291
|
# person.errors.add(:name)
|
269
292
|
# # => ["is invalid"]
|
270
|
-
# person.errors.add(:name,
|
293
|
+
# person.errors.add(:name, :not_implemented, message: "must be implemented")
|
271
294
|
# # => ["is invalid", "must be implemented"]
|
272
295
|
#
|
273
296
|
# person.errors.messages
|
274
|
-
# # => {:name=>["
|
297
|
+
# # => {:name=>["is invalid", "must be implemented"]}
|
298
|
+
#
|
299
|
+
# person.errors.details
|
300
|
+
# # => {:name=>[{error: :not_implemented}, {error: :invalid}]}
|
275
301
|
#
|
276
302
|
# If +message+ is a symbol, it will be translated using the appropriate
|
277
303
|
# scope (see +generate_message+).
|
@@ -283,9 +309,9 @@ module ActiveModel
|
|
283
309
|
# ActiveModel::StrictValidationFailed instead of adding the error.
|
284
310
|
# <tt>:strict</tt> option can also be set to any other exception.
|
285
311
|
#
|
286
|
-
# person.errors.add(:name,
|
312
|
+
# person.errors.add(:name, :invalid, strict: true)
|
287
313
|
# # => ActiveModel::StrictValidationFailed: name is invalid
|
288
|
-
# person.errors.add(:name,
|
314
|
+
# person.errors.add(:name, :invalid, strict: NameIsInvalid)
|
289
315
|
# # => NameIsInvalid: name is invalid
|
290
316
|
#
|
291
317
|
# person.errors.messages # => {}
|
@@ -293,17 +319,23 @@ module ActiveModel
|
|
293
319
|
# +attribute+ should be set to <tt>:base</tt> if the error is not
|
294
320
|
# directly associated with a single attribute.
|
295
321
|
#
|
296
|
-
# person.errors.add(:base,
|
322
|
+
# person.errors.add(:base, :name_or_email_blank,
|
323
|
+
# message: "either name or email must be present")
|
297
324
|
# person.errors.messages
|
298
325
|
# # => {:base=>["either name or email must be present"]}
|
326
|
+
# person.errors.details
|
327
|
+
# # => {:base=>[{error: :name_or_email_blank}]}
|
299
328
|
def add(attribute, message = :invalid, options = {})
|
329
|
+
message = message.call if message.respond_to?(:call)
|
330
|
+
detail = normalize_detail(message, options)
|
300
331
|
message = normalize_message(attribute, message, options)
|
301
332
|
if exception = options[:strict]
|
302
333
|
exception = ActiveModel::StrictValidationFailed if exception == true
|
303
334
|
raise exception, full_message(attribute, message)
|
304
335
|
end
|
305
336
|
|
306
|
-
|
337
|
+
details[attribute.to_sym] << detail
|
338
|
+
messages[attribute.to_sym] << message
|
307
339
|
end
|
308
340
|
|
309
341
|
# Will add an error message to each of the attributes in +attributes+
|
@@ -313,6 +345,14 @@ module ActiveModel
|
|
313
345
|
# person.errors.messages
|
314
346
|
# # => {:name=>["can't be empty"]}
|
315
347
|
def add_on_empty(attributes, options = {})
|
348
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
349
|
+
ActiveModel::Errors#add_on_empty is deprecated and will be removed in Rails 5.1.
|
350
|
+
|
351
|
+
To achieve the same use:
|
352
|
+
|
353
|
+
errors.add(attribute, :empty, options) if value.nil? || value.empty?
|
354
|
+
MESSAGE
|
355
|
+
|
316
356
|
Array(attributes).each do |attribute|
|
317
357
|
value = @base.send(:read_attribute_for_validation, attribute)
|
318
358
|
is_empty = value.respond_to?(:empty?) ? value.empty? : false
|
@@ -327,6 +367,14 @@ module ActiveModel
|
|
327
367
|
# person.errors.messages
|
328
368
|
# # => {:name=>["can't be blank"]}
|
329
369
|
def add_on_blank(attributes, options = {})
|
370
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
371
|
+
ActiveModel::Errors#add_on_blank is deprecated and will be removed in Rails 5.1.
|
372
|
+
|
373
|
+
To achieve the same use:
|
374
|
+
|
375
|
+
errors.add(attribute, :blank, options) if value.blank?
|
376
|
+
MESSAGE
|
377
|
+
|
330
378
|
Array(attributes).each do |attribute|
|
331
379
|
value = @base.send(:read_attribute_for_validation, attribute)
|
332
380
|
add(attribute, :blank, options) if value.blank?
|
@@ -334,11 +382,23 @@ module ActiveModel
|
|
334
382
|
end
|
335
383
|
|
336
384
|
# Returns +true+ if an error on the attribute with the given message is
|
337
|
-
# present, +false+ otherwise. +message+ is treated the same as for +add+.
|
385
|
+
# present, or +false+ otherwise. +message+ is treated the same as for +add+.
|
338
386
|
#
|
339
387
|
# person.errors.add :name, :blank
|
340
|
-
# person.errors.added? :name, :blank
|
388
|
+
# person.errors.added? :name, :blank # => true
|
389
|
+
# person.errors.added? :name, "can't be blank" # => true
|
390
|
+
#
|
391
|
+
# If the error message requires an option, then it returns +true+ with
|
392
|
+
# the correct option, or +false+ with an incorrect or missing option.
|
393
|
+
#
|
394
|
+
# person.errors.add :name, :too_long, { count: 25 }
|
395
|
+
# person.errors.added? :name, :too_long, count: 25 # => true
|
396
|
+
# person.errors.added? :name, "is too long (maximum is 25 characters)" # => true
|
397
|
+
# person.errors.added? :name, :too_long, count: 24 # => false
|
398
|
+
# person.errors.added? :name, :too_long # => false
|
399
|
+
# person.errors.added? :name, "is too long" # => false
|
341
400
|
def added?(attribute, message = :invalid, options = {})
|
401
|
+
message = message.call if message.respond_to?(:call)
|
342
402
|
message = normalize_message(attribute, message, options)
|
343
403
|
self[attribute].include? message
|
344
404
|
end
|
@@ -356,6 +416,7 @@ module ActiveModel
|
|
356
416
|
def full_messages
|
357
417
|
map { |attribute, message| full_message(attribute, message) }
|
358
418
|
end
|
419
|
+
alias :to_a :full_messages
|
359
420
|
|
360
421
|
# Returns all the full error messages for a given attribute in an array.
|
361
422
|
#
|
@@ -368,7 +429,7 @@ module ActiveModel
|
|
368
429
|
# person.errors.full_messages_for(:name)
|
369
430
|
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
|
370
431
|
def full_messages_for(attribute)
|
371
|
-
|
432
|
+
messages[attribute].map { |message| full_message(attribute, message) }
|
372
433
|
end
|
373
434
|
|
374
435
|
# Returns a full message for a given attribute.
|
@@ -388,8 +449,8 @@ module ActiveModel
|
|
388
449
|
# Translates an error message in its default scope
|
389
450
|
# (<tt>activemodel.errors.messages</tt>).
|
390
451
|
#
|
391
|
-
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
|
392
|
-
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
|
452
|
+
# Error messages are first looked up in <tt>activemodel.errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
|
453
|
+
# if it's not there, it's looked up in <tt>activemodel.errors.models.MODEL.MESSAGE</tt> and if
|
393
454
|
# that is not there also, it returns the translation of the default message
|
394
455
|
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
|
395
456
|
# name, translated attribute name and the value are available for
|
@@ -421,7 +482,6 @@ module ActiveModel
|
|
421
482
|
defaults = []
|
422
483
|
end
|
423
484
|
|
424
|
-
defaults << options.delete(:message)
|
425
485
|
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
|
426
486
|
defaults << :"errors.attributes.#{attribute}.#{type}"
|
427
487
|
defaults << :"errors.messages.#{type}"
|
@@ -430,29 +490,61 @@ module ActiveModel
|
|
430
490
|
defaults.flatten!
|
431
491
|
|
432
492
|
key = defaults.shift
|
493
|
+
defaults = options.delete(:message) if options[:message]
|
433
494
|
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
|
434
495
|
|
435
496
|
options = {
|
436
497
|
default: defaults,
|
437
498
|
model: @base.model_name.human,
|
438
499
|
attribute: @base.class.human_attribute_name(attribute),
|
439
|
-
value: value
|
500
|
+
value: value,
|
501
|
+
object: @base
|
440
502
|
}.merge!(options)
|
441
503
|
|
442
504
|
I18n.translate(key, options)
|
443
505
|
end
|
444
506
|
|
507
|
+
def marshal_dump # :nodoc:
|
508
|
+
[@base, without_default_proc(@messages), without_default_proc(@details)]
|
509
|
+
end
|
510
|
+
|
511
|
+
def marshal_load(array) # :nodoc:
|
512
|
+
@base, @messages, @details = array
|
513
|
+
apply_default_array(@messages)
|
514
|
+
apply_default_array(@details)
|
515
|
+
end
|
516
|
+
|
517
|
+
def init_with(coder) # :nodoc:
|
518
|
+
coder.map.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
519
|
+
@details ||= {}
|
520
|
+
apply_default_array(@messages)
|
521
|
+
apply_default_array(@details)
|
522
|
+
end
|
523
|
+
|
445
524
|
private
|
446
525
|
def normalize_message(attribute, message, options)
|
447
526
|
case message
|
448
527
|
when Symbol
|
449
528
|
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
|
450
|
-
when Proc
|
451
|
-
message.call
|
452
529
|
else
|
453
530
|
message
|
454
531
|
end
|
455
532
|
end
|
533
|
+
|
534
|
+
def normalize_detail(message, options)
|
535
|
+
{ error: message }.merge(options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS))
|
536
|
+
end
|
537
|
+
|
538
|
+
def without_default_proc(hash)
|
539
|
+
hash.dup.tap do |new_h|
|
540
|
+
new_h.default_proc = nil
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def apply_default_array(hash)
|
545
|
+
hash.default_proc = proc { |h, key| h[key] = [] }
|
546
|
+
hash
|
547
|
+
end
|
456
548
|
end
|
457
549
|
|
458
550
|
# Raised when a validation cannot be corrected by end users and are considered
|
@@ -472,4 +564,19 @@ module ActiveModel
|
|
472
564
|
# # => ActiveModel::StrictValidationFailed: Name can't be blank
|
473
565
|
class StrictValidationFailed < StandardError
|
474
566
|
end
|
567
|
+
|
568
|
+
# Raised when attribute values are out of range.
|
569
|
+
class RangeError < ::RangeError
|
570
|
+
end
|
571
|
+
|
572
|
+
# Raised when unknown attributes are supplied via mass assignment.
|
573
|
+
class UnknownAttributeError < NoMethodError
|
574
|
+
attr_reader :record, :attribute
|
575
|
+
|
576
|
+
def initialize(record, attribute)
|
577
|
+
@record = record
|
578
|
+
@attribute = attribute
|
579
|
+
super("unknown attribute '#{attribute}' for #{@record.class}.")
|
580
|
+
end
|
581
|
+
end
|
475
582
|
end
|