hash_validator 1.1.1 → 1.2.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.
@@ -4,10 +4,15 @@ class HashValidator::Validator::HashValidator < HashValidator::Validator::Base
4
4
  end
5
5
 
6
6
  def should_validate?(rhs)
7
- rhs.is_a?(Hash)
7
+ rhs.is_a?(Hash) || (defined?(ActionController::Parameters) && rhs.is_a?(ActionController::Parameters))
8
8
  end
9
9
 
10
10
  def validate(key, value, validations, errors)
11
+ # Convert ActionController::Parameters to Hash if needed
12
+ if !value.is_a?(Hash) && defined?(ActionController::Parameters) && value.is_a?(ActionController::Parameters)
13
+ value = value.to_unsafe_h
14
+ end
15
+
11
16
  # Validate hash
12
17
  unless value.is_a?(Hash)
13
18
  errors[key] = presence_error_message
@@ -0,0 +1,23 @@
1
+ class HashValidator::Validator::HexColorValidator < HashValidator::Validator::Base
2
+ def initialize
3
+ super('hex_color') # The name of the validator
4
+ end
5
+
6
+ def presence_error_message
7
+ 'is not a valid hex color'
8
+ end
9
+
10
+ def validate(key, value, _validations, errors)
11
+ unless value.is_a?(String) && valid_hex_color?(value)
12
+ errors[key] = presence_error_message
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def valid_hex_color?(value)
19
+ /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/.match?(value)
20
+ end
21
+ end
22
+
23
+ HashValidator.append_validator(HashValidator::Validator::HexColorValidator.new)
@@ -0,0 +1,28 @@
1
+ require 'json'
2
+
3
+ class HashValidator::Validator::JsonValidator < HashValidator::Validator::Base
4
+ def initialize
5
+ super('json') # The name of the validator
6
+ end
7
+
8
+ def presence_error_message
9
+ 'is not valid JSON'
10
+ end
11
+
12
+ def validate(key, value, _validations, errors)
13
+ unless value.is_a?(String) && valid_json?(value)
14
+ errors[key] = presence_error_message
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def valid_json?(value)
21
+ JSON.parse(value)
22
+ true
23
+ rescue JSON::ParserError
24
+ false
25
+ end
26
+ end
27
+
28
+ HashValidator.append_validator(HashValidator::Validator::JsonValidator.new)
@@ -0,0 +1,28 @@
1
+ require 'uri'
2
+
3
+ class HashValidator::Validator::UrlValidator < HashValidator::Validator::Base
4
+ def initialize
5
+ super('url') # The name of the validator
6
+ end
7
+
8
+ def presence_error_message
9
+ 'is not a valid URL'
10
+ end
11
+
12
+ def validate(key, value, _validations, errors)
13
+ unless value.is_a?(String) && valid_url?(value)
14
+ errors[key] = presence_error_message
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def valid_url?(value)
21
+ uri = URI.parse(value)
22
+ %w[http https ftp].include?(uri.scheme)
23
+ rescue URI::InvalidURIError
24
+ false
25
+ end
26
+ end
27
+
28
+ HashValidator.append_validator(HashValidator::Validator::UrlValidator.new)
@@ -37,4 +37,10 @@ require 'hash_validator/validators/lambda_validator'
37
37
  require 'hash_validator/validators/optional_validator'
38
38
  require 'hash_validator/validators/many_validator'
39
39
  require 'hash_validator/validators/multiple_validator'
