activemodel 3.0.0.beta4 → 3.0.pre
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.
- data/CHANGELOG +1 -39
- data/MIT-LICENSE +1 -1
- data/README +16 -200
- data/lib/active_model.rb +19 -28
- data/lib/active_model/attribute_methods.rb +27 -142
- data/lib/active_model/conversion.rb +1 -37
- data/lib/active_model/dirty.rb +12 -51
- data/lib/active_model/errors.rb +22 -146
- data/lib/active_model/lint.rb +14 -48
- data/lib/active_model/locale/en.yml +23 -26
- data/lib/active_model/naming.rb +5 -41
- data/lib/active_model/observing.rb +16 -35
- data/lib/active_model/serialization.rb +0 -57
- data/lib/active_model/serializers/json.rb +8 -13
- data/lib/active_model/serializers/xml.rb +123 -63
- data/lib/active_model/state_machine.rb +70 -0
- data/lib/active_model/state_machine/event.rb +62 -0
- data/lib/active_model/state_machine/machine.rb +75 -0
- data/lib/active_model/state_machine/state.rb +47 -0
- data/lib/active_model/state_machine/state_transition.rb +40 -0
- data/lib/active_model/test_case.rb +2 -0
- data/lib/active_model/validations.rb +62 -125
- data/lib/active_model/validations/acceptance.rb +18 -23
- data/lib/active_model/validations/confirmation.rb +10 -14
- data/lib/active_model/validations/exclusion.rb +13 -15
- data/lib/active_model/validations/format.rb +24 -26
- data/lib/active_model/validations/inclusion.rb +13 -15
- data/lib/active_model/validations/length.rb +65 -61
- data/lib/active_model/validations/numericality.rb +58 -76
- data/lib/active_model/validations/presence.rb +8 -8
- data/lib/active_model/validations/with.rb +22 -90
- data/lib/active_model/validations_repair_helper.rb +35 -0
- data/lib/active_model/version.rb +2 -3
- metadata +19 -63
- data/lib/active_model/callbacks.rb +0 -134
- data/lib/active_model/railtie.rb +0 -2
- data/lib/active_model/translation.rb +0 -60
- data/lib/active_model/validations/validates.rb +0 -108
- data/lib/active_model/validator.rb +0 -183
@@ -1,27 +1,6 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
def initialize(options)
|
5
|
-
super(options.reverse_merge(:allow_nil => true, :accept => "1"))
|
6
|
-
end
|
7
|
-
|
8
|
-
def validate_each(record, attribute, value)
|
9
|
-
unless value == options[:accept]
|
10
|
-
record.errors.add(attribute, :accepted, :default => options[:message])
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def setup(klass)
|
15
|
-
# Note: instance_methods.map(&:to_s) is important for 1.9 compatibility
|
16
|
-
# as instance_methods returns symbols unlike 1.8 which returns strings.
|
17
|
-
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
|
18
|
-
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
|
19
|
-
klass.send(:attr_reader, *attr_readers)
|
20
|
-
klass.send(:attr_writer, *attr_writers)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
25
4
|
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
|
26
5
|
#
|
27
6
|
# class Person < ActiveRecord::Base
|
@@ -46,7 +25,23 @@ module ActiveModel
|
|
46
25
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
47
26
|
# method, proc or string should return or evaluate to a true or false value.
|
48
27
|
def validates_acceptance_of(*attr_names)
|
49
|
-
|
28
|
+
configuration = { :allow_nil => true, :accept => "1" }
|
29
|
+
configuration.update(attr_names.extract_options!)
|
30
|
+
|
31
|
+
db_cols = begin
|
32
|
+
column_names
|
33
|
+
rescue Exception # To ignore both statement and connection errors
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
37
|
+
names = attr_names.reject { |name| db_cols.include?(name.to_s) }
|
38
|
+
attr_accessor(*names)
|
39
|
+
|
40
|
+
validates_each(attr_names,configuration) do |record, attr_name, value|
|
41
|
+
unless value == configuration[:accept]
|
42
|
+
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
43
|
+
end
|
44
|
+
end
|
50
45
|
end
|
51
46
|
end
|
52
47
|
end
|
@@ -1,18 +1,6 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
def validate_each(record, attribute, value)
|
5
|
-
confirmed = record.send(:"#{attribute}_confirmation")
|
6
|
-
return if confirmed.nil? || value == confirmed
|
7
|
-
record.errors.add(attribute, :confirmation, :default => options[:message])
|
8
|
-
end
|
9
|
-
|
10
|
-
def setup(klass)
|
11
|
-
klass.send(:attr_accessor, *attributes.map { |attribute| :"#{attribute}_confirmation" })
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
16
4
|
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
|
17
5
|
#
|
18
6
|
# Model:
|
@@ -42,7 +30,15 @@ module ActiveModel
|
|
42
30
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
43
31
|
# method, proc or string should return or evaluate to a true or false value.
|
44
32
|
def validates_confirmation_of(*attr_names)
|
45
|
-
|
33
|
+
configuration = attr_names.extract_options!
|
34
|
+
|
35
|
+
attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" }))
|
36
|
+
|
37
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
38
|
+
unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
|
39
|
+
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
40
|
+
end
|
41
|
+
end
|
46
42
|
end
|
47
43
|
end
|
48
44
|
end
|
@@ -1,24 +1,12 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
def check_validity!
|
5
|
-
raise ArgumentError, "An object with the method include? is required must be supplied as the " <<
|
6
|
-
":in option of the configuration hash" unless options[:in].respond_to?(:include?)
|
7
|
-
end
|
8
|
-
|
9
|
-
def validate_each(record, attribute, value)
|
10
|
-
return unless options[:in].include?(value)
|
11
|
-
record.errors.add(attribute, :exclusion, :default => options[:message], :value => value)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
16
4
|
# Validates that the value of the specified attribute is not in a particular enumerable object.
|
17
5
|
#
|
18
6
|
# class Person < ActiveRecord::Base
|
19
7
|
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
|
20
8
|
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
|
21
|
-
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension
|
9
|
+
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed"
|
22
10
|
# end
|
23
11
|
#
|
24
12
|
# Configuration options:
|
@@ -33,7 +21,17 @@ module ActiveModel
|
|
33
21
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
34
22
|
# method, proc or string should return or evaluate to a true or false value.
|
35
23
|
def validates_exclusion_of(*attr_names)
|
36
|
-
|
24
|
+
configuration = attr_names.extract_options!
|
25
|
+
|
26
|
+
enum = configuration[:in] || configuration[:within]
|
27
|
+
|
28
|
+
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
|
29
|
+
|
30
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
31
|
+
if enum.include?(value)
|
32
|
+
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
33
|
+
end
|
34
|
+
end
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -1,30 +1,6 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
def validate_each(record, attribute, value)
|
5
|
-
if options[:with] && value.to_s !~ options[:with]
|
6
|
-
record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
|
7
|
-
elsif options[:without] && value.to_s =~ options[:without]
|
8
|
-
record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def check_validity!
|
13
|
-
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
|
14
|
-
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
|
15
|
-
end
|
16
|
-
|
17
|
-
if options[:with] && !options[:with].is_a?(Regexp)
|
18
|
-
raise ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash"
|
19
|
-
end
|
20
|
-
|
21
|
-
if options[:without] && !options[:without].is_a?(Regexp)
|
22
|
-
raise ArgumentError, "A regular expression must be supplied as the :without option of the configuration hash"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
28
4
|
# Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
|
29
5
|
# You can require that the attribute matches the regular expression:
|
30
6
|
#
|
@@ -57,7 +33,29 @@ module ActiveModel
|
|
57
33
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
58
34
|
# method, proc or string should return or evaluate to a true or false value.
|
59
35
|
def validates_format_of(*attr_names)
|
60
|
-
|
36
|
+
configuration = attr_names.extract_options!
|
37
|
+
|
38
|
+
unless configuration.include?(:with) ^ configuration.include?(:without) # ^ == xor, or "exclusive or"
|
39
|
+
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
|
40
|
+
end
|
41
|
+
|
42
|
+
if configuration[:with] && !configuration[:with].is_a?(Regexp)
|
43
|
+
raise ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash"
|
44
|
+
end
|
45
|
+
|
46
|
+
if configuration[:without] && !configuration[:without].is_a?(Regexp)
|
47
|
+
raise ArgumentError, "A regular expression must be supplied as the :without option of the configuration hash"
|
48
|
+
end
|
49
|
+
|
50
|
+
if configuration[:with]
|
51
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
52
|
+
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s !~ configuration[:with]
|
53
|
+
end
|
54
|
+
elsif configuration[:without]
|
55
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
56
|
+
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) if value.to_s =~ configuration[:without]
|
57
|
+
end
|
58
|
+
end
|
61
59
|
end
|
62
60
|
end
|
63
61
|
end
|
@@ -1,24 +1,12 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
def check_validity!
|
5
|
-
raise ArgumentError, "An object with the method include? is required must be supplied as the " <<
|
6
|
-
":in option of the configuration hash" unless options[:in].respond_to?(:include?)
|
7
|
-
end
|
8
|
-
|
9
|
-
def validate_each(record, attribute, value)
|
10
|
-
return if options[:in].include?(value)
|
11
|
-
record.errors.add(attribute, :inclusion, :default => options[:message], :value => value)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
16
4
|
# Validates whether the value of the specified attribute is available in a particular enumerable object.
|
17
5
|
#
|
18
6
|
# class Person < ActiveRecord::Base
|
19
7
|
# validates_inclusion_of :gender, :in => %w( m f )
|
20
8
|
# validates_inclusion_of :age, :in => 0..99
|
21
|
-
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension
|
9
|
+
# validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list"
|
22
10
|
# end
|
23
11
|
#
|
24
12
|
# Configuration options:
|
@@ -33,7 +21,17 @@ module ActiveModel
|
|
33
21
|
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
34
22
|
# method, proc or string should return or evaluate to a true or false value.
|
35
23
|
def validates_inclusion_of(*attr_names)
|
36
|
-
|
24
|
+
configuration = attr_names.extract_options!
|
25
|
+
|
26
|
+
enum = configuration[:in] || configuration[:within]
|
27
|
+
|
28
|
+
raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?(:include?)
|
29
|
+
|
30
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
31
|
+
unless enum.include?(value)
|
32
|
+
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
33
|
+
end
|
34
|
+
end
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -1,69 +1,19 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
|
5
|
-
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
|
6
|
-
|
7
|
-
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
|
8
|
-
|
9
|
-
def initialize(options)
|
10
|
-
if range = (options.delete(:in) || options.delete(:within))
|
11
|
-
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
|
12
|
-
options[:minimum], options[:maximum] = range.begin, range.end
|
13
|
-
options[:maximum] -= 1 if range.exclude_end?
|
14
|
-
end
|
15
|
-
|
16
|
-
super(options.reverse_merge(:tokenizer => DEFAULT_TOKENIZER))
|
17
|
-
end
|
18
|
-
|
19
|
-
def check_validity!
|
20
|
-
keys = CHECKS.keys & options.keys
|
21
|
-
|
22
|
-
if keys.empty?
|
23
|
-
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
|
24
|
-
end
|
25
|
-
|
26
|
-
keys.each do |key|
|
27
|
-
value = options[key]
|
28
|
-
|
29
|
-
unless value.is_a?(Integer) && value >= 0
|
30
|
-
raise ArgumentError, ":#{key} must be a nonnegative Integer"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def validate_each(record, attribute, value)
|
36
|
-
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
37
|
-
|
38
|
-
CHECKS.each do |key, validity_check|
|
39
|
-
next unless check_value = options[key]
|
40
|
-
custom_message = options[:message] || options[MESSAGES[key]]
|
41
|
-
|
42
|
-
valid_value = if key == :maximum
|
43
|
-
value.nil? || value.size.send(validity_check, check_value)
|
44
|
-
else
|
45
|
-
value && value.size.send(validity_check, check_value)
|
46
|
-
end
|
47
|
-
|
48
|
-
next if valid_value
|
49
|
-
record.errors.add(attribute, MESSAGES[key], :default => custom_message, :count => check_value)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
module HelperMethods
|
3
|
+
module ClassMethods
|
4
|
+
ALL_RANGE_OPTIONS = [ :is, :within, :in, :minimum, :maximum ].freeze
|
55
5
|
|
56
6
|
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
|
57
7
|
#
|
58
8
|
# class Person < ActiveRecord::Base
|
59
9
|
# validates_length_of :first_name, :maximum=>30
|
60
|
-
# validates_length_of :last_name, :maximum=>30, :message=>"less than
|
10
|
+
# validates_length_of :last_name, :maximum=>30, :message=>"less than {{count}} if you don't mind"
|
61
11
|
# validates_length_of :fax, :in => 7..32, :allow_nil => true
|
62
12
|
# validates_length_of :phone, :in => 7..32, :allow_blank => true
|
63
13
|
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
|
64
|
-
# validates_length_of :
|
65
|
-
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with
|
66
|
-
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least
|
14
|
+
# validates_length_of :fav_bra_size, :minimum => 1, :too_short => "please enter at least {{count}} character"
|
15
|
+
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with {{count}} characters... don't play me."
|
16
|
+
# validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least {{count}} words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
|
67
17
|
# end
|
68
18
|
#
|
69
19
|
# Configuration options:
|
@@ -74,9 +24,9 @@ module ActiveModel
|
|
74
24
|
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
|
75
25
|
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
|
76
26
|
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
|
77
|
-
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is
|
78
|
-
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is
|
79
|
-
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be
|
27
|
+
# * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)").
|
28
|
+
# * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)").
|
29
|
+
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be {{count}} characters)").
|
80
30
|
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
|
81
31
|
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
|
82
32
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
@@ -88,8 +38,62 @@ module ActiveModel
|
|
88
38
|
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
|
89
39
|
# count words as in above example.)
|
90
40
|
# Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
|
91
|
-
def validates_length_of(*
|
92
|
-
|
41
|
+
def validates_length_of(*attrs)
|
42
|
+
# Merge given options with defaults.
|
43
|
+
options = { :tokenizer => lambda {|value| value.split(//)} }
|
44
|
+
options.update(attrs.extract_options!.symbolize_keys)
|
45
|
+
|
46
|
+
# Ensure that one and only one range option is specified.
|
47
|
+
range_options = ALL_RANGE_OPTIONS & options.keys
|
48
|
+
case range_options.size
|
49
|
+
when 0
|
50
|
+
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
|
51
|
+
when 1
|
52
|
+
# Valid number of options; do nothing.
|
53
|
+
else
|
54
|
+
raise ArgumentError, 'Too many range options specified. Choose only one.'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get range option and value.
|
58
|
+
option = range_options.first
|
59
|
+
option_value = options[range_options.first]
|
60
|
+
key = {:is => :wrong_length, :minimum => :too_short, :maximum => :too_long}[option]
|
61
|
+
custom_message = options[:message] || options[key]
|
62
|
+
|
63
|
+
case option
|
64
|
+
when :within, :in
|
65
|
+
raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range)
|
66
|
+
|
67
|
+
validates_each(attrs, options) do |record, attr, value|
|
68
|
+
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
69
|
+
|
70
|
+
min, max = option_value.begin, option_value.end
|
71
|
+
max = max - 1 if option_value.exclude_end?
|
72
|
+
|
73
|
+
if value.nil? || value.size < min
|
74
|
+
record.errors.add(attr, :too_short, :default => custom_message || options[:too_short], :count => min)
|
75
|
+
elsif value.size > max
|
76
|
+
record.errors.add(attr, :too_long, :default => custom_message || options[:too_long], :count => max)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
when :is, :minimum, :maximum
|
80
|
+
raise ArgumentError, ":#{option} must be a nonnegative Integer" unless option_value.is_a?(Integer) and option_value >= 0
|
81
|
+
|
82
|
+
# Declare different validations per option.
|
83
|
+
validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
|
84
|
+
|
85
|
+
validates_each(attrs, options) do |record, attr, value|
|
86
|
+
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
87
|
+
|
88
|
+
valid_value = if option == :maximum
|
89
|
+
value.nil? || value.size.send(validity_checks[option], option_value)
|
90
|
+
else
|
91
|
+
value && value.size.send(validity_checks[option], option_value)
|
92
|
+
end
|
93
|
+
|
94
|
+
record.errors.add(attr, key, :default => custom_message, :count => option_value) unless valid_value
|
95
|
+
end
|
96
|
+
end
|
93
97
|
end
|
94
98
|
|
95
99
|
alias_method :validates_size_of, :validates_length_of
|
@@ -1,81 +1,10 @@
|
|
1
1
|
module ActiveModel
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module ClassMethods
|
4
|
+
ALL_NUMERICALITY_CHECKS = { :greater_than => '>', :greater_than_or_equal_to => '>=',
|
5
|
+
:equal_to => '==', :less_than => '<', :less_than_or_equal_to => '<=',
|
6
|
+
:odd => 'odd?', :even => 'even?' }.freeze
|
7
7
|
|
8
|
-
def initialize(options)
|
9
|
-
super(options.reverse_merge(:only_integer => false, :allow_nil => false))
|
10
|
-
end
|
11
|
-
|
12
|
-
def check_validity!
|
13
|
-
keys = CHECKS.keys - [:odd, :even]
|
14
|
-
options.slice(*keys).each do |option, value|
|
15
|
-
next if value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
|
16
|
-
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def validate_each(record, attr_name, value)
|
21
|
-
before_type_cast = "#{attr_name}_before_type_cast"
|
22
|
-
|
23
|
-
raw_value = record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast.to_sym)
|
24
|
-
raw_value ||= value
|
25
|
-
|
26
|
-
return if options[:allow_nil] && raw_value.nil?
|
27
|
-
|
28
|
-
unless value = parse_raw_value_as_a_number(raw_value)
|
29
|
-
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => options[:message])
|
30
|
-
return
|
31
|
-
end
|
32
|
-
|
33
|
-
if options[:only_integer]
|
34
|
-
unless value = parse_raw_value_as_an_integer(raw_value)
|
35
|
-
record.errors.add(attr_name, :not_an_integer, :value => raw_value, :default => options[:message])
|
36
|
-
return
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
options.slice(*CHECKS.keys).each do |option, option_value|
|
41
|
-
case option
|
42
|
-
when :odd, :even
|
43
|
-
unless value.to_i.send(CHECKS[option])
|
44
|
-
record.errors.add(attr_name, option, :value => value, :default => options[:message])
|
45
|
-
end
|
46
|
-
else
|
47
|
-
option_value = option_value.call(record) if option_value.is_a?(Proc)
|
48
|
-
option_value = record.send(option_value) if option_value.is_a?(Symbol)
|
49
|
-
|
50
|
-
unless value.send(CHECKS[option], option_value)
|
51
|
-
record.errors.add(attr_name, option, :default => options[:message], :value => value, :count => option_value)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
protected
|
58
|
-
|
59
|
-
def parse_raw_value_as_a_number(raw_value)
|
60
|
-
case raw_value
|
61
|
-
when /\A0[xX]/
|
62
|
-
nil
|
63
|
-
else
|
64
|
-
begin
|
65
|
-
Kernel.Float(raw_value)
|
66
|
-
rescue ArgumentError, TypeError
|
67
|
-
nil
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def parse_raw_value_as_an_integer(raw_value)
|
73
|
-
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
module HelperMethods
|
79
8
|
# Validates whether the value of the specified attribute is numeric by trying to convert it to
|
80
9
|
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
|
81
10
|
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
|
@@ -115,8 +44,61 @@ module ActiveModel
|
|
115
44
|
# validates_numericality_of :width, :greater_than => :minimum_weight
|
116
45
|
# end
|
117
46
|
#
|
47
|
+
#
|
48
|
+
|
118
49
|
def validates_numericality_of(*attr_names)
|
119
|
-
|
50
|
+
configuration = { :only_integer => false, :allow_nil => false }
|
51
|
+
configuration.update(attr_names.extract_options!)
|
52
|
+
|
53
|
+
numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys
|
54
|
+
|
55
|
+
(numericality_options - [ :odd, :even ]).each do |option|
|
56
|
+
value = configuration[option]
|
57
|
+
raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
|
58
|
+
end
|
59
|
+
|
60
|
+
validates_each(attr_names,configuration) do |record, attr_name, value|
|
61
|
+
before_type_cast = "#{attr_name}_before_type_cast"
|
62
|
+
|
63
|
+
if record.respond_to?(before_type_cast.to_sym)
|
64
|
+
raw_value = record.send("#{attr_name}_before_type_cast") || value
|
65
|
+
else
|
66
|
+
raw_value = value
|
67
|
+
end
|
68
|
+
|
69
|
+
next if configuration[:allow_nil] and raw_value.nil?
|
70
|
+
|
71
|
+
if configuration[:only_integer]
|
72
|
+
unless raw_value.to_s =~ /\A[+-]?\d+\Z/
|
73
|
+
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
|
74
|
+
next
|
75
|
+
end
|
76
|
+
raw_value = raw_value.to_i
|
77
|
+
else
|
78
|
+
begin
|
79
|
+
raw_value = Kernel.Float(raw_value)
|
80
|
+
rescue ArgumentError, TypeError
|
81
|
+
record.errors.add(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message])
|
82
|
+
next
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
numericality_options.each do |option|
|
87
|
+
case option
|
88
|
+
when :odd, :even
|
89
|
+
unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
90
|
+
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
91
|
+
end
|
92
|
+
else
|
93
|
+
configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc
|
94
|
+
configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol
|
95
|
+
|
96
|
+
unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
|
97
|
+
record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
120
102
|
end
|
121
103
|
end
|
122
104
|
end
|