activemodel 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 activemodel might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +84 -93
- data/MIT-LICENSE +1 -1
- data/README.rdoc +8 -16
- data/lib/active_model.rb +3 -2
- 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 +3 -3
- data/lib/active_model/dirty.rb +34 -35
- data/lib/active_model/errors.rb +117 -63
- 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/serializers/json.rb +1 -1
- data/lib/active_model/type.rb +59 -0
- 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 +50 -0
- data/lib/active_model/type/date_time.rb +44 -0
- data/lib/active_model/type/decimal.rb +52 -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.rb +4 -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/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 +19 -0
- data/lib/active_model/type/text.rb +11 -0
- data/lib/active_model/type/time.rb +46 -0
- data/lib/active_model/type/unsigned_integer.rb +15 -0
- data/lib/active_model/type/value.rb +112 -0
- data/lib/active_model/validations.rb +35 -3
- data/lib/active_model/validations/absence.rb +1 -1
- data/lib/active_model/validations/acceptance.rb +61 -9
- data/lib/active_model/validations/callbacks.rb +3 -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 +48 -17
- data/lib/active_model/validations/numericality.rb +12 -13
- data/lib/active_model/validations/validates.rb +1 -1
- data/lib/active_model/validations/with.rb +0 -10
- data/lib/active_model/validator.rb +6 -2
- data/lib/active_model/version.rb +1 -1
- metadata +34 -9
- data/lib/active_model/serializers/xml.rb +0 -238
@@ -6,7 +6,7 @@ module ActiveModel
|
|
6
6
|
# Provides an interface for any class to have Active Record like callbacks.
|
7
7
|
#
|
8
8
|
# Like the Active Record methods, the callback chain is aborted as soon as
|
9
|
-
# one of the methods
|
9
|
+
# one of the methods throws +:abort+.
|
10
10
|
#
|
11
11
|
# First, extend ActiveModel::Callbacks from the class you are creating:
|
12
12
|
#
|
@@ -49,7 +49,7 @@ module ActiveModel
|
|
49
49
|
# puts 'block successfully called.'
|
50
50
|
# end
|
51
51
|
#
|
52
|
-
# You can choose
|
52
|
+
# You can choose to have only specific callbacks by passing a hash to the
|
53
53
|
# +define_model_callbacks+ method.
|
54
54
|
#
|
55
55
|
# define_model_callbacks :create, only: [:after, :before]
|
@@ -103,7 +103,7 @@ module ActiveModel
|
|
103
103
|
def define_model_callbacks(*callbacks)
|
104
104
|
options = callbacks.extract_options!
|
105
105
|
options = {
|
106
|
-
terminator:
|
106
|
+
terminator: deprecated_false_terminator,
|
107
107
|
skip_after_callbacks_if_terminated: true,
|
108
108
|
scope: [:kind, :name],
|
109
109
|
only: [:before, :around, :after]
|
@@ -22,7 +22,7 @@ module ActiveModel
|
|
22
22
|
module Conversion
|
23
23
|
extend ActiveSupport::Concern
|
24
24
|
|
25
|
-
# If your object is already designed to implement all of the Active Model
|
25
|
+
# If your object is already designed to implement all of the \Active \Model
|
26
26
|
# you can use the default <tt>:to_model</tt> implementation, which simply
|
27
27
|
# returns +self+.
|
28
28
|
#
|
@@ -33,9 +33,9 @@ module ActiveModel
|
|
33
33
|
# person = Person.new
|
34
34
|
# person.to_model == person # => true
|
35
35
|
#
|
36
|
-
# If your model does not act like an Active Model object, then you should
|
36
|
+
# If your model does not act like an \Active \Model object, then you should
|
37
37
|
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
|
38
|
-
# your object with Active Model compliant methods.
|
38
|
+
# your object with \Active \Model compliant methods.
|
39
39
|
def to_model
|
40
40
|
self
|
41
41
|
end
|
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"]
|
@@ -120,11 +121,11 @@ module ActiveModel
|
|
120
121
|
|
121
122
|
included do
|
122
123
|
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
|
123
|
-
|
124
|
+
attribute_method_suffix '_previously_changed?', '_previous_change'
|
124
125
|
attribute_method_affix prefix: 'restore_', suffix: '!'
|
125
126
|
end
|
126
127
|
|
127
|
-
# Returns +true+ if any
|
128
|
+
# Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise.
|
128
129
|
#
|
129
130
|
# person.changed? # => false
|
130
131
|
# person.name = 'bob'
|
@@ -172,7 +173,7 @@ module ActiveModel
|
|
172
173
|
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
|
173
174
|
end
|
174
175
|
|
175
|
-
#
|
176
|
+
# Handles <tt>*_changed?</tt> for +method_missing+.
|
176
177
|
def attribute_changed?(attr, options = {}) #:nodoc:
|
177
178
|
result = changes_include?(attr)
|
178
179
|
result &&= options[:to] == __send__(attr) if options.key?(:to)
|
@@ -180,11 +181,16 @@ module ActiveModel
|
|
180
181
|
result
|
181
182
|
end
|
182
183
|
|
183
|
-
#
|
184
|
+
# Handles <tt>*_was</tt> for +method_missing+.
|
184
185
|
def attribute_was(attr) # :nodoc:
|
185
186
|
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
|
186
187
|
end
|
187
188
|
|
189
|
+
# Handles <tt>*_previously_changed?</tt> for +method_missing+.
|
190
|
+
def attribute_previously_changed?(attr, options = {}) #:nodoc:
|
191
|
+
previous_changes_include?(attr)
|
192
|
+
end
|
193
|
+
|
188
194
|
# Restore all previous data of the provided attributes.
|
189
195
|
def restore_attributes(attributes = changed)
|
190
196
|
attributes.each { |attr| restore_attribute! attr }
|
@@ -192,38 +198,41 @@ module ActiveModel
|
|
192
198
|
|
193
199
|
private
|
194
200
|
|
201
|
+
# Returns +true+ if attr_name is changed, +false+ otherwise.
|
195
202
|
def changes_include?(attr_name)
|
196
203
|
attributes_changed_by_setter.include?(attr_name)
|
197
204
|
end
|
198
205
|
alias attribute_changed_by_setter? changes_include?
|
199
206
|
|
207
|
+
# Returns +true+ if attr_name were changed before the model was saved,
|
208
|
+
# +false+ otherwise.
|
209
|
+
def previous_changes_include?(attr_name)
|
210
|
+
previous_changes.include?(attr_name)
|
211
|
+
end
|
212
|
+
|
200
213
|
# Removes current changes and makes them accessible through +previous_changes+.
|
201
214
|
def changes_applied # :doc:
|
202
215
|
@previously_changed = changes
|
203
216
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
204
217
|
end
|
205
218
|
|
206
|
-
#
|
219
|
+
# Clears all dirty data: current changes and previous changes.
|
207
220
|
def clear_changes_information # :doc:
|
208
221
|
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
209
222
|
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
210
223
|
end
|
211
224
|
|
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+.
|
225
|
+
# Handles <tt>*_change</tt> for +method_missing+.
|
222
226
|
def attribute_change(attr)
|
223
227
|
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
224
228
|
end
|
225
229
|
|
226
|
-
#
|
230
|
+
# Handles <tt>*_previous_change</tt> for +method_missing+.
|
231
|
+
def attribute_previous_change(attr)
|
232
|
+
previous_changes[attr] if attribute_previously_changed?(attr)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Handles <tt>*_will_change!</tt> for +method_missing+.
|
227
236
|
def attribute_will_change!(attr)
|
228
237
|
return if attribute_changed?(attr)
|
229
238
|
|
@@ -236,17 +245,7 @@ module ActiveModel
|
|
236
245
|
set_attribute_was(attr, value)
|
237
246
|
end
|
238
247
|
|
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+.
|
248
|
+
# Handles <tt>restore_*!</tt> for +method_missing+.
|
250
249
|
def restore_attribute!(attr)
|
251
250
|
if attribute_changed?(attr)
|
252
251
|
__send__("#{attr}=", changed_attributes[attr])
|
@@ -255,7 +254,7 @@ module ActiveModel
|
|
255
254
|
end
|
256
255
|
|
257
256
|
# This is necessary because `changed_attributes` might be overridden in
|
258
|
-
# other
|
257
|
+
# other implementations (e.g. in `ActiveRecord`)
|
259
258
|
alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
|
260
259
|
|
261
260
|
# 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 = Hash.new { |messages, attribute| messages[attribute] = [] }
|
75
|
+
@details = Hash.new { |details, attribute| details[attribute] = [] }
|
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
|
@@ -96,35 +112,46 @@ module ActiveModel
|
|
96
112
|
def include?(attribute)
|
97
113
|
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
|
|
@@ -134,7 +161,7 @@ module ActiveModel
|
|
134
161
|
# person.errors[:name] # => ["cannot be nil"]
|
135
162
|
# person.errors['name'] # => ["cannot be nil"]
|
136
163
|
def [](attribute)
|
137
|
-
|
164
|
+
messages[attribute.to_sym]
|
138
165
|
end
|
139
166
|
|
140
167
|
# Adds to the supplied attribute the supplied error message.
|
@@ -142,38 +169,45 @@ module ActiveModel
|
|
142
169
|
# person.errors[:name] = "must be set"
|
143
170
|
# person.errors[:name] # => ['must be set']
|
144
171
|
def []=(attribute, error)
|
145
|
-
|
172
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
173
|
+
ActiveModel::Errors#[]= is deprecated and will be removed in Rails 5.1.
|
174
|
+
|
175
|
+
Use model.errors.add(:#{attribute}, #{error.inspect}) instead.
|
176
|
+
MESSAGE
|
177
|
+
|
178
|
+
messages[attribute.to_sym] << error
|
146
179
|
end
|
147
180
|
|
148
181
|
# Iterates through each error key, value pair in the error messages hash.
|
149
182
|
# Yields the attribute and the error for that attribute. If the attribute
|
150
183
|
# has more than one error message, yields once for each error message.
|
151
184
|
#
|
152
|
-
# person.errors.add(:name, "can't be blank")
|
185
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
153
186
|
# person.errors.each do |attribute, error|
|
154
187
|
# # Will yield :name and "can't be blank"
|
155
188
|
# end
|
156
189
|
#
|
157
|
-
# person.errors.add(:name, "must be specified")
|
190
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
158
191
|
# person.errors.each do |attribute, error|
|
159
192
|
# # Will yield :name and "can't be blank"
|
160
193
|
# # then yield :name and "must be specified"
|
161
194
|
# end
|
162
195
|
def each
|
163
196
|
messages.each_key do |attribute|
|
164
|
-
|
197
|
+
messages[attribute].each { |error| yield attribute, error }
|
165
198
|
end
|
166
199
|
end
|
167
200
|
|
168
201
|
# Returns the number of error messages.
|
169
202
|
#
|
170
|
-
# person.errors.add(:name, "can't be blank")
|
203
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
171
204
|
# person.errors.size # => 1
|
172
|
-
# person.errors.add(:name, "must be specified")
|
205
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
173
206
|
# person.errors.size # => 2
|
174
207
|
def size
|
175
208
|
values.flatten.size
|
176
209
|
end
|
210
|
+
alias :count :size
|
177
211
|
|
178
212
|
# Returns all message values.
|
179
213
|
#
|
@@ -191,40 +225,20 @@ module ActiveModel
|
|
191
225
|
messages.keys
|
192
226
|
end
|
193
227
|
|
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
228
|
# Returns +true+ if no errors are found, +false+ otherwise.
|
214
229
|
# If the error message is a string it can be empty.
|
215
230
|
#
|
216
231
|
# person.errors.full_messages # => ["name cannot be nil"]
|
217
232
|
# person.errors.empty? # => false
|
218
233
|
def empty?
|
219
|
-
|
234
|
+
size.zero?
|
220
235
|
end
|
221
|
-
|
222
|
-
alias_method :blank?, :empty?
|
236
|
+
alias :blank? :empty?
|
223
237
|
|
224
238
|
# Returns an xml formatted representation of the Errors hash.
|
225
239
|
#
|
226
|
-
# person.errors.add(:name, "can't be blank")
|
227
|
-
# person.errors.add(:name, "must be specified")
|
240
|
+
# person.errors.add(:name, :blank, message: "can't be blank")
|
241
|
+
# person.errors.add(:name, :not_specified, message: "must be specified")
|
228
242
|
# person.errors.to_xml
|
229
243
|
# # =>
|
230
244
|
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
@@ -261,17 +275,20 @@ module ActiveModel
|
|
261
275
|
end
|
262
276
|
end
|
263
277
|
|
264
|
-
# Adds +message+ to the error messages on +attribute+.
|
265
|
-
# can be added to the same +attribute+.
|
266
|
-
# <tt>:invalid</tt> is assumed.
|
278
|
+
# Adds +message+ to the error messages and used validator type to +details+ on +attribute+.
|
279
|
+
# More than one error can be added to the same +attribute+.
|
280
|
+
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
|
267
281
|
#
|
268
282
|
# person.errors.add(:name)
|
269
283
|
# # => ["is invalid"]
|
270
|
-
# person.errors.add(:name,
|
284
|
+
# person.errors.add(:name, :not_implemented, message: "must be implemented")
|
271
285
|
# # => ["is invalid", "must be implemented"]
|
272
286
|
#
|
273
287
|
# person.errors.messages
|
274
|
-
# # => {:name=>["
|
288
|
+
# # => {:name=>["is invalid", "must be implemented"]}
|
289
|
+
#
|
290
|
+
# person.errors.details
|
291
|
+
# # => {:name=>[{error: :not_implemented}, {error: :invalid}]}
|
275
292
|
#
|
276
293
|
# If +message+ is a symbol, it will be translated using the appropriate
|
277
294
|
# scope (see +generate_message+).
|
@@ -283,9 +300,9 @@ module ActiveModel
|
|
283
300
|
# ActiveModel::StrictValidationFailed instead of adding the error.
|
284
301
|
# <tt>:strict</tt> option can also be set to any other exception.
|
285
302
|
#
|
286
|
-
# person.errors.add(:name,
|
303
|
+
# person.errors.add(:name, :invalid, strict: true)
|
287
304
|
# # => ActiveModel::StrictValidationFailed: name is invalid
|
288
|
-
# person.errors.add(:name,
|
305
|
+
# person.errors.add(:name, :invalid, strict: NameIsInvalid)
|
289
306
|
# # => NameIsInvalid: name is invalid
|
290
307
|
#
|
291
308
|
# person.errors.messages # => {}
|
@@ -293,17 +310,23 @@ module ActiveModel
|
|
293
310
|
# +attribute+ should be set to <tt>:base</tt> if the error is not
|
294
311
|
# directly associated with a single attribute.
|
295
312
|
#
|
296
|
-
# person.errors.add(:base,
|
313
|
+
# person.errors.add(:base, :name_or_email_blank,
|
314
|
+
# message: "either name or email must be present")
|
297
315
|
# person.errors.messages
|
298
316
|
# # => {:base=>["either name or email must be present"]}
|
317
|
+
# person.errors.details
|
318
|
+
# # => {:base=>[{error: :name_or_email_blank}]}
|
299
319
|
def add(attribute, message = :invalid, options = {})
|
320
|
+
message = message.call if message.respond_to?(:call)
|
321
|
+
detail = normalize_detail(attribute, message, options)
|
300
322
|
message = normalize_message(attribute, message, options)
|
301
323
|
if exception = options[:strict]
|
302
324
|
exception = ActiveModel::StrictValidationFailed if exception == true
|
303
325
|
raise exception, full_message(attribute, message)
|
304
326
|
end
|
305
327
|
|
306
|
-
|
328
|
+
details[attribute.to_sym] << detail
|
329
|
+
messages[attribute.to_sym] << message
|
307
330
|
end
|
308
331
|
|
309
332
|
# Will add an error message to each of the attributes in +attributes+
|
@@ -313,6 +336,14 @@ module ActiveModel
|
|
313
336
|
# person.errors.messages
|
314
337
|
# # => {:name=>["can't be empty"]}
|
315
338
|
def add_on_empty(attributes, options = {})
|
339
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
340
|
+
ActiveModel::Errors#add_on_empty is deprecated and will be removed in Rails 5.1
|
341
|
+
|
342
|
+
To achieve the same use:
|
343
|
+
|
344
|
+
errors.add(attribute, :empty, options) if value.nil? || value.empty?
|
345
|
+
MESSAGE
|
346
|
+
|
316
347
|
Array(attributes).each do |attribute|
|
317
348
|
value = @base.send(:read_attribute_for_validation, attribute)
|
318
349
|
is_empty = value.respond_to?(:empty?) ? value.empty? : false
|
@@ -327,6 +358,14 @@ module ActiveModel
|
|
327
358
|
# person.errors.messages
|
328
359
|
# # => {:name=>["can't be blank"]}
|
329
360
|
def add_on_blank(attributes, options = {})
|
361
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
362
|
+
ActiveModel::Errors#add_on_blank is deprecated and will be removed in Rails 5.1
|
363
|
+
|
364
|
+
To achieve the same use:
|
365
|
+
|
366
|
+
errors.add(attribute, :empty, options) if value.blank?
|
367
|
+
MESSAGE
|
368
|
+
|
330
369
|
Array(attributes).each do |attribute|
|
331
370
|
value = @base.send(:read_attribute_for_validation, attribute)
|
332
371
|
add(attribute, :blank, options) if value.blank?
|
@@ -339,6 +378,7 @@ module ActiveModel
|
|
339
378
|
# person.errors.add :name, :blank
|
340
379
|
# person.errors.added? :name, :blank # => true
|
341
380
|
def added?(attribute, message = :invalid, options = {})
|
381
|
+
message = message.call if message.respond_to?(:call)
|
342
382
|
message = normalize_message(attribute, message, options)
|
343
383
|
self[attribute].include? message
|
344
384
|
end
|
@@ -356,6 +396,7 @@ module ActiveModel
|
|
356
396
|
def full_messages
|
357
397
|
map { |attribute, message| full_message(attribute, message) }
|
358
398
|
end
|
399
|
+
alias :to_a :full_messages
|
359
400
|
|
360
401
|
# Returns all the full error messages for a given attribute in an array.
|
361
402
|
#
|
@@ -368,7 +409,7 @@ module ActiveModel
|
|
368
409
|
# person.errors.full_messages_for(:name)
|
369
410
|
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
|
370
411
|
def full_messages_for(attribute)
|
371
|
-
|
412
|
+
messages[attribute].map { |message| full_message(attribute, message) }
|
372
413
|
end
|
373
414
|
|
374
415
|
# Returns a full message for a given attribute.
|
@@ -388,8 +429,8 @@ module ActiveModel
|
|
388
429
|
# Translates an error message in its default scope
|
389
430
|
# (<tt>activemodel.errors.messages</tt>).
|
390
431
|
#
|
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
|
432
|
+
# Error messages are first looked up in <tt>activemodel.errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
|
433
|
+
# if it's not there, it's looked up in <tt>activemodel.errors.models.MODEL.MESSAGE</tt> and if
|
393
434
|
# that is not there also, it returns the translation of the default message
|
394
435
|
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
|
395
436
|
# name, translated attribute name and the value are available for
|
@@ -421,7 +462,6 @@ module ActiveModel
|
|
421
462
|
defaults = []
|
422
463
|
end
|
423
464
|
|
424
|
-
defaults << options.delete(:message)
|
425
465
|
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
|
426
466
|
defaults << :"errors.attributes.#{attribute}.#{type}"
|
427
467
|
defaults << :"errors.messages.#{type}"
|
@@ -430,6 +470,7 @@ module ActiveModel
|
|
430
470
|
defaults.flatten!
|
431
471
|
|
432
472
|
key = defaults.shift
|
473
|
+
defaults = options.delete(:message) if options[:message]
|
433
474
|
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
|
434
475
|
|
435
476
|
options = {
|
@@ -447,12 +488,14 @@ module ActiveModel
|
|
447
488
|
case message
|
448
489
|
when Symbol
|
449
490
|
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
|
450
|
-
when Proc
|
451
|
-
message.call
|
452
491
|
else
|
453
492
|
message
|
454
493
|
end
|
455
494
|
end
|
495
|
+
|
496
|
+
def normalize_detail(attribute, message, options)
|
497
|
+
{ error: message }.merge(options.except(*CALLBACKS_OPTIONS + MESSAGE_OPTIONS))
|
498
|
+
end
|
456
499
|
end
|
457
500
|
|
458
501
|
# Raised when a validation cannot be corrected by end users and are considered
|
@@ -472,4 +515,15 @@ module ActiveModel
|
|
472
515
|
# # => ActiveModel::StrictValidationFailed: Name can't be blank
|
473
516
|
class StrictValidationFailed < StandardError
|
474
517
|
end
|
518
|
+
|
519
|
+
# Raised when unknown attributes are supplied via mass assignment.
|
520
|
+
class UnknownAttributeError < NoMethodError
|
521
|
+
attr_reader :record, :attribute
|
522
|
+
|
523
|
+
def initialize(record, attribute)
|
524
|
+
@record = record
|
525
|
+
@attribute = attribute
|
526
|
+
super("unknown attribute '#{attribute}' for #{@record.class}.")
|
527
|
+
end
|
528
|
+
end
|
475
529
|
end
|