40
- require 'hash_validator/validators/array_validator'
40
+ require 'hash_validator/validators/array_validator'
41
+ require 'hash_validator/validators/url_validator'
42
+ require 'hash_validator/validators/json_validator'
43
+ require 'hash_validator/validators/hex_color_validator'
44
+ require 'hash_validator/validators/alphanumeric_validator'
45
+ require 'hash_validator/validators/alpha_validator'
46
+ require 'hash_validator/validators/digits_validator'
@@ -1,3 +1,3 @@
1
1
  module HashValidator
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::AlphaValidator do
4
+ let(:validator) { HashValidator::Validator::AlphaValidator.new }
5
+
6
+ context 'valid alpha strings' do
7
+ it 'validates lowercase letters' do
8
+ errors = {}
9
+ validator.validate('key', 'abc', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates uppercase letters' do
14
+ errors = {}
15
+ validator.validate('key', 'ABC', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates mixed case letters' do
20
+ errors = {}
21
+ validator.validate('key', 'AbC', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates single letter' do
26
+ errors = {}
27
+ validator.validate('key', 'a', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates long strings' do
32
+ errors = {}
33
+ validator.validate('key', 'HelloWorld', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+ end
37
+
38
+ context 'invalid alpha strings' do
39
+ it 'rejects non-string values' do
40
+ errors = {}
41
+ validator.validate('key', 123, {}, errors)
42
+ expect(errors['key']).to eq('must contain only letters')
43
+ end
44
+
45
+ it 'rejects nil values' do
46
+ errors = {}
47
+ validator.validate('key', nil, {}, errors)
48
+ expect(errors['key']).to eq('must contain only letters')
49
+ end
50
+
51
+ it 'rejects strings with numbers' do
52
+ errors = {}
53
+ validator.validate('key', 'abc123', {}, errors)
54
+ expect(errors['key']).to eq('must contain only letters')
55
+ end
56
+
57
+ it 'rejects strings with spaces' do
58
+ errors = {}
59
+ validator.validate('key', 'abc def', {}, errors)
60
+ expect(errors['key']).to eq('must contain only letters')
61
+ end
62
+
63
+ it 'rejects strings with special characters' do
64
+ errors = {}
65
+ validator.validate('key', 'abc!', {}, errors)
66
+ expect(errors['key']).to eq('must contain only letters')
67
+ end
68
+
69
+ it 'rejects strings with hyphens' do
70
+ errors = {}
71
+ validator.validate('key', 'abc-def', {}, errors)
72
+ expect(errors['key']).to eq('must contain only letters')
73
+ end
74
+
75
+ it 'rejects strings with underscores' do
76
+ errors = {}
77
+ validator.validate('key', 'abc_def', {}, errors)
78
+ expect(errors['key']).to eq('must contain only letters')
79
+ end
80
+
81
+ it 'rejects empty strings' do
82
+ errors = {}
83
+ validator.validate('key', '', {}, errors)
84
+ expect(errors['key']).to eq('must contain only letters')
85
+ end
86
+
87
+ it 'rejects strings with periods' do
88
+ errors = {}
89
+ validator.validate('key', 'abc.def', {}, errors)
90
+ expect(errors['key']).to eq('must contain only letters')
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::AlphanumericValidator do
4
+ let(:validator) { HashValidator::Validator::AlphanumericValidator.new }
5
+
6
+ context 'valid alphanumeric strings' do
7
+ it 'validates letters only' do
8
+ errors = {}
9
+ validator.validate('key', 'abc', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates numbers only' do
14
+ errors = {}
15
+ validator.validate('key', '123', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates mixed letters and numbers' do
20
+ errors = {}
21
+ validator.validate('key', 'abc123', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates uppercase letters' do
26
+ errors = {}
27
+ validator.validate('key', 'ABC', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates mixed case letters' do
32
+ errors = {}
33
+ validator.validate('key', 'AbC123', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+
37
+ it 'validates single character' do
38
+ errors = {}
39
+ validator.validate('key', 'a', {}, errors)
40
+ expect(errors).to be_empty
41
+ end
42
+
43
+ it 'validates single digit' do
44
+ errors = {}
45
+ validator.validate('key', '1', {}, errors)
46
+ expect(errors).to be_empty
47
+ end
48
+ end
49
+
50
+ context 'invalid alphanumeric strings' do
51
+ it 'rejects non-string values' do
52
+ errors = {}
53
+ validator.validate('key', 123, {}, errors)
54
+ expect(errors['key']).to eq('must contain only letters and numbers')
55
+ end
56
+
57
+ it 'rejects nil values' do
58
+ errors = {}
59
+ validator.validate('key', nil, {}, errors)
60
+ expect(errors['key']).to eq('must contain only letters and numbers')
61
+ end
62
+
63
+ it 'rejects strings with spaces' do
64
+ errors = {}
65
+ validator.validate('key', 'abc 123', {}, errors)
66
+ expect(errors['key']).to eq('must contain only letters and numbers')
67
+ end
68
+
69
+ it 'rejects strings with special characters' do
70
+ errors = {}
71
+ validator.validate('key', 'abc!123', {}, errors)
72
+ expect(errors['key']).to eq('must contain only letters and numbers')
73
+ end
74
+
75
+ it 'rejects strings with hyphens' do
76
+ errors = {}
77
+ validator.validate('key', 'abc-123', {}, errors)
78
+ expect(errors['key']).to eq('must contain only letters and numbers')
79
+ end
80
+
81
+ it 'rejects strings with underscores' do
82
+ errors = {}
83
+ validator.validate('key', 'abc_123', {}, errors)
84
+ expect(errors['key']).to eq('must contain only letters and numbers')
85
+ end
86
+
87
+ it 'rejects empty strings' do
88
+ errors = {}
89
+ validator.validate('key', '', {}, errors)
90
+ expect(errors['key']).to eq('must contain only letters and numbers')
91
+ end
92
+
93
+ it 'rejects strings with periods' do
94
+ errors = {}
95
+ validator.validate('key', 'abc.123', {}, errors)
96
+ expect(errors['key']).to eq('must contain only letters and numbers')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::DigitsValidator do
4
+ let(:validator) { HashValidator::Validator::DigitsValidator.new }
5
+
6
+ context 'valid digit strings' do
7
+ it 'validates single digit' do
8
+ errors = {}
9
+ validator.validate('key', '1', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates multiple digits' do
14
+ errors = {}
15
+ validator.validate('key', '123', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates zero' do
20
+ errors = {}
21
+ validator.validate('key', '0', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates long number strings' do
26
+ errors = {}
27
+ validator.validate('key', '1234567890', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates leading zeros' do
32
+ errors = {}
33
+ validator.validate('key', '0123', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+ end
37
+
38
+ context 'invalid digit strings' do
39
+ it 'rejects non-string values' do
40
+ errors = {}
41
+ validator.validate('key', 123, {}, errors)
42
+ expect(errors['key']).to eq('must contain only digits')
43
+ end
44
+
45
+ it 'rejects nil values' do
46
+ errors = {}
47
+ validator.validate('key', nil, {}, errors)
48
+ expect(errors['key']).to eq('must contain only digits')
49
+ end
50
+
51
+ it 'rejects strings with letters' do
52
+ errors = {}
53
+ validator.validate('key', '123abc', {}, errors)
54
+ expect(errors['key']).to eq('must contain only digits')
55
+ end
56
+
57
+ it 'rejects strings with spaces' do
58
+ errors = {}
59
+ validator.validate('key', '123 456', {}, errors)
60
+ expect(errors['key']).to eq('must contain only digits')
61
+ end
62
+
63
+ it 'rejects strings with special characters' do
64
+ errors = {}
65
+ validator.validate('key', '123!', {}, errors)
66
+ expect(errors['key']).to eq('must contain only digits')
67
+ end
68
+
69
+ it 'rejects strings with hyphens' do
70
+ errors = {}
71
+ validator.validate('key', '123-456', {}, errors)
72
+ expect(errors['key']).to eq('must contain only digits')
73
+ end
74
+
75
+ it 'rejects strings with periods' do
76
+ errors = {}
77
+ validator.validate('key', '123.456', {}, errors)
78
+ expect(errors['key']).to eq('must contain only digits')
79
+ end
80
+
81
+ it 'rejects empty strings' do
82
+ errors = {}
83
+ validator.validate('key', '', {}, errors)
84
+ expect(errors['key']).to eq('must contain only digits')
85
+ end
86
+
87
+ it 'rejects negative signs' do
88
+ errors = {}
89
+ validator.validate('key', '-123', {}, errors)
90
+ expect(errors['key']).to eq('must contain only digits')
91
+ end
92
+
93
+ it 'rejects positive signs' do
94
+ errors = {}
95
+ validator.validate('key', '+123', {}, errors)
96
+ expect(errors['key']).to eq('must contain only digits')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ActionController::Parameters support' do
4
+ # Mock ActionController::Parameters for testing
5
+ let(:mock_params_class) do
6
+ Class.new do
7
+ attr_reader :data
8
+
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ def [](key)
14
+ @data[key]
15
+ end
16
+
17
+ def keys
18
+ @data.keys
19
+ end
20
+
21
+ def to_unsafe_h
22
+ @data
23
+ end
24
+
25
+ def is_a?(klass)
26
+ return true if klass.name == 'ActionController::Parameters'
27
+ super
28
+ end
29
+
30
+ def class
31
+ OpenStruct.new(name: 'ActionController::Parameters')
32
+ end
33
+ end
34
+ end
35
+
36
+ before do
37
+ # Define ActionController::Parameters constant for testing
38
+ unless defined?(ActionController::Parameters)
39
+ ActionController = Module.new unless defined?(ActionController)
40
+ ActionController.const_set(:Parameters, mock_params_class)
41
+ end
42
+ end
43
+
44
+ it 'should validate ActionController::Parameters objects' do
45
+ params = ActionController::Parameters.new({ name: 'John', age: 30 })
46
+ validations = { name: 'string', age: 'integer' }
47
+
48
+ validator = HashValidator.validate(params, validations)
49
+
50
+ expect(validator.valid?).to be true
51
+ expect(validator.errors).to be_empty
52
+ end
53
+
54
+ it 'should handle nested ActionController::Parameters' do
55
+ nested_params = ActionController::Parameters.new({ theme: 'dark' })
56
+ params = ActionController::Parameters.new({
57
+ name: 'John',
58
+ preferences: nested_params
59
+ })
60
+
61
+ validations = {
62
+ name: 'string',
63
+ preferences: { theme: 'string' }
64
+ }
65
+
66
+ validator = HashValidator.validate(params, validations)
67
+
68
+ expect(validator.valid?).to be true
69
+ expect(validator.errors).to be_empty
70
+ end
71
+
72
+ it 'should validate ActionController::Parameters with our new validators' do
73
+ params = ActionController::Parameters.new({
74
+ name: 'John',
75
+ email: 'john@example.com',
76
+ website: 'https://john.com',
77
+ zip_code: '12345'
78
+ })
79
+
80
+ validations = {
81
+ name: 'alpha',
82
+ email: 'email',
83
+ website: 'url',
84
+ zip_code: 'digits'
85
+ }
86
+
87
+ validator = HashValidator.validate(params, validations)
88
+
89
+ expect(validator.valid?).to be true
90
+ expect(validator.errors).to be_empty
91
+ end
92
+
93
+ it 'should still work with regular hashes' do
94
+ hash = { name: 'John', age: 30 }
95
+ validations = { name: 'string', age: 'integer' }
96
+
97
+ validator = HashValidator.validate(hash, validations)
98
+
99
+ expect(validator.valid?).to be true
100
+ expect(validator.errors).to be_empty
101
+ end
102
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::HexColorValidator do
4
+ let(:validator) { HashValidator::Validator::HexColorValidator.new }
5
+
6
+ context 'valid hex colors' do
7
+ it 'validates 6-digit hex colors (lowercase)' do
8
+ errors = {}
9
+ validator.validate('key', '#ff0000', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates 6-digit hex colors (uppercase)' do
14
+ errors = {}
15
+ validator.validate('key', '#FF0000', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates 6-digit hex colors (mixed case)' do
20
+ errors = {}
21
+ validator.validate('key', '#Ff0000', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates 3-digit hex colors (lowercase)' do
26
+ errors = {}
27
+ validator.validate('key', '#f00', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates 3-digit hex colors (uppercase)' do
32
+ errors = {}
33
+ validator.validate('key', '#F00', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+
37
+ it 'validates black color' do
38
+ errors = {}
39
+ validator.validate('key', '#000000', {}, errors)
40
+ expect(errors).to be_empty
41
+ end
42
+
43
+ it 'validates white color' do
44
+ errors = {}
45
+ validator.validate('key', '#ffffff', {}, errors)
46
+ expect(errors).to be_empty
47
+ end
48
+
49
+ it 'validates complex hex colors' do
50
+ errors = {}
51
+ validator.validate('key', '#3a7bd4', {}, errors)
52
+ expect(errors).to be_empty
53
+ end
54
+ end
55
+
56
+ context 'invalid hex colors' do
57
+ it 'rejects non-string values' do
58
+ errors = {}
59
+ validator.validate('key', 123, {}, errors)
60
+ expect(errors['key']).to eq('is not a valid hex color')
61
+ end
62
+
63
+ it 'rejects nil values' do
64
+ errors = {}
65
+ validator.validate('key', nil, {}, errors)
66
+ expect(errors['key']).to eq('is not a valid hex color')
67
+ end
68
+
69
+ it 'rejects hex colors without hash' do
70
+ errors = {}
71
+ validator.validate('key', 'ff0000', {}, errors)
72
+ expect(errors['key']).to eq('is not a valid hex color')
73
+ end
74
+
75
+ it 'rejects invalid length (4 digits)' do
76
+ errors = {}
77
+ validator.validate('key', '#ff00', {}, errors)
78
+ expect(errors['key']).to eq('is not a valid hex color')
79
+ end
80
+
81
+ it 'rejects invalid length (5 digits)' do
82
+ errors = {}
83
+ validator.validate('key', '#ff000', {}, errors)
84
+ expect(errors['key']).to eq('is not a valid hex color')
85
+ end
86
+
87
+ it 'rejects invalid length (7 digits)' do
88
+ errors = {}
89
+ validator.validate('key', '#ff00000', {}, errors)
90
+ expect(errors['key']).to eq('is not a valid hex color')
91
+ end
92
+
93
+ it 'rejects invalid characters' do
94
+ errors = {}
95
+ validator.validate('key', '#gg0000', {}, errors)
96
+ expect(errors['key']).to eq('is not a valid hex color')
97
+ end
98
+
99
+ it 'rejects empty strings' do
100
+ errors = {}
101
+ validator.validate('key', '', {}, errors)
102
+ expect(errors['key']).to eq('is not a valid hex color')
103
+ end
104
+
105
+ it 'rejects just hash symbol' do
106
+ errors = {}
107
+ validator.validate('key', '#', {}, errors)
108
+ expect(errors['key']).to eq('is not a valid hex color')
109
+ end
110
+ end
111
+ end