pure_validator 0.3.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +47 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +139 -0
  8. data/Rakefile +1 -0
  9. data/bin/console +14 -0
  10. data/lib/pure_validator/args_validator.rb +107 -0
  11. data/lib/pure_validator/concern.rb +136 -0
  12. data/lib/pure_validator/core_extensions/class_attribute.rb +143 -0
  13. data/lib/pure_validator/core_extensions/humanize.rb +44 -0
  14. data/lib/pure_validator/errors.rb +23 -0
  15. data/lib/pure_validator/i18n.rb +7 -0
  16. data/lib/pure_validator/locales/en.yml +24 -0
  17. data/lib/pure_validator/locales/ru.yml +24 -0
  18. data/lib/pure_validator/validation_errors.rb +248 -0
  19. data/lib/pure_validator/validator.rb +150 -0
  20. data/lib/pure_validator/validators/email_validator.rb +48 -0
  21. data/lib/pure_validator/validators/exclusion_validator.rb +22 -0
  22. data/lib/pure_validator/validators/inclusion_validator.rb +27 -0
  23. data/lib/pure_validator/validators/length_validator.rb +32 -0
  24. data/lib/pure_validator/validators/not_nil_validator.rb +25 -0
  25. data/lib/pure_validator/validators/numericality_validator.rb +39 -0
  26. data/lib/pure_validator/validators/presence_validator.rb +26 -0
  27. data/lib/pure_validator/validators/regexp_validator.rb +21 -0
  28. data/lib/pure_validator/validators/url_validator.rb +25 -0
  29. data/lib/pure_validator/validators.rb +11 -0
  30. data/lib/pure_validator/version.rb +3 -0
  31. data/lib/pure_validator.rb +43 -0
  32. data/pure_validator.gemspec +26 -0
  33. data/spec/pure_validator/args_validator_spec.rb +169 -0
  34. data/spec/pure_validator/errors_spec.rb +10 -0
  35. data/spec/pure_validator/validation_errors_spec.rb +109 -0
  36. data/spec/pure_validator/validator_spec.rb +234 -0
  37. data/spec/pure_validator/validators/email_validator_spec.rb +35 -0
  38. data/spec/pure_validator/validators/exclusion_validator_spec.rb +23 -0
  39. data/spec/pure_validator/validators/inclusion_validator_spec.rb +23 -0
  40. data/spec/pure_validator/validators/length_validator_spec.rb +38 -0
  41. data/spec/pure_validator/validators/not_nil_validator_spec.rb +44 -0
  42. data/spec/pure_validator/validators/numericality_validator_spec.rb +49 -0
  43. data/spec/pure_validator/validators/presence_validator_spec.rb +38 -0
  44. data/spec/pure_validator/validators/regexp_validator_spec.rb +23 -0
  45. data/spec/pure_validator/validators/url_validator_spec.rb +35 -0
  46. data/spec/spec_helper.rb +21 -0
  47. metadata +175 -0
