poro_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.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +382 -0
- data/Rakefile +139 -0
- data/lib/poro_validator.rb +54 -0
- data/lib/poro_validator/configuration.rb +63 -0
- data/lib/poro_validator/error_store.rb +52 -0
- data/lib/poro_validator/errors.rb +56 -0
- data/lib/poro_validator/exceptions.rb +14 -0
- data/lib/poro_validator/validator.rb +82 -0
- data/lib/poro_validator/validator/base_class.rb +55 -0
- data/lib/poro_validator/validator/conditions.rb +71 -0
- data/lib/poro_validator/validator/context.rb +19 -0
- data/lib/poro_validator/validator/factory.rb +32 -0
- data/lib/poro_validator/validator/validation.rb +50 -0
- data/lib/poro_validator/validator/validations.rb +55 -0
- data/lib/poro_validator/validators/base_class.rb +53 -0
- data/lib/poro_validator/validators/exclusion_validator.rb +26 -0
- data/lib/poro_validator/validators/float_validator.rb +17 -0
- data/lib/poro_validator/validators/format_validator.rb +16 -0
- data/lib/poro_validator/validators/inclusion_validator.rb +26 -0
- data/lib/poro_validator/validators/integer_validator.rb +17 -0
- data/lib/poro_validator/validators/length_validator.rb +41 -0
- data/lib/poro_validator/validators/numeric_validator.rb +48 -0
- data/lib/poro_validator/validators/presence_validator.rb +23 -0
- data/lib/poro_validator/validators/range_array_validator.rb +19 -0
- data/lib/poro_validator/validators/with_validator.rb +21 -0
- data/lib/poro_validator/version.rb +3 -0
- data/poro_validator.gemspec +97 -0
- data/spec/features/composable_validations_spec.rb +26 -0
- data/spec/features/inheritable_spec.rb +29 -0
- data/spec/features/nested_validations_spec.rb +136 -0
- data/spec/lib/poro_validator/configuration_spec.rb +37 -0
- data/spec/lib/poro_validator/error_store_spec.rb +125 -0
- data/spec/lib/poro_validator/errors_spec.rb +79 -0
- data/spec/lib/poro_validator/validator/base_class_spec.rb +62 -0
- data/spec/lib/poro_validator/validator/conditions_spec.rb +112 -0
- data/spec/lib/poro_validator/validator/factory_spec.rb +23 -0
- data/spec/lib/poro_validator/validator/validation_spec.rb +69 -0
- data/spec/lib/poro_validator/validator/validations_spec.rb +34 -0
- data/spec/lib/poro_validator/validator_spec.rb +55 -0
- data/spec/lib/poro_validator/validators/base_class_spec.rb +11 -0
- data/spec/lib/poro_validator/validators/exclusion_validator_spec.rb +81 -0
- data/spec/lib/poro_validator/validators/float_validator_spec.rb +43 -0
- data/spec/lib/poro_validator/validators/format_validator_spec.rb +48 -0
- data/spec/lib/poro_validator/validators/inclusion_validator_spec.rb +81 -0
- data/spec/lib/poro_validator/validators/integer_validator_spec.rb +43 -0
- data/spec/lib/poro_validator/validators/length_validator_spec.rb +64 -0
- data/spec/lib/poro_validator/validators/numeric_validator_spec.rb +68 -0
- data/spec/lib/poro_validator/validators/presence_validator_spec.rb +52 -0
- data/spec/lib/poro_validator/validators/with_validator_spec.rb +90 -0
- data/spec/poro_validator_spec.rb +25 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/spec_helpers/concerns.rb +46 -0
- data/spec/support/spec_helpers/validator_test_macros.rb +99 -0
- metadata +199 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validator
|
3
|
+
class Context
|
4
|
+
attr_reader :entity, :context, :errors
|
5
|
+
|
6
|
+
# Initializes a new validator context
|
7
|
+
#
|
8
|
+
# @param entity [Object] The entity which to be validated
|
9
|
+
# @param context [self] The validating/validator class
|
10
|
+
# @param errors [Object] Error cache
|
11
|
+
|
12
|
+
def initialize(entity, context, errors)
|
13
|
+
@entity = entity
|
14
|
+
@context = context
|
15
|
+
@errors = errors
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validator
|
3
|
+
class Factory
|
4
|
+
class Validators
|
5
|
+
class << self
|
6
|
+
def set_validator(attr_name, validator, options={})
|
7
|
+
klass = class_name(validator)
|
8
|
+
begin
|
9
|
+
PoroValidator.const_get(klass).new(attr_name, options)
|
10
|
+
rescue NameError => e
|
11
|
+
raise(::PoroValidator::ValidatorNotFound.new(
|
12
|
+
"Validator not found: ::PoroValidator::#{klass} exception: #{e}"
|
13
|
+
))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def class_name(validator)
|
20
|
+
"Validators::#{camel_case(validator.to_s)}Validator"
|
21
|
+
end
|
22
|
+
|
23
|
+
def camel_case(str)
|
24
|
+
str.split('_').map do |char|
|
25
|
+
char.capitalize
|
26
|
+
end.join('')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validator
|
3
|
+
class Validation
|
4
|
+
# Builds a validation from the params passed which is initiated first
|
5
|
+
# then passed into the Validators.set_validator method
|
6
|
+
#
|
7
|
+
# @params[attr_name] - name of the attribute to be validated
|
8
|
+
# @params[validator] - validator class to be used for validating
|
9
|
+
# attribute
|
10
|
+
# @params[options] - arguments against validator and additional options
|
11
|
+
# e.g, message:, on:, etc.
|
12
|
+
def self.build(attr_name, validator, options)
|
13
|
+
b = new(attr_name, validator, options)
|
14
|
+
b = Factory::Validators.set_validator(b.attr_name,
|
15
|
+
b.validator,
|
16
|
+
b.options)
|
17
|
+
b
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(attr_name, validator, options = nil)
|
21
|
+
@attr_name = attr_name
|
22
|
+
@validator = validator
|
23
|
+
opts =
|
24
|
+
case options
|
25
|
+
when TrueClass
|
26
|
+
{}
|
27
|
+
when Hash
|
28
|
+
options
|
29
|
+
when Range, Array
|
30
|
+
{ in: options }
|
31
|
+
else
|
32
|
+
{ with: options }
|
33
|
+
end
|
34
|
+
@options = opts
|
35
|
+
end
|
36
|
+
|
37
|
+
def options
|
38
|
+
@options
|
39
|
+
end
|
40
|
+
|
41
|
+
def attr_name
|
42
|
+
@attr_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def validator
|
46
|
+
@validator
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validator
|
3
|
+
# @private
|
4
|
+
class Validations < BaseClass
|
5
|
+
def self.build_validations(validations = [])
|
6
|
+
inst = new
|
7
|
+
unless validations.empty?
|
8
|
+
validations.each do |validation|
|
9
|
+
inst << validation
|
10
|
+
end
|
11
|
+
end
|
12
|
+
inst
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(&block)
|
16
|
+
super()
|
17
|
+
instance_eval(&block) if block_given?
|
18
|
+
end
|
19
|
+
|
20
|
+
def build(attr, **options)
|
21
|
+
@validators = options.reject do |k,v|
|
22
|
+
[:if, :unless].include?(k)
|
23
|
+
end
|
24
|
+
|
25
|
+
@conditions = [] << options.select do |k,v|
|
26
|
+
[:if, :unless].include?(k)
|
27
|
+
end
|
28
|
+
|
29
|
+
@validators.each do |validator,options|
|
30
|
+
nested_conditions = {}
|
31
|
+
if options.is_a?(::Hash)
|
32
|
+
# Check if there are any nested conditions for a validator
|
33
|
+
# options and select if it exists.
|
34
|
+
#
|
35
|
+
# e.g
|
36
|
+
# validates :foo, format: { with: "hello", unless: :foo_rule }
|
37
|
+
nested_conditions = options.select do |k,v|
|
38
|
+
[:if, :unless].include?(k)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remove the nested conditions from the validator options.
|
42
|
+
unless nested_conditions.empty?
|
43
|
+
options.reject! { |k,v| [:if, :unless].include?(k) }
|
44
|
+
@conditions << nested_conditions
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
self << { validator: Validation.build(attr, validator, options),
|
49
|
+
conditions: @conditions.reject(&:empty?)
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class BaseClass
|
4
|
+
attr_writer :attribute
|
5
|
+
|
6
|
+
def initialize(attribute, options = {})
|
7
|
+
@attribute = attribute
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def options
|
12
|
+
@options
|
13
|
+
end
|
14
|
+
|
15
|
+
def attribute
|
16
|
+
@attribute
|
17
|
+
end
|
18
|
+
|
19
|
+
def errors
|
20
|
+
@errors ||= context.errors
|
21
|
+
end
|
22
|
+
|
23
|
+
def context
|
24
|
+
@context
|
25
|
+
end
|
26
|
+
|
27
|
+
def nested?
|
28
|
+
attribute.is_a?(::Array)
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(attribute, value, options)
|
32
|
+
raise ::PoroValidator::OverloadriddenRequired.new(
|
33
|
+
"This method needs to be overloaded/overriden."
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def value
|
38
|
+
if nested?
|
39
|
+
@value = attribute.flatten.inject(context.entity, :public_send)
|
40
|
+
else
|
41
|
+
@value = context.entity.public_send(attribute)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @private
|
46
|
+
def __validate__(validator_context)
|
47
|
+
@context = validator_context
|
48
|
+
@errors = context.errors
|
49
|
+
validate(attribute, value, options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class ExclusionValidator < RangeArrayValidator
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
in_option = options[:in]
|
6
|
+
|
7
|
+
unless validate_option(in_option)
|
8
|
+
raise ::PoroValidator::InvalidValidator,
|
9
|
+
[
|
10
|
+
"There was no range or array specified. Pass in a range or array.",
|
11
|
+
"e.g",
|
12
|
+
"validates :foo, exclusion: 1..10",
|
13
|
+
"or",
|
14
|
+
"validates :boo, exclusion: { in: 1..10 }"
|
15
|
+
].join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
message = options.fetch(:message, :exclusion)
|
19
|
+
|
20
|
+
if covered?(in_option, value) || included?(in_option, value)
|
21
|
+
errors.add(attribute, message, in_option)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class FloatValidator < BaseClass
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
message = options.fetch(:message, :float)
|
6
|
+
|
7
|
+
begin
|
8
|
+
Kernel.Float(value.to_s)
|
9
|
+
nil
|
10
|
+
rescue
|
11
|
+
errors.add(attribute, message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class FormatValidator < BaseClass
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
return if value.nil?
|
6
|
+
|
7
|
+
pattern = options.fetch(:with)
|
8
|
+
message = options.fetch(:message, :format)
|
9
|
+
|
10
|
+
unless value.to_s =~ pattern
|
11
|
+
errors.add(attribute, message, pattern)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class InclusionValidator < RangeArrayValidator
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
in_option = options[:in]
|
6
|
+
|
7
|
+
unless validate_option(in_option)
|
8
|
+
raise ::PoroValidator::InvalidValidator,
|
9
|
+
[
|
10
|
+
"There was no range or array specified. Pass in a range or array.",
|
11
|
+
"e.g",
|
12
|
+
"validates :foo, inclusion: 1..10",
|
13
|
+
"or",
|
14
|
+
"validates :boo, inclusion: { in: 1..10 }"
|
15
|
+
].join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
message = options.fetch(:message, :inclusion)
|
19
|
+
|
20
|
+
unless covered?(in_option, value) || included?(in_option, value)
|
21
|
+
errors.add(attribute, message, in_option)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class IntegerValidator < BaseClass
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
message = options.fetch(:message, :integer)
|
6
|
+
|
7
|
+
begin
|
8
|
+
Kernel.Integer(value.to_s)
|
9
|
+
nil
|
10
|
+
rescue
|
11
|
+
errors.add(attribute, message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class LengthValidator < BaseClass
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
message = options.fetch(:message, :length)
|
6
|
+
|
7
|
+
options.keys.each do |key|
|
8
|
+
matcher = matchers(key)
|
9
|
+
next if matcher.nil?
|
10
|
+
v = value.is_a?(::String) ? value : value.to_s
|
11
|
+
unless matchers(key).call(v, options[key])
|
12
|
+
errors.add(attribute, message, key => options[key])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def matchers(key)
|
20
|
+
m = {
|
21
|
+
extremum: lambda do |value, length|
|
22
|
+
value.length == length
|
23
|
+
end,
|
24
|
+
|
25
|
+
max: lambda do |value, length|
|
26
|
+
value.length <= length
|
27
|
+
end,
|
28
|
+
|
29
|
+
min: lambda do |value, length|
|
30
|
+
value.length >= length
|
31
|
+
end,
|
32
|
+
|
33
|
+
in: lambda do |value, length|
|
34
|
+
length.cover?(value.length)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
m.fetch(key, nil)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class NumericValidator < BaseClass
|
4
|
+
def validate(attribute, value, options)
|
5
|
+
message = options.fetch(:message, :numeric)
|
6
|
+
|
7
|
+
begin
|
8
|
+
Kernel.Integer(value.to_s)
|
9
|
+
nil
|
10
|
+
rescue
|
11
|
+
errors.add(attribute, :integer)
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
options.keys.each do |key|
|
16
|
+
matcher = matchers(key)
|
17
|
+
next if matcher.nil?
|
18
|
+
unless matchers(key).call(value, options[key])
|
19
|
+
errors.add(attribute, message, key => options[key])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def matchers(key)
|
27
|
+
m = {
|
28
|
+
extremum: lambda do |value, length|
|
29
|
+
value == length
|
30
|
+
end,
|
31
|
+
|
32
|
+
max: lambda do |value, length|
|
33
|
+
value <= length
|
34
|
+
end,
|
35
|
+
|
36
|
+
min: lambda do |value, length|
|
37
|
+
value >= length
|
38
|
+
end,
|
39
|
+
|
40
|
+
in: lambda do |value, length|
|
41
|
+
length.cover?(value)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
m.fetch(key, nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PoroValidator
|
2
|
+
module Validators
|
3
|
+
class PresenceValidator < BaseClass
|
4
|
+
BLANK_STRING_MATCHER = /\A[[:space:]]*\z/.freeze
|
5
|
+
|
6
|
+
def validate(attribute, value, options)
|
7
|
+
allow_blank = options.fetch(:allow_blank, false)
|
8
|
+
message = options.fetch(:message, :presence)
|
9
|
+
|
10
|
+
if value.is_a?(::String)
|
11
|
+
if value.gsub(/\s+/, '').match(BLANK_STRING_MATCHER)
|
12
|
+
errors.add(attribute, message) unless allow_blank
|
13
|
+
return
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
18
|
+
errors.add(attribute, message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|