attr_validator 0.0.1
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/.gitignore +4 -0
- data/.travis.yml +4 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/Rakefile +1 -0
- data/attr_validator.gemspec +24 -0
- data/lib/attr_validator/args_validator.rb +118 -0
- data/lib/attr_validator/concern.rb +136 -0
- data/lib/attr_validator/core_extensions/class_attribute.rb +143 -0
- data/lib/attr_validator/errors.rb +23 -0
- data/lib/attr_validator/i18n.rb +7 -0
- data/lib/attr_validator/locales/en.yml +23 -0
- data/lib/attr_validator/validation_errors.rb +245 -0
- data/lib/attr_validator/validator.rb +148 -0
- data/lib/attr_validator/validators/email_validator.rb +48 -0
- data/lib/attr_validator/validators/exclusion_validator.rb +23 -0
- data/lib/attr_validator/validators/inclusion_validator.rb +28 -0
- data/lib/attr_validator/validators/length_validator.rb +32 -0
- data/lib/attr_validator/validators/not_nil_validator.rb +25 -0
- data/lib/attr_validator/validators/numericality_validator.rb +39 -0
- data/lib/attr_validator/validators/presence_validator.rb +26 -0
- data/lib/attr_validator/validators/regexp_validator.rb +21 -0
- data/lib/attr_validator/validators/url_validator.rb +25 -0
- data/lib/attr_validator/validators.rb +11 -0
- data/lib/attr_validator/version.rb +3 -0
- data/lib/attr_validator.rb +41 -0
- data/spec/attr_validator/validator_spec.rb +105 -0
- data/spec/attr_validator/validators/email_validator_spec.rb +24 -0
- data/spec/attr_validator/validators/exclusion_validator_spec.rb +24 -0
- data/spec/attr_validator/validators/inclusion_validator_spec.rb +24 -0
- data/spec/attr_validator/validators/length_validator_spec.rb +39 -0
- data/spec/attr_validator/validators/not_nil_validator_spec.rb +25 -0
- data/spec/attr_validator/validators/numericality_validator_spec.rb +50 -0
- data/spec/attr_validator/validators/presence_validator_spec.rb +25 -0
- data/spec/attr_validator/validators/regexp_validator_spec.rb +24 -0
- data/spec/attr_validator/validators/url_validator_spec.rb +24 -0
- data/spec/spec_helper.rb +7 -0
- metadata +145 -0
@@ -0,0 +1,245 @@
|
|
1
|
+
class AttrValidator::ValidationErrors
|
2
|
+
attr_reader :messages
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@messages = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
# Clear the error messages.
|
9
|
+
#
|
10
|
+
# errors.full_messages # => ["name can not be nil"]
|
11
|
+
# errors.clear
|
12
|
+
# errors.full_messages # => []
|
13
|
+
def clear
|
14
|
+
messages.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns +true+ if the error messages include an error for the given key
|
18
|
+
# +attribute+, +false+ otherwise.
|
19
|
+
#
|
20
|
+
# errors.messages # => {:name=>["can not be nil"]}
|
21
|
+
# errors.include?(:name) # => true
|
22
|
+
# errors.include?(:age) # => false
|
23
|
+
def include?(attribute)
|
24
|
+
(v = messages[attribute]) && v.any?
|
25
|
+
end
|
26
|
+
# aliases include?
|
27
|
+
alias :has_key? :include?
|
28
|
+
|
29
|
+
# Get messages for +key+.
|
30
|
+
#
|
31
|
+
# errors.messages # => {:name=>["can not be nil"]}
|
32
|
+
# errors.get(:name) # => ["can not be nil"]
|
33
|
+
# errors.get(:age) # => nil
|
34
|
+
def get(key)
|
35
|
+
messages[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set messages for +key+ to +value+.
|
39
|
+
#
|
40
|
+
# errors.get(:name) # => ["can not be nil"]
|
41
|
+
# errors.set(:name, ["can't be nil"])
|
42
|
+
# errors.get(:name) # => ["can't be nil"]
|
43
|
+
def set(key, value)
|
44
|
+
messages[key] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Delete messages for +key+. Returns the deleted messages.
|
48
|
+
#
|
49
|
+
# errors.get(:name) # => ["can not be nil"]
|
50
|
+
# errors.delete(:name) # => ["can not be nil"]
|
51
|
+
# errors.get(:name) # => nil
|
52
|
+
def delete(key)
|
53
|
+
messages.delete(key)
|
54
|
+
end
|
55
|
+
|
56
|
+
# When passed a symbol or a name of a method, returns an array of errors
|
57
|
+
# for the method.
|
58
|
+
#
|
59
|
+
# errors[:name] # => ["can not be nil"]
|
60
|
+
# errors['name'] # => ["can not be nil"]
|
61
|
+
def [](attribute)
|
62
|
+
get(attribute.to_sym) || set(attribute.to_sym, [])
|
63
|
+
end
|
64
|
+
|
65
|
+
# Adds to the supplied attribute the supplied error message.
|
66
|
+
#
|
67
|
+
# errors[:name] = "must be set"
|
68
|
+
# errors[:name] # => ['must be set']
|
69
|
+
def []=(attribute, error)
|
70
|
+
self[attribute] << error
|
71
|
+
end
|
72
|
+
|
73
|
+
# Iterates through each error key, value pair in the error messages hash.
|
74
|
+
# Yields the attribute and the error for that attribute. If the attribute
|
75
|
+
# has more than one error message, yields once for each error message.
|
76
|
+
#
|
77
|
+
# errors.add(:name, "can't be blank")
|
78
|
+
# errors.each do |attribute, error|
|
79
|
+
# # Will yield :name and "can't be blank"
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# errors.add(:name, "must be specified")
|
83
|
+
# errors.each do |attribute, error|
|
84
|
+
# # Will yield :name and "can't be blank"
|
85
|
+
# # then yield :name and "must be specified"
|
86
|
+
# end
|
87
|
+
def each
|
88
|
+
messages.each_key do |attribute|
|
89
|
+
self[attribute].each { |error| yield attribute, error }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns the number of error messages.
|
94
|
+
#
|
95
|
+
# errors.add(:name, "can't be blank")
|
96
|
+
# errors.size # => 1
|
97
|
+
# errors.add(:name, "must be specified")
|
98
|
+
# errors.size # => 2
|
99
|
+
def size
|
100
|
+
values.flatten.size
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns all message values.
|
104
|
+
#
|
105
|
+
# errors.messages # => {:name=>["can not be nil", "must be specified"]}
|
106
|
+
# errors.values # => [["can not be nil", "must be specified"]]
|
107
|
+
def values
|
108
|
+
messages.values
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns all message keys.
|
112
|
+
#
|
113
|
+
# errors.messages # => {:name=>["can not be nil", "must be specified"]}
|
114
|
+
# errors.keys # => [:name]
|
115
|
+
def keys
|
116
|
+
messages.keys
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns an array of error messages, with the attribute name included.
|
120
|
+
#
|
121
|
+
# errors.add(:name, "can't be blank")
|
122
|
+
# errors.add(:name, "must be specified")
|
123
|
+
# errors.to_a # => ["name can't be blank", "name must be specified"]
|
124
|
+
def to_a
|
125
|
+
full_messages
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the number of error messages.
|
129
|
+
#
|
130
|
+
# errors.add(:name, "can't be blank")
|
131
|
+
# errors.count # => 1
|
132
|
+
# errors.add(:name, "must be specified")
|
133
|
+
# errors.count # => 2
|
134
|
+
def count
|
135
|
+
to_a.size
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns +true+ if no errors are found, +false+ otherwise.
|
139
|
+
# If the error message is a string it can be empty.
|
140
|
+
#
|
141
|
+
# errors.full_messages # => ["name can not be nil"]
|
142
|
+
# errors.empty? # => false
|
143
|
+
def empty?
|
144
|
+
messages.all? { |k, v| v && v.empty? && !v.is_a?(String) }
|
145
|
+
end
|
146
|
+
# aliases empty?
|
147
|
+
alias_method :blank?, :empty?
|
148
|
+
|
149
|
+
# Returns a Hash of attributes with their error messages. If +full_messages+
|
150
|
+
# is +true+, it will contain full messages (see +full_message+).
|
151
|
+
#
|
152
|
+
# errors.to_hash # => {:name=>["can not be nil"]}
|
153
|
+
# errors.to_hash(true) # => {:name=>["name can not be nil"]}
|
154
|
+
def to_hash(full_messages = false)
|
155
|
+
if full_messages
|
156
|
+
messages = {}
|
157
|
+
self.messages.each do |attribute, array|
|
158
|
+
messages[attribute] = array.map { |message| full_message(attribute, message) }
|
159
|
+
end
|
160
|
+
messages
|
161
|
+
else
|
162
|
+
self.messages.dup
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Adds +message+ to the error messages on +attribute+. More than one error
|
167
|
+
# can be added to the same +attribute+
|
168
|
+
#
|
169
|
+
# errors.add(:name, 'is invalid')
|
170
|
+
# # => ["is invalid"]
|
171
|
+
# errors.add(:name, 'must be implemented')
|
172
|
+
# # => ["is invalid", "must be implemented"]
|
173
|
+
#
|
174
|
+
# errors.messages
|
175
|
+
# # => {:name=>["must be implemented", "is invalid"]}
|
176
|
+
#
|
177
|
+
# If +message+ is a proc, it will be called, allowing for things like
|
178
|
+
# <tt>Time.now</tt> to be used within an error.
|
179
|
+
#
|
180
|
+
# errors.messages # => {}
|
181
|
+
def add(attribute, message)
|
182
|
+
self[attribute] << message
|
183
|
+
end
|
184
|
+
|
185
|
+
# Adds +messages+ to the error messages on +attribute+.
|
186
|
+
#
|
187
|
+
# errors.add(:name, ['is invalid', 'must present'])
|
188
|
+
# # => ["is invalid", "must present"]
|
189
|
+
def add_all(attribute, errors)
|
190
|
+
messages[attribute] ||= []
|
191
|
+
messages[attribute] += errors
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns +true+ if an error on the attribute with the given message is
|
195
|
+
# present, +false+ otherwise. +message+ is treated the same as for +add+.
|
196
|
+
#
|
197
|
+
# errors.add :name, :blank
|
198
|
+
# errors.added? :name, :blank # => true
|
199
|
+
def added?(attribute, message)
|
200
|
+
self[attribute].include? message
|
201
|
+
end
|
202
|
+
|
203
|
+
# Returns all the full error messages in an array.
|
204
|
+
#
|
205
|
+
# class PersonValidator
|
206
|
+
# validates :name, :address, :email, presence: true
|
207
|
+
# validates :name, length: { min: 5, max: 30 }
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# = create(address: '123 First St.')
|
211
|
+
# errors.full_messages
|
212
|
+
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
|
213
|
+
def full_messages
|
214
|
+
map { |attribute, message| full_message(attribute, message) }
|
215
|
+
end
|
216
|
+
|
217
|
+
# Returns all the full error messages for a given attribute in an array.
|
218
|
+
#
|
219
|
+
# class PersonValidator
|
220
|
+
# validates :name, :address, :email, presence: true
|
221
|
+
# validates :name, length: { min: 5, max: 30 }
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
# = create()
|
225
|
+
# errors.full_messages_for(:name)
|
226
|
+
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
|
227
|
+
def full_messages_for(attribute)
|
228
|
+
(get(attribute) || []).map { |message| full_message(attribute, message) }
|
229
|
+
end
|
230
|
+
|
231
|
+
# Returns a full message for a given attribute.
|
232
|
+
#
|
233
|
+
# errors.full_message(:name, 'is invalid') # => "Name is invalid"
|
234
|
+
def full_message(attribute, message)
|
235
|
+
return message if attribute == :base
|
236
|
+
attr_name = attribute.to_s.tr('.', '_').humanize
|
237
|
+
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
|
238
|
+
I18n.t(:"errors.format", {
|
239
|
+
default: "%{attribute} %{message}",
|
240
|
+
attribute: attr_name,
|
241
|
+
message: message
|
242
|
+
})
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module AttrValidator::Validator
|
2
|
+
extend AttrValidator::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
class_attribute :validations, :associated_validations, :custom_validations
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def validates(*args)
|
10
|
+
options = args.pop
|
11
|
+
AttrValidator::ArgsValidator.is_hash!(options, "last argument")
|
12
|
+
|
13
|
+
self.validations ||= {}
|
14
|
+
args.each do |attr_name|
|
15
|
+
add_validations(attr_name, options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_associated(association_name, options)
|
20
|
+
AttrValidator::ArgsValidator.not_nil!(options[:validator], :validator)
|
21
|
+
AttrValidator::ArgsValidator.is_class_or_symbol!(options[:validator], :validator)
|
22
|
+
AttrValidator::ArgsValidator.is_symbol_or_block!(options[:if], :if) if options[:if]
|
23
|
+
AttrValidator::ArgsValidator.is_symbol_or_block!(options[:unless], :unless) if options[:unless]
|
24
|
+
|
25
|
+
self.associated_validations ||= {}
|
26
|
+
self.associated_validations[association_name] = options
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate(method_name = nil, &block)
|
30
|
+
self.custom_validations ||= []
|
31
|
+
if block_given?
|
32
|
+
self.custom_validations << block
|
33
|
+
elsif method_name
|
34
|
+
AttrValidator::ArgsValidator.is_symbol!(method_name, "validate method name")
|
35
|
+
self.custom_validations << method_name
|
36
|
+
else
|
37
|
+
raise ArgumentError, "method name or block should be given for validate"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def add_validations(attr_name, options)
|
44
|
+
self.validations[attr_name] ||= {}
|
45
|
+
options.each do |validator_name, validation_options|
|
46
|
+
validator = AttrValidator.validators[validator_name]
|
47
|
+
unless validator
|
48
|
+
raise AttrValidator::Errors::MissingValidatorError, "Validator with name '#{validator_name}' doesn't exist"
|
49
|
+
end
|
50
|
+
validator.validate_options(validation_options)
|
51
|
+
self.validations[attr_name][validator] = validation_options
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate(entity)
|
57
|
+
errors = AttrValidator::ValidationErrors.new
|
58
|
+
self.validations ||= {}
|
59
|
+
self.custom_validations ||= []
|
60
|
+
self.associated_validations ||= {}
|
61
|
+
|
62
|
+
self.validations.each do |attr_name, validators|
|
63
|
+
error_messages = validate_attr(attr_name, entity, validators)
|
64
|
+
errors.add_all(attr_name, error_messages) unless error_messages.empty?
|
65
|
+
end
|
66
|
+
self.associated_validations.each do |association_name, options|
|
67
|
+
next if skip_validation?(options)
|
68
|
+
validator = options[:validator].is_a?(Class) ? options[:validator].new : self.send(options[:validator])
|
69
|
+
children = entity.send(association_name)
|
70
|
+
if children.is_a?(Array)
|
71
|
+
validate_children(association_name, validator, children, errors)
|
72
|
+
elsif children
|
73
|
+
validate_child(association_name, validator, children, errors)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
self.custom_validations.each do |custom_validation|
|
77
|
+
if custom_validation.is_a?(Symbol)
|
78
|
+
self.send(custom_validation, entity, errors)
|
79
|
+
else # it's Proc
|
80
|
+
custom_validation.call(entity, errors)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
errors.to_hash
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate!(entity)
|
87
|
+
errors = validate(entity)
|
88
|
+
unless errors.empty?
|
89
|
+
raise AttrValidator::Errors::ValidationError.new("Validation Error", errors)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def validate_attr(attr_name, entity, validators)
|
96
|
+
attr_value = entity.send(attr_name)
|
97
|
+
error_messages = []
|
98
|
+
validators.each do |validator, validation_rule|
|
99
|
+
error_messages = validator.validate(attr_value, validation_rule)
|
100
|
+
break unless error_messages.empty?
|
101
|
+
end
|
102
|
+
error_messages
|
103
|
+
end
|
104
|
+
|
105
|
+
def skip_validation?(options)
|
106
|
+
if options[:if]
|
107
|
+
if options[:if].is_a?(Symbol)
|
108
|
+
true unless self.send(options[:if])
|
109
|
+
elsif options[:if].is_a?(Proc)
|
110
|
+
true unless self.instance_exec(&options[:if])
|
111
|
+
else
|
112
|
+
false
|
113
|
+
end
|
114
|
+
elsif options[:unless]
|
115
|
+
if options[:unless].is_a?(Symbol)
|
116
|
+
true if self.send(options[:unless])
|
117
|
+
elsif options[:unless].is_a?(Proc)
|
118
|
+
true if self.instance_exec(&options[:unless])
|
119
|
+
else
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_children(association_name, validator, children, errors)
|
126
|
+
if validator.respond_to?(:validate_all)
|
127
|
+
children_errors = validator.validate_all(children)
|
128
|
+
elsif validator.respond_to?(:validate)
|
129
|
+
children_errors = children.inject([]) do |errors, child|
|
130
|
+
errors << validator.validate(child).to_hash
|
131
|
+
end
|
132
|
+
else
|
133
|
+
raise NotImplementedError, "Validator should respond at least to :validate or :validate_all"
|
134
|
+
end
|
135
|
+
unless children_errors.all?(&:empty?)
|
136
|
+
errors.messages["#{association_name}_errors".to_sym] ||= []
|
137
|
+
errors.messages["#{association_name}_errors".to_sym] += children_errors
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def validate_child(association_name, validator, child, errors)
|
142
|
+
child_errors = validator.validate(child).to_hash
|
143
|
+
unless child_errors.empty?
|
144
|
+
errors.messages["#{association_name}_errors".to_sym] = child_errors
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class AttrValidator::Validators::EmailValidator
|
2
|
+
# This rule was adapted from https://github.com/emmanuel/aequitas/blob/master/lib/aequitas/rule/format/email_address.rb
|
3
|
+
EMAIL_ADDRESS = begin
|
4
|
+
letter = 'a-zA-Z'
|
5
|
+
digit = '0-9'
|
6
|
+
atext = "[#{letter}#{digit}\!\#\$\%\&\'\*+\/\=\?\^\_\`\{\|\}\~\-]"
|
7
|
+
dot_atom_text = "#{atext}+([.]#{atext}*)+"
|
8
|
+
dot_atom = dot_atom_text
|
9
|
+
no_ws_ctl = '\x01-\x08\x11\x12\x14-\x1f\x7f'
|
10
|
+
qtext = "[^#{no_ws_ctl}\\x0d\\x22\\x5c]" # Non-whitespace, non-control character except for \ and "
|
11
|
+
text = '[\x01-\x09\x11\x12\x14-\x7f]'
|
12
|
+
quoted_pair = "(\\x5c#{text})"
|
13
|
+
qcontent = "(?:#{qtext}|#{quoted_pair})"
|
14
|
+
quoted_string = "[\"]#{qcontent}+[\"]"
|
15
|
+
atom = "#{atext}+"
|
16
|
+
word = "(?:#{atom}|#{quoted_string})"
|
17
|
+
obs_local_part = "#{word}([.]#{word})*"
|
18
|
+
local_part = "(?:#{dot_atom}|#{quoted_string}|#{obs_local_part})"
|
19
|
+
dtext = "[#{no_ws_ctl}\\x21-\\x5a\\x5e-\\x7e]"
|
20
|
+
dcontent = "(?:#{dtext}|#{quoted_pair})"
|
21
|
+
domain_literal = "\\[#{dcontent}+\\]"
|
22
|
+
obs_domain = "#{atom}([.]#{atom})+"
|
23
|
+
domain = "(?:#{dot_atom}|#{domain_literal}|#{obs_domain})"
|
24
|
+
addr_spec = "#{local_part}\@#{domain}"
|
25
|
+
pattern = /\A#{addr_spec}\z/u
|
26
|
+
end
|
27
|
+
|
28
|
+
# Validates that value actually is email
|
29
|
+
# @param value [String] object to validate
|
30
|
+
# @param email_flag [Boolean] should be given string be email or not
|
31
|
+
# @return [Array] empty array if string is valid, array with error message otherwise
|
32
|
+
def self.validate(value, email_flag)
|
33
|
+
return [] if value.nil?
|
34
|
+
|
35
|
+
errors = []
|
36
|
+
if email_flag
|
37
|
+
errors << AttrValidator::I18n.t("errors.invalid_email") unless !!EMAIL_ADDRESS.match(value)
|
38
|
+
else
|
39
|
+
errors << AttrValidator::I18n.t("errors.can_not_be_email") if !!EMAIL_ADDRESS.match(value)
|
40
|
+
end
|
41
|
+
errors
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.validate_options(email_flag)
|
45
|
+
AttrValidator::ArgsValidator.is_boolean!(email_flag, :validation_rule)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class AttrValidator::Validators::ExclusionValidator
|
2
|
+
|
3
|
+
# Validates that value isn't in the specified list validation.in
|
4
|
+
# @param value Object object to validate exclusion
|
5
|
+
# @return Boolean true if object is excluded, false otherwise
|
6
|
+
def self.validate(value, options)
|
7
|
+
return [] if value.nil?
|
8
|
+
|
9
|
+
errors = []
|
10
|
+
if options[:in]
|
11
|
+
if options[:in].include?(value)
|
12
|
+
errors << (options[:message] || AttrValidator::I18n.t('errors.should_not_be_included_in_list', list: options[:in]))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
errors
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.validate_options(options)
|
19
|
+
AttrValidator::ArgsValidator.is_hash!(options, :validation_rule)
|
20
|
+
AttrValidator::ArgsValidator.has_only_allowed_keys!(options, [:in, :message], :validation_rule)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class AttrValidator::Validators::InclusionValidator
|
2
|
+
|
3
|
+
# Validates that given value inscluded in the specified list
|
4
|
+
# @param value [Object] object to validate
|
5
|
+
# @parm options [Hash] validation options, e.g. { in: [:small, :medium, :large], message: "not included in the list of allowed items" }
|
6
|
+
# where :in - list of allowed values,
|
7
|
+
# message - is a message to return if value is not included in the list
|
8
|
+
# @return Boolean true if object is included in the list, false otherwise
|
9
|
+
def self.validate(value, options)
|
10
|
+
return [] if value.nil?
|
11
|
+
|
12
|
+
errors = []
|
13
|
+
if options[:in]
|
14
|
+
unless options[:in].include?(value)
|
15
|
+
errors << (options[:message] || AttrValidator::I18n.t('errors.should_be_included_in_list', list: options[:in]))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
errors
|
19
|
+
end
|
20
|
+
|
21
|
+
# Validates that options specified in
|
22
|
+
# :inclusion are valid
|
23
|
+
def self.validate_options(options)
|
24
|
+
AttrValidator::ArgsValidator.is_hash!(options, 'validation options')
|
25
|
+
AttrValidator::ArgsValidator.has_only_allowed_keys!(options, [:in, :message], 'validation options')
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class AttrValidator::Validators::LengthValidator
|
2
|
+
|
3
|
+
# Validates that given object has correct length
|
4
|
+
# @param object [#length] object to validate
|
5
|
+
# @param options [Hash] validation options, e.g. { min: 2, max: 4, equal_to: 3, not_equal_to: 6 }
|
6
|
+
# @return [Array] empty array if object is valid, array of error messages otherwise
|
7
|
+
def self.validate(object, options)
|
8
|
+
return [] if object.nil?
|
9
|
+
|
10
|
+
errors = []
|
11
|
+
if options[:min]
|
12
|
+
errors << AttrValidator::I18n.t('errors.can_not_be_less_than', length: options[:min]) if object.length < options[:min]
|
13
|
+
end
|
14
|
+
if options[:max]
|
15
|
+
errors << AttrValidator::I18n.t('errors.can_not_be_more_than', length: options[:max]) if object.length > options[:max]
|
16
|
+
end
|
17
|
+
if options[:equal_to]
|
18
|
+
errors << AttrValidator::I18n.t('errors.should_be_equal_to', length: options[:equal_to]) if object.length != options[:equal_to]
|
19
|
+
end
|
20
|
+
if options[:not_equal_to]
|
21
|
+
errors << AttrValidator::I18n.t('errors.should_not_be_equal_to', length: options[:not_equal_to]) if object.length == options[:not_equal_to]
|
22
|
+
end
|
23
|
+
|
24
|
+
errors
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.validate_options(options)
|
28
|
+
AttrValidator::ArgsValidator.is_hash!(options, :validation_rule)
|
29
|
+
AttrValidator::ArgsValidator.has_only_allowed_keys!(options, [:min, :max, :equal_to, :not_equal_to], :validation_rule)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AttrValidator::Validators::NotNilValidator
|
2
|
+
|
3
|
+
# Validates that given object not nil
|
4
|
+
# @param value [Object] object to validate
|
5
|
+
# @param presence [Boolean] validation options, check presence or not
|
6
|
+
# @return [Array] empty array if object is valid, array of error messages otherwise
|
7
|
+
def self.validate(value, presence)
|
8
|
+
errors = []
|
9
|
+
if presence
|
10
|
+
if value.nil?
|
11
|
+
errors << AttrValidator::I18n.t('errors.can_not_be_nil')
|
12
|
+
end
|
13
|
+
else
|
14
|
+
if value
|
15
|
+
errors << AttrValidator::I18n.t('errors.should_be_nil')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
errors
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.validate_options(presence_flag)
|
22
|
+
AttrValidator::ArgsValidator.is_boolean!(presence_flag, :validation_rule)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class AttrValidator::Validators::NumericalityValidator
|
2
|
+
|
3
|
+
# Validates that +number+ satisfies all validation rules defined in +options+
|
4
|
+
# @param number [Numeric] number to validate
|
5
|
+
# @param options [Hash] validation rules
|
6
|
+
# @return [Array] empty array if number is valid, array of error messages otherwise
|
7
|
+
def self.validate(number, options)
|
8
|
+
return [] if number.nil?
|
9
|
+
|
10
|
+
errors = []
|
11
|
+
if options[:greater_than]
|
12
|
+
errors << AttrValidator::I18n.t('errors.should_be_greater_than', number: options[:greater_than]) if number <= options[:greater_than]
|
13
|
+
end
|
14
|
+
if options[:greater_than_or_equal_to]
|
15
|
+
errors << AttrValidator::I18n.t('errors.should_be_greater_than_or_equal_to', number: options[:greater_than_or_equal_to]) if number < options[:greater_than_or_equal_to]
|
16
|
+
end
|
17
|
+
if options[:less_than]
|
18
|
+
errors << AttrValidator::I18n.t('errors.should_be_less_than', number: options[:less_than]) if number >= options[:less_than]
|
19
|
+
end
|
20
|
+
if options[:less_than_or_equal_to]
|
21
|
+
errors << AttrValidator::I18n.t('errors.should_be_less_than_or_equal_to', number: options[:less_than_or_equal_to]) if number > options[:less_than_or_equal_to]
|
22
|
+
end
|
23
|
+
if options[:even]
|
24
|
+
errors << AttrValidator::I18n.t('errors.should_be_even') unless number.even?
|
25
|
+
end
|
26
|
+
if options[:odd]
|
27
|
+
errors << AttrValidator::I18n.t('errors.should_be_odd') unless number.odd?
|
28
|
+
end
|
29
|
+
errors
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.validate_options(options)
|
33
|
+
AttrValidator::ArgsValidator.is_hash!(options, :validation_rule)
|
34
|
+
AttrValidator::ArgsValidator.has_only_allowed_keys!(options, [
|
35
|
+
:greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to, :even, :odd
|
36
|
+
], :validation_rule)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class AttrValidator::Validators::PresenceValidator
|
2
|
+
|
3
|
+
# Validates that given object not nil and not empty
|
4
|
+
# if string is given strips it before validating
|
5
|
+
# @param value [Object] object to validate
|
6
|
+
# @param presence [Boolean] check presence or not
|
7
|
+
# @return [Array] empty array if number is valid, array of error messages otherwise
|
8
|
+
def self.validate(value, presence)
|
9
|
+
errors = []
|
10
|
+
if presence
|
11
|
+
if value.nil? || (value.is_a?(String) && value.strip.length == 0)
|
12
|
+
errors << AttrValidator::I18n.t('errors.can_not_be_blank')
|
13
|
+
end
|
14
|
+
else
|
15
|
+
if value
|
16
|
+
errors << AttrValidator::I18n.t('errors.should_be_blank')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
errors
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.validate_options(presence_flag)
|
23
|
+
AttrValidator::ArgsValidator.is_boolean!(presence_flag, :validation_rule)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class AttrValidator::Validators::RegexpValidator
|
2
|
+
|
3
|
+
# Validates that given value match regexp if regexp validation exists
|
4
|
+
# @param value String value to match with regexp
|
5
|
+
# @param regexp [Regexp] regexp to match
|
6
|
+
# @return [Array] empty array if number is valid, array of error messages otherwise
|
7
|
+
def self.validate(value, regexp)
|
8
|
+
return [] if value.nil?
|
9
|
+
|
10
|
+
errors = []
|
11
|
+
unless !!regexp.match(value)
|
12
|
+
errors << AttrValidator::I18n.t('errors.does_not_match')
|
13
|
+
end
|
14
|
+
errors
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.validate_options(regexp)
|
18
|
+
AttrValidator::ArgsValidator.is_string_or_regexp!(regexp, :validation_rule)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AttrValidator::Validators::UrlValidator
|
2
|
+
URL_REGEXP = /^(https?:\/\/)?([\w\.]+)\.([a-z]{2,6}\.?)(\/[\w\.]*)*\/?$/
|
3
|
+
|
4
|
+
# Validates that string is a valid url
|
5
|
+
# @param value [String] string to validate
|
6
|
+
# @param url [Boolean] should given string be url or not
|
7
|
+
# @return [Array] empty array if number is valid, array of error messages otherwise
|
8
|
+
def self.validate(value, url_flag)
|
9
|
+
return [] if value.nil?
|
10
|
+
|
11
|
+
errors = []
|
12
|
+
if url_flag
|
13
|
+
errors << AttrValidator::I18n.t('errors.invalid_url') unless !!URL_REGEXP.match(value)
|
14
|
+
else
|
15
|
+
errors << AttrValidator::I18n.t('errors.can_not_be_url') if !!URL_REGEXP.match(value)
|
16
|
+
end
|
17
|
+
|
18
|
+
errors
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.validate_options(url_flag)
|
22
|
+
AttrValidator::ArgsValidator.is_boolean!(url_flag, :validation_rule)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AttrValidator::Validators
|
2
|
+
require 'attr_validator/validators/exclusion_validator'
|
3
|
+
require 'attr_validator/validators/inclusion_validator'
|
4
|
+
require 'attr_validator/validators/email_validator'
|
5
|
+
require 'attr_validator/validators/url_validator'
|
6
|
+
require 'attr_validator/validators/length_validator'
|
7
|
+
require 'attr_validator/validators/numericality_validator'
|
8
|
+
require 'attr_validator/validators/presence_validator'
|
9
|
+
require 'attr_validator/validators/not_nil_validator'
|
10
|
+
require 'attr_validator/validators/regexp_validator'
|
11
|
+
end
|