not-naughty 0.6.0
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.rdoc +46 -0
- data/COPYING +18 -0
- data/README.rdoc +59 -0
- data/Rakefile +126 -0
- data/lib/core_extensions.rb +20 -0
- data/lib/not_naughty.rb +86 -0
- data/lib/not_naughty/builder.rb +68 -0
- data/lib/not_naughty/error_handler.rb +48 -0
- data/lib/not_naughty/instance_methods.rb +20 -0
- data/lib/not_naughty/validation.rb +126 -0
- data/lib/not_naughty/validations/acceptance_validation.rb +46 -0
- data/lib/not_naughty/validations/confirmation_validation.rb +41 -0
- data/lib/not_naughty/validations/format_validation.rb +55 -0
- data/lib/not_naughty/validations/length_validation.rb +95 -0
- data/lib/not_naughty/validations/numericality_validation.rb +43 -0
- data/lib/not_naughty/validations/presence_validation.rb +31 -0
- data/lib/not_naughty/validator.rb +125 -0
- data/lib/not_naughty/violation.rb +36 -0
- data/spec/builder_spec.rb +80 -0
- data/spec/error_handler_spec.rb +21 -0
- data/spec/not_naughty_spec.rb +82 -0
- data/spec/rcov.opts +4 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/validation_spec.rb +118 -0
- data/spec/validations_spec.rb +267 -0
- data/spec/validator_spec.rb +132 -0
- data/spec/violation_spec.rb +36 -0
- metadata +93 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
module InstanceMethods
|
3
|
+
|
4
|
+
# Returns instance of Errors.
|
5
|
+
def errors() @errors ||= ::NotNaughty::Violation.new end
|
6
|
+
|
7
|
+
# Returns true if all validations passed
|
8
|
+
def valid?
|
9
|
+
validate
|
10
|
+
errors.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
# Clears errors and validates.
|
14
|
+
def validate
|
15
|
+
errors.clear
|
16
|
+
self.class.validator.invoke self
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == The superclass for Validations.
|
4
|
+
#
|
5
|
+
# See new for more information.
|
6
|
+
class Validation
|
7
|
+
|
8
|
+
BASEDIR = File.dirname __FILE__
|
9
|
+
# Loader pattern
|
10
|
+
PATTERN = File.join BASEDIR, %w[validations ** %s_validation.rb]
|
11
|
+
|
12
|
+
# Loads validations.
|
13
|
+
def self.load(*validations)
|
14
|
+
validations.each do |validation|
|
15
|
+
Dir.glob(PATTERN % validation).each { |path| require path }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
extend Observable
|
20
|
+
def self.inherited(descendant) #:nodoc:
|
21
|
+
changed and notify_observers(descendant)
|
22
|
+
|
23
|
+
descendant.
|
24
|
+
instance_variable_set :@observer_peers, @observer_peers.clone
|
25
|
+
end
|
26
|
+
|
27
|
+
# Builds validations.
|
28
|
+
#
|
29
|
+
# <b>Example:</b>
|
30
|
+
# NotNaughty::Validation.new :temp, :if => :water? do |obj, attr, val|
|
31
|
+
# obj.errors.add attr, 'too hot' unless val < 100
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# <b>Like:</b>
|
35
|
+
# class TempValidation < NotNaughty::Validation
|
36
|
+
# def initialize(opts, attributes)
|
37
|
+
# super opts, attributes method(:temp_validation)
|
38
|
+
# end
|
39
|
+
# def temp_validation(obj, attr, val)
|
40
|
+
# obj.errors.add attr, 'too hot' unless val < 100
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Validation.new TempValidation, :temp, :if => :water?
|
45
|
+
#
|
46
|
+
# The last one also notifies all Observers of Validation (see
|
47
|
+
# Builder#update). If Builder#update is called because <Name>Validation
|
48
|
+
# is inherited from Validation the ValidationBuilder gets the method
|
49
|
+
# validates_<name>_of and so does the classes that included the Builder.
|
50
|
+
def self.new(*params, &block)
|
51
|
+
attributes = if params.first.is_a? Class and params.first < self
|
52
|
+
klass = params.shift
|
53
|
+
klass.new(*params, &block)
|
54
|
+
else
|
55
|
+
options = params.extract_options!
|
56
|
+
instance = allocate
|
57
|
+
instance.send :initialize, options, params.map {|p|p.to_sym}, &block
|
58
|
+
instance
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :attributes
|
63
|
+
|
64
|
+
def initialize(opts, attributes, &block) #:nodoc:
|
65
|
+
build_conditions opts[:if], opts[:unless]
|
66
|
+
@attributes, @block, @opts = attributes, block, opts
|
67
|
+
end
|
68
|
+
|
69
|
+
def call_without_conditions(obj, attr, value) #:nodoc:
|
70
|
+
@block.call obj, attr, value
|
71
|
+
end
|
72
|
+
alias_method :call, :call_without_conditions
|
73
|
+
|
74
|
+
def call_with_conditions(obj, attr, value) #:nodoc:
|
75
|
+
if @conditions.all? { |c| c.evaluate obj }
|
76
|
+
call_without_conditions obj, attr, value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
def build_conditions(p, n) #:nodoc:
|
82
|
+
@conditions = []
|
83
|
+
[p].flatten.each {|c| @conditions << Condition.new(c) if c }
|
84
|
+
[n].flatten.each {|c| @conditions << Condition.new(c, false) if c }
|
85
|
+
|
86
|
+
(class << self; self; end).module_eval do
|
87
|
+
alias_method :call, :call_with_conditions
|
88
|
+
end unless @conditions.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
# Conditions for use in Validations are usually used with Validations.
|
92
|
+
class Condition
|
93
|
+
|
94
|
+
# An instance of Condition accepts Symbols, UnboundMethods or anything
|
95
|
+
# that responds to :call.
|
96
|
+
#
|
97
|
+
# The following examples are similiar to each other:
|
98
|
+
#
|
99
|
+
# NotNaughty::Validation::Condition.new proc {|o| o.nil?}
|
100
|
+
# NotNaughty::Validation::Condition.new :nil?
|
101
|
+
# NotNaughty::Validation::Condition.new Object.instance_method(:nil?)
|
102
|
+
def self.new(condition, positive = true)
|
103
|
+
instance = allocate
|
104
|
+
instance.instance_variable_set :@condition, condition
|
105
|
+
|
106
|
+
block = case condition
|
107
|
+
when Symbol then positive ?
|
108
|
+
proc { |o| o.send @condition } :
|
109
|
+
proc { |o| not o.send @condition }
|
110
|
+
when UnboundMethod then positive ?
|
111
|
+
proc { |o| @condition.bind(o).call } :
|
112
|
+
proc { |o| not @condition.bind(o).call }
|
113
|
+
else positive ?
|
114
|
+
proc { |o| @condition.call o } :
|
115
|
+
proc { |o| not @condition.call o }
|
116
|
+
end
|
117
|
+
|
118
|
+
(class << instance; self; end).
|
119
|
+
module_eval { define_method(:evaluate, &block) }
|
120
|
+
|
121
|
+
instance
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == Validates acceptance of obj's attribute against a fixed value via <tt>:eql?</tt> call.
|
4
|
+
#
|
5
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
6
|
+
# is added to the obj's instance of Errors.
|
7
|
+
#
|
8
|
+
# <b>Options:</b>
|
9
|
+
# <tt>:accept</tt>:: object to check against (via :eql?)
|
10
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
11
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
12
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
13
|
+
#
|
14
|
+
# <b>Example:</b>
|
15
|
+
#
|
16
|
+
# invalid = 'abc'
|
17
|
+
# valid = '1'
|
18
|
+
# def invalid.errors() @errors ||= NotNauthy::Errors.new end
|
19
|
+
# def valid.errors() @errors ||= NotNauthy::Errors.new end
|
20
|
+
#
|
21
|
+
# AcceptanceValidation.new({}, :to_s).call invalid, :to_s, 'abc'
|
22
|
+
# invalid.errors.on(:to_s).any? # => true
|
23
|
+
#
|
24
|
+
# AcceptanceValidation.new({}, :to_s).call valid, :to_s, '1'
|
25
|
+
# valid.errors.on(:to_s).any? # => false
|
26
|
+
class AcceptanceValidation < Validation
|
27
|
+
|
28
|
+
def initialize(opts, attributes) #:nodoc:
|
29
|
+
__message, __accept =
|
30
|
+
opts[:message] || '#{"%s".humanize} not accepted.',
|
31
|
+
opts[:accept] || '1'
|
32
|
+
|
33
|
+
if opts[:allow_blank] or opts.fetch(:allow_nil, true)
|
34
|
+
__allow = if opts[:allow_blank] then :blank? else :nil? end
|
35
|
+
super opts, attributes do |o, a, v|
|
36
|
+
o.errors.add a, __message unless v.send __allow or __accept.eql? v
|
37
|
+
end
|
38
|
+
else
|
39
|
+
super opts, attributes do |o, a, v|
|
40
|
+
o.errors.add a, __message unless __accept.eql? v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == Validates confirmaton of obj's attribute via <tt>:eql?</tt> call against the _appropiate_ confirmation attribute.
|
4
|
+
#
|
5
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
6
|
+
# is added to the obj's instance of Errors.
|
7
|
+
#
|
8
|
+
# <b>Options:</b>
|
9
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
10
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
11
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
12
|
+
#
|
13
|
+
# <b>Example:</b>
|
14
|
+
#
|
15
|
+
# obj = 'abc'
|
16
|
+
# def obj.errors() @errors ||= NotNauthy::Errors.new end
|
17
|
+
# def obj.to_s_confirmation() '123 end
|
18
|
+
#
|
19
|
+
# ConfirmationValidation.new({}, :to_s).call obj, :to_s, 'abc'
|
20
|
+
# obj.errors.on(:to_s).any? # => true
|
21
|
+
class ConfirmationValidation < Validation
|
22
|
+
|
23
|
+
def initialize(opts, attributes) #:nodoc:
|
24
|
+
__message = opts[:message] || '#{"%s".humanize} could not be confirmed.'
|
25
|
+
|
26
|
+
if opts[:allow_blank] or opts[:allow_nil]
|
27
|
+
__allow = if opts[:allow_blank] then :blank? else :nil? end
|
28
|
+
super opts, attributes do |o, a, v|
|
29
|
+
o.errors.add a, __message unless
|
30
|
+
v.send __allow or o.send(:"#{a}_confirmation").eql? v
|
31
|
+
end
|
32
|
+
else
|
33
|
+
super opts, attributes do |o, a, v|
|
34
|
+
o.errors.add a, __message unless
|
35
|
+
o.send(:"#{a}_confirmation").eql? v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == Validates format of obj's attribute via the <tt>:match</tt> method.
|
4
|
+
#
|
5
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
6
|
+
# is added to the obj's instance of Errors.
|
7
|
+
#
|
8
|
+
# <b>Options:</b>
|
9
|
+
# <tt>:with</tt>:: object that checks via a <tt>:match</tt> call
|
10
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
11
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
12
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
13
|
+
#
|
14
|
+
# <b>Example:</b>
|
15
|
+
#
|
16
|
+
# obj = 'abc'
|
17
|
+
# def obj.errors() @errors ||= NotNauthy::Errors.new end
|
18
|
+
#
|
19
|
+
# FormatValidation.new({:with => /[a-z]/}, :to_s).call obj, :to_s, 'abc'
|
20
|
+
# obj.errors.on(:to_s).any? # => false
|
21
|
+
#
|
22
|
+
# FormatValidation.new({:with => /[A-Z]/}, :to_s).call obj, :to_s, 'abc'
|
23
|
+
# obj.errors.on(:to_s) # => ["Format of to_s does not match."]
|
24
|
+
class FormatValidation < Validation
|
25
|
+
|
26
|
+
# Predefined matchers.
|
27
|
+
DEFINITIONS = {
|
28
|
+
:email => /[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/,
|
29
|
+
:ip => /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
|
30
|
+
}
|
31
|
+
|
32
|
+
def initialize(opts, attributes) #:nodoc:
|
33
|
+
format_matcher = if Symbol === opts[:with] then DEFINITIONS[opts[:with]]
|
34
|
+
elsif opts[:with].respond_to? :match then opts[:with]
|
35
|
+
end or raise ArgumentError, "#{ opts[:with].inspect } doesn't :match"
|
36
|
+
|
37
|
+
msg = opts[:message] || '%s does not match format.'
|
38
|
+
|
39
|
+
if opts[:allow_blank] or opts[:allow_nil]
|
40
|
+
pass = opts[:allow_blank] ? :blank? : :nil?
|
41
|
+
|
42
|
+
super opts, attributes do |obj, attr, val|
|
43
|
+
val.send pass or format_matcher.match val or
|
44
|
+
obj.errors.add attr, msg
|
45
|
+
end
|
46
|
+
else
|
47
|
+
super opts, attributes do |obj, attr, val|
|
48
|
+
format_matcher.match val or
|
49
|
+
obj.errors.add attr, msg
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == Validates length of obj's attribute via the <tt>:length</tt> method.
|
4
|
+
#
|
5
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
6
|
+
# is added to the obj's instance of Errors.
|
7
|
+
#
|
8
|
+
# <b>Options:</b>
|
9
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
10
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
11
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
12
|
+
#
|
13
|
+
# <b>Boundaries (by precendence):</b>
|
14
|
+
# <tt>:is</tt>:: valid length
|
15
|
+
# <tt>:within</tt>:: valid range of length
|
16
|
+
# <tt>:minimum</tt>:: maximum length
|
17
|
+
# <tt>:maximum</tt>:: minimum length
|
18
|
+
#
|
19
|
+
# If both, <tt>:minimum</tt> and <tt>:maximum</tt> are provided they're
|
20
|
+
# converted to :within. Each boundary type has its own default message:
|
21
|
+
# precise:: "Length of %s is not equal to #{__length}."
|
22
|
+
# range:: "Length of %s is not within #{__range.first} and #{__range.last}."
|
23
|
+
# lower:: "Length of %s is smaller than #{__boundary}."
|
24
|
+
# upper:: "Length of %s is greater than #{__boundary}."
|
25
|
+
#
|
26
|
+
# <b>Example:</b>
|
27
|
+
#
|
28
|
+
# obj = %w[a sentence with five words] #
|
29
|
+
# def obj.errors() @errors ||= NotNauthy::Errors.new end
|
30
|
+
#
|
31
|
+
# LengthValidation.new({:minimum => 4}, :to_a).
|
32
|
+
# call obj, :to_a, %w[a sentence with five words]
|
33
|
+
# obj.errors.on(:to_s).any? # => false
|
34
|
+
#
|
35
|
+
# LengthValidation.new({:within => 1..4}, :to_a).
|
36
|
+
# call obj, :to_a, %w[a sentence with five words]
|
37
|
+
# obj.errors.on(:to_s).any? # => true
|
38
|
+
class LengthValidation < Validation
|
39
|
+
|
40
|
+
def initialize(opts, attributes) #:nodoc:
|
41
|
+
|
42
|
+
block = build_block opts
|
43
|
+
|
44
|
+
if opts[:allow_blank]
|
45
|
+
super opts, attributes do |o, a, v|
|
46
|
+
block[o, a, v] unless v.blank?
|
47
|
+
end
|
48
|
+
else
|
49
|
+
super opts, attributes do |o, a, v|
|
50
|
+
block[o, a, v] unless v.nil?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def build_block(opts) #:nodoc:
|
57
|
+
if __length = opts[:is]
|
58
|
+
__message = opts[:message] ||
|
59
|
+
"Length of %s is not equal to #{__length}."
|
60
|
+
proc do |o, a, v|
|
61
|
+
o.errors.add a, __message unless __length.eql? v.length
|
62
|
+
end
|
63
|
+
elsif opts[:within] or opts[:minimum] && opts[:maximum]
|
64
|
+
__range = opts[:within]
|
65
|
+
__range ||= Range.new opts[:minimum], opts[:maximum]
|
66
|
+
|
67
|
+
__message = opts[:message] ||
|
68
|
+
"Length of %s is not within #{__range.first} and #{__range.last}."
|
69
|
+
|
70
|
+
proc do |o, a, v|
|
71
|
+
o.errors.add a, __message unless __range.include? v.length
|
72
|
+
end
|
73
|
+
elsif opts[:minimum]
|
74
|
+
__boundary = opts[:minimum]
|
75
|
+
__message = opts[:message] ||
|
76
|
+
"Length of %s is smaller than #{__boundary}."
|
77
|
+
|
78
|
+
proc do |o, a, v|
|
79
|
+
o.errors.add a, __message unless __boundary <= v.length
|
80
|
+
end
|
81
|
+
elsif opts[:maximum]
|
82
|
+
__boundary = opts[:maximum]
|
83
|
+
__message = opts[:message] ||
|
84
|
+
"Length of %s is greater than #{__boundary}."
|
85
|
+
|
86
|
+
proc do |o, a, v|
|
87
|
+
o.errors.add a, __message unless __boundary >= v.length
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise ArgumentError, 'no boundary given'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
NotNaughty::Validation.load :format
|
2
|
+
|
3
|
+
module NotNaughty
|
4
|
+
|
5
|
+
# == Validates numericality of obj's attribute via an regular expression.
|
6
|
+
#
|
7
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
8
|
+
# is added to the obj's instance of Errors.
|
9
|
+
#
|
10
|
+
# <b>Options:</b>
|
11
|
+
# <tt>:only_integer</tt>:: validates with <tt>/^[+-]?\d+$/</tt> (false)
|
12
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
13
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
14
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
15
|
+
#
|
16
|
+
# <b>Example:</b>
|
17
|
+
#
|
18
|
+
# obj = '-12.2' #
|
19
|
+
# def obj.errors() @errors ||= NotNauthy::Errors.new end
|
20
|
+
#
|
21
|
+
# NumericalityValidation.new({}, :to_s).call obj, :to_s, '-12.2'
|
22
|
+
# obj.errors.on(:to_s).any? # => false
|
23
|
+
#
|
24
|
+
# NumericalityValidation.new({:only_integer => true}, :to_s).
|
25
|
+
# call obj, :to_s, '-12.2'
|
26
|
+
#
|
27
|
+
# obj.errors.on(:to_s).any? # => true
|
28
|
+
class NumericalityValidation < FormatValidation
|
29
|
+
|
30
|
+
def initialize(opts, attributes) #:nodoc:
|
31
|
+
opts[:with] = if opts[:only_integer]
|
32
|
+
opts[:message] ||= '#{"%s".humanize} is not an integer.'
|
33
|
+
/^[+-]?\d+$/
|
34
|
+
else
|
35
|
+
opts[:message] ||= '#{"%s".humanize} is not a number.'
|
36
|
+
/^[+-]?\d*\.?\d+$/
|
37
|
+
end
|
38
|
+
|
39
|
+
super opts, attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module NotNaughty
|
2
|
+
|
3
|
+
# == Validates presence of obj's attribute via the <tt>:blank?</tt> method.
|
4
|
+
#
|
5
|
+
# Unless the validation succeeds an error hash (:attribute => :message)
|
6
|
+
# is added to the obj's instance of Errors.
|
7
|
+
#
|
8
|
+
# <b>Options:</b>
|
9
|
+
# <tt>:message</tt>:: see NotNaughty::Errors for details
|
10
|
+
# <tt>:if</tt>:: see NotNaughty::Validation::Condition for details
|
11
|
+
# <tt>:unless</tt>:: see NotNaughty::Validation::Condition for details
|
12
|
+
#
|
13
|
+
# <b>Example:</b>
|
14
|
+
#
|
15
|
+
# obj = '' # blank? => true
|
16
|
+
# def obj.errors() @errors ||= NotNauthy::Errors.new end
|
17
|
+
#
|
18
|
+
# PresenceValidation.new({}, :to_s).call obj, :to_s, ''
|
19
|
+
# obj.errors.on(:to_s) # => ["To s is not present."]
|
20
|
+
class PresenceValidation < Validation
|
21
|
+
|
22
|
+
def initialize(opts, attributes) #:nodoc:
|
23
|
+
__message = opts[:message] || '#{"%s".humanize} is not present.'
|
24
|
+
|
25
|
+
super opts, attributes do |o, a, v|
|
26
|
+
o.errors.add a, __message if v.blank?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|