@@ -0,0 +1,23 @@
1
+ module PureValidator::Errors
2
+ # Thrown when object has validation errors
3
+ class ValidationError < StandardError
4
+ attr_reader :errors
5
+
6
+ def initialize(message, errors)
7
+ @errors = errors
8
+ super(message)
9
+ end
10
+
11
+ def message
12
+ "#{@message}\n#{errors}"
13
+ end
14
+
15
+ def short_message
16
+ 'Validation error'
17
+ end
18
+ end
19
+
20
+
21
+ # Thrown when validator is not defined
22
+ class MissingValidatorError < StandardError; end
23
+ end
@@ -0,0 +1,7 @@
1
+ class PureValidator::I18n
2
+
3
+ # Translates message to specific language
4
+ def self.t(message, options = {})
5
+ I18n.t("pure_validator.#{message}", options)
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ en:
2
+ pure_validator:
3
+ errors:
4
+ invalid_email: "invalid email"
5
+ can_not_be_email: "can't be email"
6
+ should_not_be_included_in_list: "should not be included in %{list}"
7
+ should_be_included_in_list: "should be included in %{list}"
8
+ can_not_be_less_than: "can not be less than %{length}"
9
+ can_not_be_more_than: "can not be more than %{length}"
10
+ should_be_equal_to: "should be equal to %{length}"
11
+ should_not_be_equal_to: "should not be equal to %{length}"
12
+ can_not_be_nil: "can not be nil"
13
+ should_be_nil: "should be nil"
14
+ should_be_greater_than: "should be greater than %{number}"
15
+ should_be_greater_than_or_equal_to: "should be greater than or equal to %{number}"
16
+ should_be_less_than: "should be less than %{number}"
17
+ should_be_less_than_or_equal_to: "should be less than or equal to %{number}"
18
+ should_be_even: "should be even number"
19
+ should_be_odd: "should be odd number"
20
+ can_not_be_blank: "can not be blank"
21
+ should_be_blank: "should be blank"
22
+ does_not_match: "does not match defined format"
23
+ invalid_url: "invalid url"
24
+ can_not_be_url: "can not be a url"
@@ -0,0 +1,24 @@
1
+ ru:
2
+ pure_validator:
3
+ errors:
4
+ invalid_email: "невалидный электронный адрес"
5
+ can_not_be_email: "не может быть адресом элекронной почты"
6
+ should_not_be_included_in_list: "не должен быть включен в список %{list}"
7
+ should_be_included_in_list: "должен быть включен в список %{list}"
8
+ can_not_be_less_than: "не может быть меньше чем %{length}"
9
+ can_not_be_more_than: "не может быть больше чем %{length}"
10
+ should_be_equal_to: "должен быть равен %{length}"
11
+ should_not_be_equal_to: "не должен быть равен %{length}"
12
+ can_not_be_nil: "не может быть пустым"
13
+ should_be_nil: "должен быть пустым"
14
+ should_be_greater_than: "должен быть больше чем %{number}"
15
+ should_be_greater_than_or_equal_to: "должен быть больше либо равен %{number}"
16
+ should_be_less_than: "должен быть меньше чем %{number}"
17
+ should_be_less_than_or_equal_to: "должен быть меньше либо равен %{number}"
18
+ should_be_even: "должен быть четным числом"
19
+ should_be_odd: "должен быть нечетным числом"
20
+ can_not_be_blank: "не может быть пустым"
21
+ should_be_blank: "должен быть пустым"
22
+ does_not_match: "неверный формат"
23
+ invalid_url: "невалидный адресс"
24
+ can_not_be_url: "не может быть адресом"
@@ -0,0 +1,248 @@
1
+ class PureValidator::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
+ messages.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 = humanize(attribute.to_s.tr('.', '_'))
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
+ def humanize(value, options = {})
246
+ PureValidator::Humanize.humanize(value, options)
247
+ end
248
+ end
@@ -0,0 +1,150 @@
1
+ module PureValidator::Validator
2
+ extend PureValidator::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
+ PureValidator::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
+ PureValidator::ArgsValidator.not_nil!(options[:validator], :validator)
21
+ PureValidator::ArgsValidator.is_class_or_symbol!(options[:validator], :validator)
22
+ PureValidator::ArgsValidator.is_symbol_or_block!(options[:if], :if) if options[:if]
23
+ PureValidator::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
+ PureValidator::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 = PureValidator.validators[validator_name]
47
+ unless validator
48
+ raise PureValidator::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 = PureValidator::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 PureValidator::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
+ return unless options[:if] || options[:unless]
107
+ return handle_if_skip_validation(options) if options[:if]
108
+ return handle_unless_skip_validation(options) if options[:unless]
109
+ end
110
+
111
+ def handle_if_skip_validation(options)
112
+ if options[:if].is_a?(Symbol)
113
+ return ! self.send(options[:if])
114
+ elsif options[:if].is_a?(Proc)
115
+ return ! self.instance_exec(&options[:if])
116
+ end
117
+ end
118
+
119
+ def handle_unless_skip_validation(options)
120
+ if options[:unless].is_a?(Symbol)
121
+ return self.send(options[:unless])
122
+ elsif options[:unless].is_a?(Proc)
123
+ return self.instance_exec(&options[:unless])
124
+ end
125
+ end
126
+
127
+ def validate_children(association_name, validator, children, errors)
128
+ if validator.respond_to?(:validate_all)
129
+ children_errors = validator.validate_all(children)
130
+ elsif validator.respond_to?(:validate)
131
+ children_errors = children.inject([]) do |errors, child|
132
+ errors << validator.validate(child).to_hash
133
+ end
134
+ else
135
+ raise NotImplementedError, "Validator should respond at least to :validate or :validate_all"
136
+ end
137
+ unless children_errors.all?(&:empty?)
138
+ errors.messages["#{association_name}_errors".to_sym] ||= []
139
+ errors.messages["#{association_name}_errors".to_sym] += children_errors
140
+ end
141
+ end
142
+
143
+ def validate_child(association_name, validator, child, errors)
144
+ child_errors = validator.validate(child).to_hash
145
+ unless child_errors.empty?
146
+ errors.messages["#{association_name}_errors".to_sym] = child_errors
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,48 @@
1
+ class PureValidator::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 << PureValidator::I18n.t("errors.invalid_email") unless !!EMAIL_ADDRESS.match(value)
38
+ else
39
+ errors << PureValidator::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
+ PureValidator::ArgsValidator.is_boolean!(email_flag, :validation_rule)
46
+ end
47
+
48
+ end
@@ -0,0 +1,22 @@
1
+ class PureValidator::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].include?(value)
11
+ errors << (options[:message] || PureValidator::I18n.t('errors.should_not_be_included_in_list', list: options[:in]))
12
+ end
13
+ errors
14
+ end
15
+
16
+ def self.validate_options(options)
17
+ PureValidator::ArgsValidator.is_hash!(options, :validation_rule)
18
+ PureValidator::ArgsValidator.has_key!(options, :in)
19
+ PureValidator::ArgsValidator.has_only_allowed_keys!(options, [:in, :message], :validation_rule)
20
+ end
21
+
22
+ end
@@ -0,0 +1,27 @@
1
+ class PureValidator::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
+ unless options[:in].include?(value)
14
+ errors << (options[:message] || PureValidator::I18n.t('errors.should_be_included_in_list', list: options[:in]))
15
+ end
16
+ errors
17
+ end
18
+
19
+ # Validates that options specified in
20
+ # :inclusion are valid
21
+ def self.validate_options(options)
22
+ PureValidator::ArgsValidator.is_hash!(options, 'validation options')
23
+ PureValidator::ArgsValidator.has_key!(options, :in)
24
+ PureValidator::ArgsValidator.has_only_allowed_keys!(options, [:in, :message], 'validation options')
25
+ end
26
+
27
+ end
@@ -0,0 +1,32 @@
1
+ class PureValidator::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 << PureValidator::I18n.t('errors.can_not_be_less_than', length: options[:min]) if object.length < options[:min]
13
+ end
14
+ if options[:max]
15
+ errors << PureValidator::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 << PureValidator::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 << PureValidator::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
+ PureValidator::ArgsValidator.is_hash!(options, :validation_rule)
29
+ PureValidator::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 PureValidator::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 << PureValidator::I18n.t('errors.can_not_be_nil')
12
+ end
13
+ else
14
+ if value
15
+ errors << PureValidator::I18n.t('errors.should_be_nil')
16
+ end
17
+ end
18
+ errors
19
+ end
20
+
21
+ def self.validate_options(presence_flag)
22
+ PureValidator::ArgsValidator.is_boolean!(presence_flag, :validation_rule)
23
+ end
24
+
25
+ end
@@ -0,0 +1,39 @@
1
+ class PureValidator::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 << PureValidator::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 << PureValidator::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 << PureValidator::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 << PureValidator::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 << PureValidator::I18n.t('errors.should_be_even') unless number.even?
25
+ end
26
+ if options[:odd]
27
+ errors << PureValidator::I18n.t('errors.should_be_odd') unless number.odd?
28
+ end
29
+ errors
30
+ end
31
+
32
+ def self.validate_options(options)
33
+ PureValidator::ArgsValidator.is_hash!(options, :validation_rule)
34
+ PureValidator::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