hash_validator 1.2.0 → 2.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +6 -1
  3. data/.rubocop.yml +340 -0
  4. data/Gemfile +3 -1
  5. data/README.md +175 -13
  6. data/Rakefile +6 -4
  7. data/hash_validator.gemspec +11 -5
  8. data/lib/hash_validator/base.rb +2 -0
  9. data/lib/hash_validator/configuration.rb +18 -0
  10. data/lib/hash_validator/validations/many.rb +2 -0
  11. data/lib/hash_validator/validations/multiple.rb +2 -0
  12. data/lib/hash_validator/validations/optional.rb +2 -0
  13. data/lib/hash_validator/validations.rb +5 -3
  14. data/lib/hash_validator/validators/alpha_validator.rb +8 -14
  15. data/lib/hash_validator/validators/alphanumeric_validator.rb +8 -14
  16. data/lib/hash_validator/validators/array_validator.rb +45 -49
  17. data/lib/hash_validator/validators/base.rb +31 -4
  18. data/lib/hash_validator/validators/boolean_validator.rb +6 -6
  19. data/lib/hash_validator/validators/class_validator.rb +4 -2
  20. data/lib/hash_validator/validators/digits_validator.rb +8 -14
  21. data/lib/hash_validator/validators/dynamic_func_validator.rb +26 -0
  22. data/lib/hash_validator/validators/dynamic_pattern_validator.rb +25 -0
  23. data/lib/hash_validator/validators/email_validator.rb +8 -8
  24. data/lib/hash_validator/validators/enumerable_validator.rb +8 -8
  25. data/lib/hash_validator/validators/hash_validator.rb +7 -5
  26. data/lib/hash_validator/validators/hex_color_validator.rb +8 -14
  27. data/lib/hash_validator/validators/ip_validator.rb +24 -0
  28. data/lib/hash_validator/validators/ipv4_validator.rb +20 -0
  29. data/lib/hash_validator/validators/ipv6_validator.rb +24 -0
  30. data/lib/hash_validator/validators/json_validator.rb +9 -14
  31. data/lib/hash_validator/validators/lambda_validator.rb +9 -10
  32. data/lib/hash_validator/validators/many_validator.rb +6 -4
  33. data/lib/hash_validator/validators/multiple_validator.rb +5 -3
  34. data/lib/hash_validator/validators/optional_validator.rb +4 -2
  35. data/lib/hash_validator/validators/presence_validator.rb +8 -8
  36. data/lib/hash_validator/validators/regex_validator.rb +8 -8
  37. data/lib/hash_validator/validators/simple_type_validators.rb +4 -2
  38. data/lib/hash_validator/validators/simple_validator.rb +4 -4
  39. data/lib/hash_validator/validators/url_validator.rb +9 -14
  40. data/lib/hash_validator/validators.rb +63 -25
  41. data/lib/hash_validator/version.rb +3 -1
  42. data/lib/hash_validator.rb +7 -4
  43. data/spec/configuration_spec.rb +191 -0
  44. data/spec/hash_validator_spec.rb +135 -116
  45. data/spec/hash_validator_spec_helper.rb +2 -0
  46. data/spec/spec_helper.rb +14 -4
  47. data/spec/validators/alpha_validator_spec.rb +43 -41
  48. data/spec/validators/alphanumeric_validator_spec.rb +44 -42
  49. data/spec/validators/array_spec.rb +102 -47
  50. data/spec/validators/base_spec.rb +25 -10
  51. data/spec/validators/boolean_spec.rb +15 -13
  52. data/spec/validators/class_spec.rb +20 -18
  53. data/spec/validators/digits_validator_spec.rb +46 -44
  54. data/spec/validators/dynamic_func_validator_spec.rb +254 -0
  55. data/spec/validators/dynamic_pattern_validator_spec.rb +152 -0
  56. data/spec/validators/email_spec.rb +15 -13
  57. data/spec/validators/hash_validator_spec.rb +39 -37
  58. data/spec/validators/hex_color_validator_spec.rb +49 -47
  59. data/spec/validators/in_enumerable_spec.rb +32 -30
  60. data/spec/validators/ip_validator_spec.rb +107 -0
  61. data/spec/validators/ipv4_validator_spec.rb +101 -0
  62. data/spec/validators/ipv6_validator_spec.rb +101 -0
  63. data/spec/validators/json_validator_spec.rb +38 -36
  64. data/spec/validators/lambda_spec.rb +20 -18
  65. data/spec/validators/many_spec.rb +25 -23
  66. data/spec/validators/multiple_spec.rb +13 -11
  67. data/spec/validators/optional_spec.rb +22 -20
  68. data/spec/validators/presence_spec.rb +16 -14
  69. data/spec/validators/regexp_spec.rb +14 -12
  70. data/spec/validators/simple_spec.rb +17 -15
  71. data/spec/validators/simple_types_spec.rb +21 -19
  72. data/spec/validators/url_validator_spec.rb +34 -32
  73. data/spec/validators/user_defined_spec.rb +31 -29
  74. metadata +77 -3
  75. data/Plan.md +0 -309
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HashValidator::Validations
2
4
  class Multiple
