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.
Files changed (40) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +37 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +119 -0
  7. data/Rakefile +1 -0
  8. data/attr_validator.gemspec +24 -0
  9. data/lib/attr_validator/args_validator.rb +118 -0
  10. data/lib/attr_validator/concern.rb +136 -0
  11. data/lib/attr_validator/core_extensions/class_attribute.rb +143 -0
  12. data/lib/attr_validator/errors.rb +23 -0
  13. data/lib/attr_validator/i18n.rb +7 -0
  14. data/lib/attr_validator/locales/en.yml +23 -0
  15. data/lib/attr_validator/validation_errors.rb +245 -0
  16. data/lib/attr_validator/validator.rb +148 -0
  17. data/lib/attr_validator/validators/email_validator.rb +48 -0
  18. data/lib/attr_validator/validators/exclusion_validator.rb +23 -0
  19. data/lib/attr_validator/validators/inclusion_validator.rb +28 -0
  20. data/lib/attr_validator/validators/length_validator.rb +32 -0
  21. data/lib/attr_validator/validators/not_nil_validator.rb +25 -0
  22. data/lib/attr_validator/validators/numericality_validator.rb +39 -0
  23. data/lib/attr_validator/validators/presence_validator.rb +26 -0
  24. data/lib/attr_validator/validators/regexp_validator.rb +21 -0
  25. data/lib/attr_validator/validators/url_validator.rb +25 -0
  26. data/lib/attr_validator/validators.rb +11 -0
  27. data/lib/attr_validator/version.rb +3 -0
  28. data/lib/attr_validator.rb +41 -0
  29. data/spec/attr_validator/validator_spec.rb +105 -0
  30. data/spec/attr_validator/validators/email_validator_spec.rb +24 -0
  31. data/spec/attr_validator/validators/exclusion_validator_spec.rb +24 -0
  32. data/spec/attr_validator/validators/inclusion_validator_spec.rb +24 -0
  33. data/spec/attr_validator/validators/length_validator_spec.rb +39 -0
  34. data/spec/attr_validator/validators/not_nil_validator_spec.rb +25 -0
  35. data/spec/attr_validator/validators/numericality_validator_spec.rb +50 -0
  36. data/spec/attr_validator/validators/presence_validator_spec.rb +25 -0
  37. data/spec/attr_validator/validators/regexp_validator_spec.rb +24 -0
  38. data/spec/attr_validator/validators/url_validator_spec.rb +24 -0
  39. data/spec/spec_helper.rb +7 -0
  40. 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
@@ -0,0 +1,3 @@
1
+ module AttrValidator
2
+ VERSION = "0.0.1"
3
+ end