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