3
5
  attr_reader :validations
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HashValidator::Validations
2
4
  class Optional
3
5
  attr_reader :validation
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HashValidator
2
4
  module Validations
3
5
  end
4
6
  end
5
7
 
6
8
  # Load validators
7
- require 'hash_validator/validations/optional'
8
- require 'hash_validator/validations/many'
9
- require 'hash_validator/validations/multiple'
9
+ require "hash_validator/validations/optional"
10
+ require "hash_validator/validations/many"
11
+ require "hash_validator/validations/multiple"
@@ -1,23 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::AlphaValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('alpha') # The name of the validator
4
- end
5
-
6
- def presence_error_message
7
- 'must contain only letters'
5
+ super("alpha") # The name of the validator
8
6
  end
9
7
 
10
- def validate(key, value, _validations, errors)
11
- unless value.is_a?(String) && valid_alpha?(value)
12
- errors[key] = presence_error_message
13
- end
8
+ def error_message
9
+ "must contain only letters"
14
10
  end
15
11
 
16
- private
17
-
18
- def valid_alpha?(value)
19
- /\A[a-zA-Z]+\z/.match?(value)
12
+ def valid?(value)
13
+ value.is_a?(String) && /\A[a-zA-Z]+\z/.match?(value)
20
14
  end
21
15
  end
22
16
 
23
- HashValidator.append_validator(HashValidator::Validator::AlphaValidator.new)
17
+ HashValidator.add_validator(HashValidator::Validator::AlphaValidator.new)
@@ -1,23 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::AlphanumericValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('alphanumeric') # The name of the validator
4
- end
5
-
6
- def presence_error_message
7
- 'must contain only letters and numbers'
5
+ super("alphanumeric") # The name of the validator
8
6
  end
9
7
 
10
- def validate(key, value, _validations, errors)
11
- unless value.is_a?(String) && valid_alphanumeric?(value)
12
- errors[key] = presence_error_message
13
- end
8
+ def error_message
9
+ "must contain only letters and numbers"
14
10
  end
15
11
 
16
- private
17
-
18
- def valid_alphanumeric?(value)
19
- /\A[a-zA-Z0-9]+\z/.match?(value)
12
+ def valid?(value)
13
+ value.is_a?(String) && /\A[a-zA-Z0-9]+\z/.match?(value)
20
14
  end
21
15
  end
22
16
 
23
- HashValidator.append_validator(HashValidator::Validator::AlphanumericValidator.new)
17
+ HashValidator.add_validator(HashValidator::Validator::AlphanumericValidator.new)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::ArrayValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('__array__') # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
5
+ super("__array__") # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
4
6
  end
5
7
 
6
8
  def should_validate?(rhs)
@@ -8,73 +10,67 @@ class HashValidator::Validator::ArrayValidator < HashValidator::Validator::Base
8
10
  return false unless rhs.size > 0
9
11
  return false unless rhs[0] == :array
10
12
 
