hash_validator 1.2.0 → 2.0.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.
- checksums.yaml +4 -4
- data/README.md +173 -12
- data/lib/hash_validator/configuration.rb +16 -0
- data/lib/hash_validator/validators/alpha_validator.rb +4 -12
- data/lib/hash_validator/validators/alphanumeric_validator.rb +4 -12
- data/lib/hash_validator/validators/array_validator.rb +1 -1
- data/lib/hash_validator/validators/base.rb +28 -3
- data/lib/hash_validator/validators/boolean_validator.rb +3 -5
- data/lib/hash_validator/validators/class_validator.rb +1 -1
- data/lib/hash_validator/validators/digits_validator.rb +4 -12
- data/lib/hash_validator/validators/dynamic_func_validator.rb +26 -0
- data/lib/hash_validator/validators/dynamic_pattern_validator.rb +23 -0
- data/lib/hash_validator/validators/email_validator.rb +4 -6
- data/lib/hash_validator/validators/enumerable_validator.rb +4 -6
- data/lib/hash_validator/validators/hash_validator.rb +2 -2
- data/lib/hash_validator/validators/hex_color_validator.rb +4 -12
- data/lib/hash_validator/validators/ip_validator.rb +22 -0
- data/lib/hash_validator/validators/ipv4_validator.rb +18 -0
- data/lib/hash_validator/validators/ipv6_validator.rb +22 -0
- data/lib/hash_validator/validators/json_validator.rb +4 -11
- data/lib/hash_validator/validators/lambda_validator.rb +5 -8
- data/lib/hash_validator/validators/many_validator.rb +3 -3
- data/lib/hash_validator/validators/multiple_validator.rb +1 -1
- data/lib/hash_validator/validators/optional_validator.rb +1 -1
- data/lib/hash_validator/validators/presence_validator.rb +4 -6
- data/lib/hash_validator/validators/regex_validator.rb +4 -6
- data/lib/hash_validator/validators/simple_type_validators.rb +1 -1
- data/lib/hash_validator/validators/simple_validator.rb +2 -4
- data/lib/hash_validator/validators/url_validator.rb +4 -11
- data/lib/hash_validator/validators.rb +40 -4
- data/lib/hash_validator/version.rb +1 -1
- data/lib/hash_validator.rb +1 -0
- data/spec/configuration_spec.rb +189 -0
- data/spec/hash_validator_spec.rb +4 -4
- data/spec/validators/base_spec.rb +2 -2
- data/spec/validators/dynamic_func_validator_spec.rb +252 -0
- data/spec/validators/dynamic_pattern_validator_spec.rb +150 -0
- data/spec/validators/ip_validator_spec.rb +105 -0
- data/spec/validators/ipv4_validator_spec.rb +99 -0
- data/spec/validators/ipv6_validator_spec.rb +99 -0
- data/spec/validators/user_defined_spec.rb +2 -2
- metadata +20 -3
- data/Plan.md +0 -309
@@ -15,21 +15,18 @@ class HashValidator::Validator::LambdaValidator < HashValidator::Validator::Base
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def error_message
|
19
19
|
'is not valid'
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
24
|
-
errors[key] = presence_error_message
|
25
|
-
end
|
26
|
-
|
22
|
+
def valid?(value, lambda)
|
23
|
+
lambda.call(value)
|
27
24
|
rescue
|
28
|
-
|
25
|
+
false
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
32
29
|
class HashValidator::Validator::LambdaValidator::InvalidArgumentCount < StandardError
|
33
30
|
end
|
34
31
|
|
35
|
-
HashValidator.
|
32
|
+
HashValidator.add_validator(HashValidator::Validator::LambdaValidator.new)
|
@@ -9,13 +9,13 @@ module HashValidator
|
|
9
9
|
validation.is_a?(Validations::Many)
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def error_message
|
13
13
|
"enumerable required"
|
14
14
|
end
|
15
15
|
|
16
16
|
def validate(key, value, validations, errors)
|
17
17
|
unless value.is_a?(Enumerable)
|
18
|
-
errors[key] =
|
18
|
+
errors[key] = error_message
|
19
19
|
return
|
20
20
|
end
|
21
21
|
|
@@ -37,4 +37,4 @@ module HashValidator
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
HashValidator.
|
40
|
+
HashValidator.add_validator(HashValidator::Validator::ManyValidator.new)
|
@@ -3,15 +3,13 @@ class HashValidator::Validator::PresenceValidator < HashValidator::Validator::Ba
|
|
3
3
|
super('required')
|
4
4
|
end
|
5
5
|
|
6
|
-
def
|
6
|
+
def error_message
|
7
7
|
'is required'
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
errors[key] = presence_error_message
|
13
|
-
end
|
10
|
+
def valid?(value)
|
11
|
+
!value.nil?
|
14
12
|
end
|
15
13
|
end
|
16
14
|
|
17
|
-
HashValidator.
|
15
|
+
HashValidator.add_validator(HashValidator::Validator::PresenceValidator.new)
|
@@ -7,15 +7,13 @@ class HashValidator::Validator::RegexpValidator < HashValidator::Validator::Base
|
|
7
7
|
rhs.is_a?(Regexp)
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def error_message
|
11
11
|
'does not match regular expression'
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
errors[key] = presence_error_message
|
17
|
-
end
|
14
|
+
def valid?(value, regexp)
|
15
|
+
regexp.match(value.to_s)
|
18
16
|
end
|
19
17
|
end
|
20
18
|
|
21
|
-
HashValidator.
|
19
|
+
HashValidator.add_validator(HashValidator::Validator::RegexpValidator.new)
|
@@ -13,5 +13,5 @@
|
|
13
13
|
Time
|
14
14
|
].each do |type|
|
15
15
|
name = type.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase # ActiveSupport/Inflector#underscore behaviour
|
16
|
-
HashValidator.
|
16
|
+
HashValidator.add_validator(HashValidator::Validator::SimpleValidator.new(name, lambda { |v| v.is_a?(type) }))
|
17
17
|
end
|
@@ -12,9 +12,7 @@ class HashValidator::Validator::SimpleValidator < HashValidator::Validator::Base
|
|
12
12
|
self.lambda = lambda
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
errors[key] = presence_error_message
|
18
|
-
end
|
15
|
+
def valid?(value)
|
16
|
+
lambda.call(value)
|
19
17
|
end
|
20
18
|
end
|
@@ -5,19 +5,12 @@ class HashValidator::Validator::UrlValidator < HashValidator::Validator::Base
|
|
5
5
|
super('url') # The name of the validator
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def error_message
|
9
9
|
'is not a valid URL'
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
unless value.is_a?(String)
|
14
|
-
errors[key] = presence_error_message
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def valid_url?(value)
|
12
|
+
def valid?(value)
|
13
|
+
return false unless value.is_a?(String)
|
21
14
|
uri = URI.parse(value)
|
22
15
|
%w[http https ftp].include?(uri.scheme)
|
23
16
|
rescue URI::InvalidURIError
|
@@ -25,4 +18,4 @@ class HashValidator::Validator::UrlValidator < HashValidator::Validator::Base
|
|
25
18
|
end
|
26
19
|
end
|
27
20
|
|
28
|
-
HashValidator.
|
21
|
+
HashValidator.add_validator(HashValidator::Validator::UrlValidator.new)
|
@@ -2,9 +2,40 @@ module HashValidator
|
|
2
2
|
@@validators = []
|
3
3
|
|
4
4
|
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
5
|
+
def self.remove_validator(name)
|
6
|
+
name = name.to_s
|
7
|
+
@@validators.reject! { |v| v.name == name }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.add_validator(*args)
|
11
|
+
validator = case args.length
|
12
|
+
when 1
|
13
|
+
# Instance-based validator (existing behavior)
|
14
|
+
validator_instance = args[0]
|
15
|
+
unless validator_instance.is_a?(HashValidator::Validator::Base)
|
16
|
+
raise StandardError.new('validators need to inherit from HashValidator::Validator::Base')
|
17
|
+
end
|
18
|
+
validator_instance
|
19
|
+
when 2
|
20
|
+
# Dynamic validator with options
|
21
|
+
name = args[0]
|
22
|
+
options = args[1]
|
23
|
+
|
24
|
+
if options.is_a?(Hash)
|
25
|
+
if options[:pattern]
|
26
|
+
# Pattern-based validator
|
27
|
+
HashValidator::Validator::DynamicPatternValidator.new(name, options[:pattern], options[:error_message])
|
28
|
+
elsif options[:func]
|
29
|
+
# Function-based validator
|
30
|
+
HashValidator::Validator::DynamicFuncValidator.new(name, options[:func], options[:error_message])
|
31
|
+
else
|
32
|
+
raise ArgumentError, 'Options hash must contain either :pattern or :func key'
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise ArgumentError, 'Second argument must be an options hash with :pattern or :func key'
|
36
|
+
end
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'add_validator expects 1 argument (validator instance) or 2 arguments (name, options)'
|
8
39
|
end
|
9
40
|
|
10
41
|
if @@validators.detect { |v| v.name == validator.name }
|
@@ -24,6 +55,8 @@ end
|
|
24
55
|
|
25
56
|
# Load validators
|
26
57
|
require 'hash_validator/validators/base'
|
58
|
+
require 'hash_validator/validators/dynamic_pattern_validator'
|
59
|
+
require 'hash_validator/validators/dynamic_func_validator'
|
27
60
|
require 'hash_validator/validators/simple_validator'
|
28
61
|
require 'hash_validator/validators/class_validator'
|
29
62
|
require 'hash_validator/validators/hash_validator'
|
@@ -43,4 +76,7 @@ require 'hash_validator/validators/json_validator'
|
|
43
76
|
require 'hash_validator/validators/hex_color_validator'
|
44
77
|
require 'hash_validator/validators/alphanumeric_validator'
|
45
78
|
require 'hash_validator/validators/alpha_validator'
|
46
|
-
require 'hash_validator/validators/digits_validator'
|
79
|
+
require 'hash_validator/validators/digits_validator'
|
80
|
+
require 'hash_validator/validators/ipv4_validator'
|
81
|
+
require 'hash_validator/validators/ipv6_validator'
|
82
|
+
require 'hash_validator/validators/ip_validator'
|
data/lib/hash_validator.rb
CHANGED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'HashValidator.configure' do
|
4
|
+
describe 'configuration DSL' do
|
5
|
+
after(:each) do
|
6
|
+
# Clean up any validators added during tests
|
7
|
+
HashValidator.remove_validator('test_pattern')
|
8
|
+
HashValidator.remove_validator('test_func')
|
9
|
+
HashValidator.remove_validator('bulk_odd')
|
10
|
+
HashValidator.remove_validator('bulk_even')
|
11
|
+
HashValidator.remove_validator('bulk_palindrome')
|
12
|
+
HashValidator.remove_validator('custom_configured')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'allows adding instance-based validators' do
|
16
|
+
class HashValidator::Validator::CustomConfiguredValidator < HashValidator::Validator::Base
|
17
|
+
def initialize
|
18
|
+
super('custom_configured')
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?(value)
|
22
|
+
value == 'configured'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
HashValidator.configure do |config|
|
27
|
+
config.add_validator HashValidator::Validator::CustomConfiguredValidator.new
|
28
|
+
end
|
29
|
+
|
30
|
+
validator = HashValidator.validate(
|
31
|
+
{ status: 'configured' },
|
32
|
+
{ status: 'custom_configured' }
|
33
|
+
)
|
34
|
+
expect(validator.valid?).to eq true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'allows adding pattern-based validators' do
|
38
|
+
HashValidator.configure do |config|
|
39
|
+
config.add_validator 'test_pattern',
|
40
|
+
pattern: /\A[A-Z]{3}\z/,
|
41
|
+
error_message: 'must be three uppercase letters'
|
42
|
+
end
|
43
|
+
|
44
|
+
validator = HashValidator.validate(
|
45
|
+
{ code: 'ABC' },
|
46
|
+
{ code: 'test_pattern' }
|
47
|
+
)
|
48
|
+
expect(validator.valid?).to eq true
|
49
|
+
|
50
|
+
validator = HashValidator.validate(
|
51
|
+
{ code: 'abc' },
|
52
|
+
{ code: 'test_pattern' }
|
53
|
+
)
|
54
|
+
expect(validator.valid?).to eq false
|
55
|
+
expect(validator.errors).to eq({ code: 'must be three uppercase letters' })
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'allows adding function-based validators' do
|
59
|
+
HashValidator.configure do |config|
|
60
|
+
config.add_validator 'test_func',
|
61
|
+
func: ->(v) { v.is_a?(Integer) && v > 100 },
|
62
|
+
error_message: 'must be an integer greater than 100'
|
63
|
+
end
|
64
|
+
|
65
|
+
validator = HashValidator.validate(
|
66
|
+
{ score: 150 },
|
67
|
+
{ score: 'test_func' }
|
68
|
+
)
|
69
|
+
expect(validator.valid?).to eq true
|
70
|
+
|
71
|
+
validator = HashValidator.validate(
|
72
|
+
{ score: 50 },
|
73
|
+
{ score: 'test_func' }
|
74
|
+
)
|
75
|
+
expect(validator.valid?).to eq false
|
76
|
+
expect(validator.errors).to eq({ score: 'must be an integer greater than 100' })
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'allows adding multiple validators in one configure block' do
|
80
|
+
HashValidator.configure do |config|
|
81
|
+
config.add_validator 'bulk_odd',
|
82
|
+
pattern: /\A\d*[13579]\z/,
|
83
|
+
error_message: 'must be odd'
|
84
|
+
|
85
|
+
config.add_validator 'bulk_even',
|
86
|
+
pattern: /\A\d*[02468]\z/,
|
87
|
+
error_message: 'must be even'
|
88
|
+
|
89
|
+
config.add_validator 'bulk_palindrome',
|
90
|
+
func: proc { |s| s.to_s == s.to_s.reverse },
|
91
|
+
error_message: 'must be a palindrome'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Test odd validator
|
95
|
+
validator = HashValidator.validate(
|
96
|
+
{ num: '13' },
|
97
|
+
{ num: 'bulk_odd' }
|
98
|
+
)
|
99
|
+
expect(validator.valid?).to eq true
|
100
|
+
|
101
|
+
# Test even validator
|
102
|
+
validator = HashValidator.validate(
|
103
|
+
{ num: '24' },
|
104
|
+
{ num: 'bulk_even' }
|
105
|
+
)
|
106
|
+
expect(validator.valid?).to eq true
|
107
|
+
|
108
|
+
# Test palindrome validator
|
109
|
+
validator = HashValidator.validate(
|
110
|
+
{ word: 'level' },
|
111
|
+
{ word: 'bulk_palindrome' }
|
112
|
+
)
|
113
|
+
expect(validator.valid?).to eq true
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'allows removing validators within configure block' do
|
117
|
+
# First add a validator
|
118
|
+
HashValidator.add_validator 'test_pattern',
|
119
|
+
pattern: /test/,
|
120
|
+
error_message: 'test'
|
121
|
+
|
122
|
+
# Then remove it in configure
|
123
|
+
HashValidator.configure do |config|
|
124
|
+
config.remove_validator 'test_pattern'
|
125
|
+
end
|
126
|
+
|
127
|
+
# Should raise error as validator no longer exists
|
128
|
+
expect {
|
129
|
+
HashValidator.validate({ value: 'test' }, { value: 'test_pattern' })
|
130
|
+
}.to raise_error(StandardError, /Could not find valid validator/)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'works without a block' do
|
134
|
+
expect {
|
135
|
+
HashValidator.configure
|
136
|
+
}.not_to raise_error
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'yields a Configuration instance' do
|
140
|
+
HashValidator.configure do |config|
|
141
|
+
expect(config).to be_a(HashValidator::Configuration)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'Rails-style initializer example' do
|
147
|
+
after(:each) do
|
148
|
+
HashValidator.remove_validator('phone')
|
149
|
+
HashValidator.remove_validator('postal_code')
|
150
|
+
HashValidator.remove_validator('age_range')
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'can be used like a Rails initializer' do
|
154
|
+
# This would typically be in config/initializers/hash_validator.rb
|
155
|
+
HashValidator.configure do |config|
|
156
|
+
# Add pattern validators
|
157
|
+
config.add_validator 'phone',
|
158
|
+
pattern: /\A\+?[1-9]\d{1,14}\z/,
|
159
|
+
error_message: 'must be a valid international phone number'
|
160
|
+
|
161
|
+
config.add_validator 'postal_code',
|
162
|
+
pattern: /\A[A-Z0-9]{3,10}\z/i,
|
163
|
+
error_message: 'must be a valid postal code'
|
164
|
+
|
165
|
+
# Add function validator
|
166
|
+
config.add_validator 'age_range',
|
167
|
+
func: ->(age) { age.is_a?(Integer) && age.between?(0, 120) },
|
168
|
+
error_message: 'must be between 0 and 120'
|
169
|
+
end
|
170
|
+
|
171
|
+
# Use the configured validators
|
172
|
+
validator = HashValidator.validate(
|
173
|
+
{
|
174
|
+
phone: '+14155551234',
|
175
|
+
postal_code: 'SW1A1AA',
|
176
|
+
age: 30
|
177
|
+
},
|
178
|
+
{
|
179
|
+
phone: 'phone',
|
180
|
+
postal_code: 'postal_code',
|
181
|
+
age: 'age_range'
|
182
|
+
}
|
183
|
+
)
|
184
|
+
|
185
|
+
expect(validator.valid?).to eq true
|
186
|
+
expect(validator.errors).to be_empty
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/spec/hash_validator_spec.rb
CHANGED
@@ -7,20 +7,20 @@ describe HashValidator do
|
|
7
7
|
|
8
8
|
it 'allows validators with unique names' do
|
9
9
|
expect {
|
10
|
-
HashValidator.
|
10
|
+
HashValidator.add_validator(new_validator1)
|
11
11
|
}.to_not raise_error
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'does not allow validators with conflicting names' do
|
15
15
|
expect {
|
16
|
-
HashValidator.
|
17
|
-
HashValidator.
|
16
|
+
HashValidator.add_validator(new_validator2)
|
17
|
+
HashValidator.add_validator(new_validator2)
|
18
18
|
}.to raise_error(StandardError, 'validators need to have unique names')
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'does not allow validators that do not inherit from the base validator class' do
|
22
22
|
expect {
|
23
|
-
HashValidator.
|
23
|
+
HashValidator.add_validator('Not a validator')
|
24
24
|
}.to raise_error(StandardError, 'validators need to inherit from HashValidator::Validator::Base')
|
25
25
|
end
|
26
26
|
end
|
@@ -16,8 +16,8 @@ describe HashValidator::Validator::Base do
|
|
16
16
|
describe '#validate' do
|
17
17
|
let(:validator) { HashValidator::Validator::Base.new('test') }
|
18
18
|
|
19
|
-
it 'throws an exception as base validators
|
20
|
-
expect { validator.validate('key', 'value', {}, {}) }.to raise_error(StandardError, '
|
19
|
+
it 'throws an exception as base validators must implement valid? or override validate' do
|
20
|
+
expect { validator.validate('key', 'value', {}, {}) }.to raise_error(StandardError, 'Validator must implement either valid? or override validate method')
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|