11
- return true
13
+ true
12
14
  end
13
15
 
14
16
  def validate(key, value, specification, errors)
15
- # the first item in specification is always ":array"
16
- unless specification[0] == :array
17
+ # Validate specification format
18
+ if specification[0] != :array
17
19
  errors[key] = "Wrong array specification. The #{:array} is expected as first item."
18
- return
19
- end
20
-
21
- if specification.size > 2
20
+ elsif specification.size > 2
22
21
  errors[key] = "Wrong size of array specification. Allowed is one or two items."
23
- return
24
- end
25
-
26
- unless value.is_a?(Array)
22
+ elsif !value.is_a?(Array)
27
23
  errors[key] = "#{Array} required"
28
- return
24
+ elsif specification.size >= 2 && !specification[1].nil?
25
+ validate_array_specification(key, value, specification[1], errors)
29
26
  end
27
+ end
30
28
 
31
- # second item is optional
32
- return if specification.size < 2
29
+ private
30
+ def validate_array_specification(key, value, array_spec, errors)
31
+ # Convert numeric specification to hash format
32
+ array_spec = { size: array_spec } if array_spec.is_a?(Numeric)
33
33
 
34
- array_spec = specification[1]
35
- return if array_spec.nil? # array specification is optional
34
+ unless array_spec.is_a?(Hash)
35
+ errors[key] = "Second item of array specification must be #{Hash} or #{Numeric}."
36
+ return
37
+ end
36
38
 
37
- if array_spec.is_a?(Numeric)
38
- array_spec = { size: array_spec }
39
- end
39
+ return if array_spec.empty?
40
40
 
41
- unless array_spec.is_a?(Hash)
42
- errors[key] = "Second item of array specification must be #{Hash} or #{Numeric}."
43
- return
41
+ validate_size_specification(key, value, array_spec, errors) if errors.empty?
42
+ validate_allowed_keys(key, array_spec, errors) if errors.empty?
44
43
  end
45
44
 
46
- return if array_spec.empty?
47
-
48
- size_spec = array_spec[:size]
49
-
50
- size_spec_present = case size_spec
51
- when String
52
- !object.strip.empty?
53
- when NilClass
54
- false
55
- when Numeric
56
- true
57
- when Array, Hash
58
- !object.empty?
59
- else
60
- !!object
45
+ def validate_size_specification(key, value, array_spec, errors)
46
+ size_spec = array_spec[:size]
47
+
48
+ size_spec_present = case size_spec
49
+ when String
50
+ !size_spec.strip.empty?
51
+ when NilClass
52
+ false
53
+ when Numeric
54
+ true
55
+ when Array, Hash
56
+ !size_spec.empty?
57
+ else
58
+ !!size_spec
61
59
  end
62
60
 
63
- if size_spec_present
64
- unless value.size == size_spec
61
+ if size_spec_present && value.size != size_spec
65
62
  errors[key] = "The required size of array is #{size_spec} but is #{value.size}."
66
- return
67
63
  end
68
64
  end
69
65
 
70
- allowed_keys = [:size]
71
- wrong_keys = array_spec.keys - allowed_keys
66
+ def validate_allowed_keys(key, array_spec, errors)
67
+ allowed_keys = [:size]
68
+ wrong_keys = array_spec.keys - allowed_keys
72
69
 
73
- return if wrong_keys.size < 1
74
-
75
- errors[key] = "Not supported specification for array: #{wrong_keys.sort.join(", ")}."
76
- return
77
- end
70
+ if wrong_keys.any?
71
+ errors[key] = "Not supported specification for array: #{wrong_keys.sort.join(", ")}."
72
+ end
73
+ end
78
74
  end
79
75
 
80
- HashValidator.append_validator(HashValidator::Validator::ArrayValidator.new)
76
+ HashValidator.add_validator(HashValidator::Validator::ArrayValidator.new)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::Base
2
4
  attr_accessor :name
3
5
 
@@ -6,7 +8,7 @@ class HashValidator::Validator::Base
6
8
  self.name = name.to_s
7
9
 
8
10
  unless self.name.size > 0
9
- raise StandardError.new('Validator must be initialized with a valid name (length greater than zero)')
11
+ raise StandardError.new("Validator must be initialized with a valid name (length greater than zero)")
10
12
  end
11
13
  end
12
14
 
@@ -14,11 +16,36 @@ class HashValidator::Validator::Base
14
16
  self.name == name.to_s
15
17
  end
16
18
 
17
- def presence_error_message
19
+ def error_message
18
20
  "#{self.name} required"
19
21
  end
20
22
 
21
- def validate(*)
22
- raise StandardError.new('validate should not be called directly on BaseValidator')
23
+ def validate(key, value, validations, errors)
24
+ # If the subclass implements valid?, use that for simple boolean validation
25
+ if self.class.instance_methods(false).include?(:valid?)
26
+ # Check the arity of the valid? method to determine how many arguments to pass
27
+ valid_result = case method(:valid?).arity
28
+ when 1
29
+ valid?(value)
30
+ when 2
31
+ valid?(value, validations)
32
+ else
33
+ raise StandardError.new("valid? method must accept either 1 argument (value) or 2 arguments (value, validations)")
34
+ end
35
+
36
+ unless valid_result
37
+ errors[key] = error_message
38
+ end
39
+ else
40
+ # Otherwise, subclass must override validate
41
+ raise StandardError.new("Validator must implement either valid? or override validate method")
42
+ end
23
43
  end
44
+
45
+ # Subclasses can optionally implement this for simple boolean validation
46
+ # Return true if valid, false if invalid
47
+ # Either:
48
+ # def valid?(value) # For simple validations
49
+ # def valid?(value, validations) # When validation context is needed
50
+ # end
24
51
  end
@@ -1,13 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::BooleanValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('boolean') # The name of the validator
5
+ super("boolean") # The name of the validator
4
6
  end
5
7
 
6
- def validate(key, value, _validations, errors)
7
- unless [TrueClass, FalseClass].include?(value.class)
8
- errors[key] = presence_error_message
9
- end
8
+ def valid?(value)
9
+ [TrueClass, FalseClass].include?(value.class)
10
10
  end
11
11
  end
12
12
 
13
- HashValidator.append_validator(HashValidator::Validator::BooleanValidator.new)
13
+ HashValidator.add_validator(HashValidator::Validator::BooleanValidator.new)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::ClassValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('_class') # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
5
+ super("_class") # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
4
6
  end
5
7
 
6
8
  def should_validate?(rhs)
@@ -14,4 +16,4 @@ class HashValidator::Validator::ClassValidator < HashValidator::Validator::Base
14
16
  end
15
17
  end
16
18
 
17
- HashValidator.append_validator(HashValidator::Validator::ClassValidator.new)
19
+ HashValidator.add_validator(HashValidator::Validator::ClassValidator.new)
@@ -1,23 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::DigitsValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('digits') # The name of the validator
4
- end
5
-
6
- def presence_error_message
7
- 'must contain only digits'
5
+ super("digits") # The name of the validator
8
6
  end
9
7
 
10
- def validate(key, value, _validations, errors)
11
- unless value.is_a?(String) && valid_digits?(value)
12
- errors[key] = presence_error_message
13
- end
8
+ def error_message
9
+ "must contain only digits"
14
10
  end
15
11
 
16
- private
17
-
18
- def valid_digits?(value)
19
- /\A\d+\z/.match?(value)
12
+ def valid?(value)
13
+ value.is_a?(String) && /\A\d+\z/.match?(value)
20
14
  end
21
15
  end
22
16
 
23
- HashValidator.append_validator(HashValidator::Validator::DigitsValidator.new)
17
+ HashValidator.add_validator(HashValidator::Validator::DigitsValidator.new)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HashValidator::Validator::DynamicFuncValidator < HashValidator::Validator::Base
4
+ attr_accessor :func, :custom_error_message
5
+
6
+ def initialize(name, func, error_message = nil)
7
+ super(name)
8
+
9
+ unless func.respond_to?(:call)
10
+ raise ArgumentError, "Function must be callable (proc or lambda)"
11
+ end
12
+
13
+ @func = func
14
+ @custom_error_message = error_message
15
+ end
16
+
17
+ def error_message
18
+ @custom_error_message || super
19
+ end
20
+
21
+ def valid?(value)
22
+ !!@func.call(value)
23
+ rescue
24
+ false
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HashValidator::Validator::DynamicPatternValidator < HashValidator::Validator::Base
4
+ attr_accessor :pattern, :custom_error_message
5
+
6
+ def initialize(name, pattern, error_message = nil)
7
+ super(name)
8
+
9
+ unless pattern.is_a?(Regexp)
10
+ raise ArgumentError, "Pattern must be a regular expression"
11
+ end
12
+
13
+ @pattern = pattern
14
+ @custom_error_message = error_message
15
+ end
16
+
17
+ def error_message
18
+ @custom_error_message || super
19
+ end
20
+
21
+ def valid?(value)
22
+ return false unless value.respond_to?(:to_s)
23
+ @pattern.match?(value.to_s)
24
+ end
25
+ end
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::EmailValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('email') # The name of the validator
5
+ super("email") # The name of the validator
4
6
  end
5
7
 
6
- def presence_error_message
7
- 'is not a valid email'
8
+ def error_message
9
+ "is not a valid email"
8
10
  end
9
11
 
10
- def validate(key, value, _validations, errors)
11
- unless value.is_a?(String) && value.include?("@")
12
- errors[key] = presence_error_message
13
- end
12
+ def valid?(value)
13
+ value.is_a?(String) && value.include?("@")
14
14
  end
15
15
  end
16
16
 
17
- HashValidator.append_validator(HashValidator::Validator::EmailValidator.new)
17
+ HashValidator.add_validator(HashValidator::Validator::EmailValidator.new)
@@ -1,21 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::EnumerableValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('_enumerable') # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
5
+ super("_enumerable") # The name of the validator, underscored as it won't usually be directly invoked (invoked through use of validator)
4
6
  end
5
7
 
6
8
  def should_validate?(rhs)
7
9
  rhs.is_a?(Enumerable)
8
10
  end
9
11
 
10
- def presence_error_message
11
- 'value from list required'
12
+ def error_message
13
+ "value from list required"
12
14
  end
13
15
 
14
- def validate(key, value, validations, errors)
15
- unless validations.include?(value)
16
- errors[key] = presence_error_message
17
- end
16
+ def valid?(value, validations)
17
+ validations.include?(value)
18
18
  end
19
19
  end
20
20
 
21
- HashValidator.append_validator(HashValidator::Validator::EnumerableValidator.new)
21
+ HashValidator.add_validator(HashValidator::Validator::EnumerableValidator.new)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::HashValidator < HashValidator::Validator::Base
2
4
  def initialize
3
- super('hash')
5
+ super("hash")
4
6
  end
5
7
 
6
8
  def should_validate?(rhs)
@@ -15,7 +17,7 @@ class HashValidator::Validator::HashValidator < HashValidator::Validator::Base
15
17
 
16
18
  # Validate hash
17
19
  unless value.is_a?(Hash)
18
- errors[key] = presence_error_message
20
+ errors[key] = error_message
19
21
  return
20
22
  end
21
23
 
@@ -28,14 +30,14 @@ class HashValidator::Validator::HashValidator < HashValidator::Validator::Base
28
30
 
29
31
  if HashValidator::Base.strict?
30
32
  value.keys.each do |k|
31
- errors[k] = 'key not expected' unless validations[k]
33
+ errors[k] = "key not expected" unless validations[k]
32
34
  end
33
35
  end
34
36
 
35
37
  # Cleanup errors (remove any empty nested errors)
36
- errors.delete_if { |_,v| v.empty? }
38
+ errors.delete_if { |_, v| v.empty? }
37
39
  end
38
40
  end
39
41
 
40
42
 
41
- HashValidator.append_validator(HashValidator::Validator::HashValidator.new)
43
+ HashValidator.add_validator(HashValidator::Validator::HashValidator.new)
@@ -1,23 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class HashValidator::Validator::HexColorValidator < HashValidator::Validator::Base
2
4
  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'
5
+ super("hex_color") # The name of the validator
8
6
  end
9
7
 
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
8
+ def error_message
9
+ "is not a valid hex color"
14
10
  end
15
11
 
16
- private
17
-
18
- def valid_hex_color?(value)
19
- /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/.match?(value)
12
+ def valid?(value)
13
+ value.is_a?(String) && /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/.match?(value)
20
14
  end
21
15
  end
22
16
 
23
- HashValidator.append_validator(HashValidator::Validator::HexColorValidator.new)
17
+ HashValidator.add_validator(HashValidator::Validator::HexColorValidator.new)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
5
+ class HashValidator::Validator::IpValidator < HashValidator::Validator::Base
6
+ def initialize
7
+ super("ip") # The name of the validator
8
+ end
9
+
10
+ def error_message
11
+ "is not a valid IP address"
12
+ end
13
+
14
+ def valid?(value)
15
+ return false unless value.is_a?(String)
16
+ # Use IPAddr to validate both IPv4 and IPv6 addresses
17
+ IPAddr.new(value)
18
+ true
19
+ rescue IPAddr::Error, IPAddr::InvalidAddressError
20
+ false
21
+ end
22
+ end
23
+
24
+ HashValidator.add_validator(HashValidator::Validator::IpValidator.new)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HashValidator::Validator::Ipv4Validator < HashValidator::Validator::Base
4
+ def initialize
5
+ super("ipv4") # The name of the validator
6
+ end
7
+
8
+ def error_message
9
+ "is not a valid IPv4 address"
10
+ end
11
+
12
+ def valid?(value)
13
+ return false unless value.is_a?(String)
14
+ # IPv4 regex: 4 octets, each 0-255
15
+ ipv4_regex = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/
16
+ value.match?(ipv4_regex)
17
+ end
18
+ end
19
+
20
+ HashValidator.add_validator(HashValidator::Validator::Ipv4Validator.new)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
5
+ class HashValidator::Validator::Ipv6Validator < HashValidator::Validator::Base
6
+ def initialize
7
+ super("ipv6") # The name of the validator
8
+ end
9
+
10
+ def error_message
11
+ "is not a valid IPv6 address"
12
+ end
13
+
14
+ def valid?(value)
15
+ return false unless value.is_a?(String)
16
+ # Use IPAddr to validate IPv6 addresses (handles standard and compressed notation)
17
+ addr = IPAddr.new(value)
18
+ addr.ipv6?
19
+ rescue IPAddr::Error, IPAddr::InvalidAddressError
20
+ false
21
+ end
22
+ end
23
+
24
+ HashValidator.add_validator(HashValidator::Validator::Ipv6Validator.new)
@@ -1,23 +1,18 @@
1
- require 'json'
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
2
4
 
3
5
  class HashValidator::Validator::JsonValidator < HashValidator::Validator::Base
4
6
  def initialize
5
- super('json') # The name of the validator
6
- end
7
-
8
- def presence_error_message
9
- 'is not valid JSON'
7
+ super("json") # The name of the validator
10
8
  end
11
9
 
12
- def validate(key, value, _validations, errors)
13
- unless value.is_a?(String) && valid_json?(value)
14
- errors[key] = presence_error_message
15
- end
10
+ def error_message
11
+ "is not valid JSON"
16
12
  end
17
13
 
18
- private
19
-
20
- def valid_json?(value)
14
+ def valid?(value)
15
+ return false unless value.is_a?(String)
21
16
  JSON.parse(value)
22
17
  true
23
18
  rescue JSON::ParserError
@@ -25,4 +20,4 @@ class HashValidator::Validator::JsonValidator < HashValidator::Validator::Base
25
20
  end
26
21
  end
27
22
 
28
- HashValidator.append_validator(HashValidator::Validator::JsonValidator.new)
23
+ HashValidator.add_validator(HashValidator::Validator::JsonValidator.new)