schemacop 2.4.7 → 3.0.0.rc0

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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +25 -1
  4. data/.travis.yml +2 -1
  5. data/CHANGELOG.md +8 -0
  6. data/README.md +41 -708
  7. data/README_V2.md +775 -0
  8. data/README_V3.md +683 -0
  9. data/Rakefile +8 -12
  10. data/VERSION +1 -1
  11. data/lib/schemacop.rb +35 -37
  12. data/lib/schemacop/base_schema.rb +37 -0
  13. data/lib/schemacop/railtie.rb +10 -0
  14. data/lib/schemacop/schema.rb +1 -60
  15. data/lib/schemacop/schema2.rb +22 -0
  16. data/lib/schemacop/schema3.rb +21 -0
  17. data/lib/schemacop/scoped_env.rb +25 -13
  18. data/lib/schemacop/v2.rb +26 -0
  19. data/lib/schemacop/{caster.rb → v2/caster.rb} +16 -2
  20. data/lib/schemacop/{collector.rb → v2/collector.rb} +5 -2
  21. data/lib/schemacop/{dupper.rb → v2/dupper.rb} +1 -1
  22. data/lib/schemacop/{field_node.rb → v2/field_node.rb} +4 -3
  23. data/lib/schemacop/v2/node.rb +142 -0
  24. data/lib/schemacop/{node_resolver.rb → v2/node_resolver.rb} +1 -1
  25. data/lib/schemacop/{node_supporting_field.rb → v2/node_supporting_field.rb} +8 -10
  26. data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +6 -3
  27. data/lib/schemacop/{node_with_block.rb → v2/node_with_block.rb} +3 -2
  28. data/lib/schemacop/v2/root_node.rb +6 -0
  29. data/lib/schemacop/v2/validator/array_validator.rb +32 -0
  30. data/lib/schemacop/{validator → v2/validator}/boolean_validator.rb +1 -1
  31. data/lib/schemacop/v2/validator/float_validator.rb +7 -0
  32. data/lib/schemacop/v2/validator/hash_validator.rb +37 -0
  33. data/lib/schemacop/v2/validator/integer_validator.rb +7 -0
  34. data/lib/schemacop/{validator → v2/validator}/nil_validator.rb +1 -1
  35. data/lib/schemacop/v2/validator/number_validator.rb +21 -0
  36. data/lib/schemacop/v2/validator/object_validator.rb +29 -0
  37. data/lib/schemacop/v2/validator/string_validator.rb +39 -0
  38. data/lib/schemacop/{validator → v2/validator}/symbol_validator.rb +1 -1
  39. data/lib/schemacop/v3.rb +45 -0
  40. data/lib/schemacop/v3/all_of_node.rb +27 -0
  41. data/lib/schemacop/v3/any_of_node.rb +28 -0
  42. data/lib/schemacop/v3/array_node.rb +219 -0
  43. data/lib/schemacop/v3/boolean_node.rb +16 -0
  44. data/lib/schemacop/v3/combination_node.rb +45 -0
  45. data/lib/schemacop/v3/context.rb +17 -0
  46. data/lib/schemacop/v3/dsl_scope.rb +46 -0
  47. data/lib/schemacop/v3/global_context.rb +114 -0
  48. data/lib/schemacop/v3/hash_node.rb +217 -0
  49. data/lib/schemacop/v3/integer_node.rb +13 -0
  50. data/lib/schemacop/v3/is_not_node.rb +32 -0
  51. data/lib/schemacop/v3/node.rb +214 -0
  52. data/lib/schemacop/v3/node_registry.rb +49 -0
  53. data/lib/schemacop/v3/number_node.rb +18 -0
  54. data/lib/schemacop/v3/numeric_node.rb +76 -0
  55. data/lib/schemacop/v3/object_node.rb +40 -0
  56. data/lib/schemacop/v3/one_of_node.rb +28 -0
  57. data/lib/schemacop/v3/reference_node.rb +49 -0
  58. data/lib/schemacop/v3/result.rb +58 -0
  59. data/lib/schemacop/v3/string_node.rb +124 -0
  60. data/lib/schemacop/v3/symbol_node.rb +13 -0
  61. data/schemacop.gemspec +24 -27
  62. data/test/lib/test_helper.rb +152 -0
  63. data/test/schemas/nested/group.rb +6 -0
  64. data/test/schemas/user.rb +7 -0
  65. data/test/unit/schemacop/v2/casting_test.rb +120 -0
  66. data/test/unit/schemacop/v2/collector_test.rb +47 -0
  67. data/test/unit/schemacop/v2/custom_check_test.rb +95 -0
  68. data/test/unit/schemacop/v2/custom_if_test.rb +97 -0
  69. data/test/unit/schemacop/v2/defaults_test.rb +95 -0
  70. data/test/unit/schemacop/v2/empty_test.rb +16 -0
  71. data/test/unit/schemacop/v2/nil_dis_allow_test.rb +43 -0
  72. data/test/unit/schemacop/v2/node_resolver_test.rb +28 -0
  73. data/test/unit/schemacop/v2/short_forms_test.rb +351 -0
  74. data/test/unit/schemacop/v2/types_test.rb +88 -0
  75. data/test/unit/schemacop/v2/validator_array_test.rb +99 -0
  76. data/test/unit/schemacop/v2/validator_boolean_test.rb +17 -0
  77. data/test/unit/schemacop/v2/validator_float_test.rb +59 -0
  78. data/test/unit/schemacop/v2/validator_hash_test.rb +95 -0
  79. data/test/unit/schemacop/v2/validator_integer_test.rb +48 -0
  80. data/test/unit/schemacop/v2/validator_nil_test.rb +15 -0
  81. data/test/unit/schemacop/v2/validator_number_test.rb +62 -0
  82. data/test/unit/schemacop/v2/validator_object_test.rb +141 -0
  83. data/test/unit/schemacop/v2/validator_string_test.rb +78 -0
  84. data/test/unit/schemacop/v2/validator_symbol_test.rb +18 -0
  85. data/test/unit/schemacop/v3/all_of_node_test.rb +199 -0
  86. data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
  87. data/test/unit/schemacop/v3/array_node_test.rb +805 -0
  88. data/test/unit/schemacop/v3/boolean_node_test.rb +126 -0
  89. data/test/unit/schemacop/v3/global_context_test.rb +164 -0
  90. data/test/unit/schemacop/v3/hash_node_test.rb +775 -0
  91. data/test/unit/schemacop/v3/integer_node_test.rb +323 -0
  92. data/test/unit/schemacop/v3/is_not_node_test.rb +173 -0
  93. data/test/unit/schemacop/v3/node_test.rb +148 -0
  94. data/test/unit/schemacop/v3/number_node_test.rb +292 -0
  95. data/test/unit/schemacop/v3/object_node_test.rb +170 -0
  96. data/test/unit/schemacop/v3/one_of_node_test.rb +187 -0
  97. data/test/unit/schemacop/v3/reference_node_test.rb +351 -0
  98. data/test/unit/schemacop/v3/string_node_test.rb +334 -0
  99. data/test/unit/schemacop/v3/symbol_node_test.rb +75 -0
  100. metadata +152 -145
  101. data/doc/Schemacop.html +0 -146
  102. data/doc/Schemacop/ArrayValidator.html +0 -329
  103. data/doc/Schemacop/BooleanValidator.html +0 -145
  104. data/doc/Schemacop/Caster.html +0 -379
  105. data/doc/Schemacop/Collector.html +0 -787
  106. data/doc/Schemacop/Dupper.html +0 -214
  107. data/doc/Schemacop/Exceptions.html +0 -115
  108. data/doc/Schemacop/Exceptions/InvalidSchemaError.html +0 -124
  109. data/doc/Schemacop/Exceptions/ValidationError.html +0 -124
  110. data/doc/Schemacop/FieldNode.html +0 -421
  111. data/doc/Schemacop/FloatValidator.html +0 -158
  112. data/doc/Schemacop/HashValidator.html +0 -293
  113. data/doc/Schemacop/IntegerValidator.html +0 -158
  114. data/doc/Schemacop/NilValidator.html +0 -145
  115. data/doc/Schemacop/Node.html +0 -1438
  116. data/doc/Schemacop/NodeResolver.html +0 -258
  117. data/doc/Schemacop/NodeSupportingField.html +0 -590
  118. data/doc/Schemacop/NodeSupportingType.html +0 -612
  119. data/doc/Schemacop/NodeWithBlock.html +0 -289
  120. data/doc/Schemacop/NumberValidator.html +0 -232
  121. data/doc/Schemacop/ObjectValidator.html +0 -298
  122. data/doc/Schemacop/RootNode.html +0 -171
  123. data/doc/Schemacop/Schema.html +0 -699
  124. data/doc/Schemacop/StringValidator.html +0 -295
  125. data/doc/Schemacop/SymbolValidator.html +0 -145
  126. data/doc/ScopedEnv.html +0 -351
  127. data/doc/_index.html +0 -379
  128. data/doc/class_list.html +0 -51
  129. data/doc/css/common.css +0 -1
  130. data/doc/css/full_list.css +0 -58
  131. data/doc/css/style.css +0 -496
  132. data/doc/file.README.html +0 -833
  133. data/doc/file_list.html +0 -56
  134. data/doc/frames.html +0 -17
  135. data/doc/index.html +0 -833
  136. data/doc/inheritance.graphml +0 -524
  137. data/doc/inheritance.pdf +0 -825
  138. data/doc/js/app.js +0 -303
  139. data/doc/js/full_list.js +0 -216
  140. data/doc/js/jquery.js +0 -4
  141. data/doc/method_list.html +0 -587
  142. data/doc/top-level-namespace.html +0 -112
  143. data/lib/schemacop/node.rb +0 -139
  144. data/lib/schemacop/root_node.rb +0 -4
  145. data/lib/schemacop/validator/array_validator.rb +0 -30
  146. data/lib/schemacop/validator/float_validator.rb +0 -5
  147. data/lib/schemacop/validator/hash_validator.rb +0 -35
  148. data/lib/schemacop/validator/integer_validator.rb +0 -5
  149. data/lib/schemacop/validator/number_validator.rb +0 -19
  150. data/lib/schemacop/validator/object_validator.rb +0 -27
  151. data/lib/schemacop/validator/string_validator.rb +0 -37
  152. data/test/casting_test.rb +0 -118
  153. data/test/collector_test.rb +0 -45
  154. data/test/custom_check_test.rb +0 -93
  155. data/test/custom_if_test.rb +0 -95
  156. data/test/defaults_test.rb +0 -93
  157. data/test/empty_test.rb +0 -14
  158. data/test/nil_dis_allow_test.rb +0 -41
  159. data/test/node_resolver_test.rb +0 -26
  160. data/test/short_forms_test.rb +0 -349
  161. data/test/test_helper.rb +0 -13
  162. data/test/types_test.rb +0 -84
  163. data/test/validator_array_test.rb +0 -97
  164. data/test/validator_boolean_test.rb +0 -15
  165. data/test/validator_float_test.rb +0 -57
  166. data/test/validator_hash_test.rb +0 -93
  167. data/test/validator_integer_test.rb +0 -46
  168. data/test/validator_nil_test.rb +0 -13
  169. data/test/validator_number_test.rb +0 -60
  170. data/test/validator_object_test.rb +0 -139
  171. data/test/validator_string_test.rb +0 -76
  172. data/test/validator_symbol_test.rb +0 -16
@@ -0,0 +1,142 @@
1
+ module Schemacop
2
+ module V2
3
+ class Node
4
+ attr_reader :options
5
+
6
+ class_attribute :allowed_options
7
+ self.allowed_options = {}.freeze
8
+
9
+ class_attribute :symbols
10
+ self.symbols = [].freeze
11
+
12
+ class_attribute :klasses
13
+ self.klasses = [].freeze
14
+
15
+ def self.option(key, default: nil)
16
+ self.allowed_options = allowed_options.merge(key => default)
17
+ end
18
+
19
+ option :if
20
+ option :check
21
+ option :cast
22
+ option :default
23
+
24
+ def type_label
25
+ str = (symbols.first || 'unknown').to_s
26
+ str += '*' if option?(:if)
27
+ return str
28
+ end
29
+
30
+ def self.symbol(symbol)
31
+ self.symbols += [symbol]
32
+ end
33
+
34
+ def self.clear_symbols
35
+ self.symbols = [].freeze
36
+ end
37
+
38
+ def self.klass(klass)
39
+ self.klasses += [klass]
40
+ end
41
+
42
+ def self.clear_klasses
43
+ self.klasses = [].freeze
44
+ end
45
+
46
+ def self.register(symbols: [], klasses: [], clear: true, before: nil)
47
+ NodeResolver.register(self, before: before)
48
+ symbols = [*symbols]
49
+ klasses = [*klasses]
50
+ if clear
51
+ clear_symbols
52
+ clear_klasses
53
+ end
54
+ symbols.each { |s| symbol s }
55
+ klasses.each { |k| klass k }
56
+ end
57
+
58
+ def self.type_matches?(type)
59
+ symbol_matches?(type) || class_matches?(type)
60
+ end
61
+
62
+ def self.symbol_matches?(type)
63
+ return false unless type.is_a?(Symbol)
64
+
65
+ symbols.include?(type)
66
+ end
67
+
68
+ def self.class_matches?(type)
69
+ return false unless type.is_a?(Class)
70
+
71
+ klasses.each do |klass|
72
+ return true if type <= klass
73
+ end
74
+ return false
75
+ end
76
+
77
+ def self.build(options, &block)
78
+ new(options, &block)
79
+ end
80
+
81
+ def initialize(options = {})
82
+ # Check and save given options
83
+ @options = self.class.allowed_options.merge(options)
84
+ if (obsolete_opts = @options.keys - self.class.allowed_options.keys).any?
85
+ fail Exceptions::InvalidSchemaError,
86
+ "Unrecognized option(s) #{obsolete_opts.inspect} for #{self.class.inspect}, allowed options: #{self.class.allowed_options.keys.inspect}."
87
+ end
88
+
89
+ if option?(:cast) && self.class.klasses.size > 1
90
+ fail Exceptions::InvalidSchemaError,
91
+ "Casting is only allowed for single-value datatypes, but type #{self.class.inspect} has classes "\
92
+ "#{self.class.klasses.map(&:inspect)}."
93
+ end
94
+ end
95
+
96
+ def option(key)
97
+ options[key]
98
+ end
99
+
100
+ def option?(key)
101
+ !!options[key]
102
+ end
103
+
104
+ def exec_block
105
+ fail Exceptions::InvalidSchemaError, 'Node does not support block.' if block_given?
106
+ end
107
+
108
+ def resolve_type_klass(type)
109
+ klass = NodeResolver.resolve(type)
110
+ unless klass
111
+ fail Exceptions::InvalidSchemaError, "No validation class found for type #{type.inspect}."
112
+ end
113
+
114
+ return klass
115
+ end
116
+
117
+ def validate(data, collector)
118
+ validate_custom_check(data, collector)
119
+ end
120
+
121
+ def type_matches?(data)
122
+ self.class.type_matches?(data.class) && type_filter_matches?(data)
123
+ end
124
+
125
+ def type_filter_matches?(data)
126
+ !option?(:if) || option(:if).call(data)
127
+ end
128
+
129
+ protected
130
+
131
+ def validate_custom_check(data, collector)
132
+ if option?(:check) && (check_result = option(:check).call(data)) != true
133
+ if check_result.is_a?(String)
134
+ collector.error "Custom :check failed: #{check_result}."
135
+ else
136
+ collector.error 'Custom :check failed.'
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class NodeResolver
3
3
  class_attribute :node_classes
4
4
  self.node_classes = [].freeze
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class NodeSupportingField < NodeWithBlock
3
3
  block_method :req?
4
4
  block_method :req!
@@ -23,7 +23,7 @@ module Schemacop
23
23
  field(*args, required: true, allow_nil: false, &block)
24
24
  end
25
25
 
26
- alias_method :req, :req!
26
+ alias req req!
27
27
 
28
28
  def opt?(*args, &block)
29
29
  field(*args, required: false, allow_nil: true, &block)
@@ -33,7 +33,7 @@ module Schemacop
33
33
  field(*args, required: false, allow_nil: false, &block)
34
34
  end
35
35
 
36
- alias_method :opt, :opt?
36
+ alias opt opt?
37
37
 
38
38
  protected
39
39
 
@@ -42,14 +42,12 @@ module Schemacop
42
42
 
43
43
  if @fields[name]
44
44
  @fields[name].type(*args, &block)
45
- else
46
- if args.any?
47
- @fields[name] = FieldNode.new(name, required) do
48
- type(*args, &block)
49
- end
50
- else
51
- @fields[name] = FieldNode.new(name, required, &block)
45
+ elsif args.any?
46
+ @fields[name] = FieldNode.new(name, required) do
47
+ type(*args, &block)
52
48
  end
49
+ else
50
+ @fields[name] = FieldNode.new(name, required, &block)
53
51
  end
54
52
 
55
53
  @fields[name].type(:nil) if allow_nil
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class NodeSupportingType < NodeWithBlock
3
3
  block_method :type
4
4
 
@@ -14,6 +14,7 @@ module Schemacop
14
14
 
15
15
  if @types.none?
16
16
  fail Exceptions::InvalidSchemaError, 'Block must contain a type definition or not be given at all.' if block_given?
17
+
17
18
  type :object
18
19
  end
19
20
 
@@ -46,8 +47,8 @@ module Schemacop
46
47
  # happens in here directly and not in the constructor. This way we can
47
48
  # always call 'type', even if we don't have one and the type is auto-guessed
48
49
  # as it formerly was the case in the constructor.
49
- def type(*args, &block)
50
- options = args.last.is_a?(Hash) ? args.pop : {}
50
+ def type(*args, **kwargs, &block)
51
+ options = kwargs
51
52
  types = [*args.shift]
52
53
  subtypes = args
53
54
 
@@ -101,9 +102,11 @@ module Schemacop
101
102
  def cast!(data, collector)
102
103
  @types.each do |type|
103
104
  next unless type.option?(:cast) && !type.type_matches?(data) && type.type_filter_matches?(data)
105
+
104
106
  caster = Caster.new(type.option(:cast), data, type.class.klasses.first)
105
107
 
106
108
  next unless caster.castable?
109
+
107
110
  begin
108
111
  data = caster.cast
109
112
  collector.override_value(data)
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class NodeWithBlock < Node
3
3
  class_attribute :block_methods
4
4
  self.block_methods = [].freeze
@@ -9,7 +9,8 @@ module Schemacop
9
9
 
10
10
  def exec_block(&block)
11
11
  return unless block_given?
12
- se = ScopedEnv.new(self, self.class.block_methods)
12
+
13
+ se = Schemacop::ScopedEnv.new(self, self.class.block_methods)
13
14
  se.instance_exec(&block)
14
15
  end
15
16
  end
@@ -0,0 +1,6 @@
1
+ module Schemacop
2
+ module V2
3
+ class RootNode < NodeSupportingType
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ module Schemacop
2
+ module V2
3
+ class ArrayValidator < NodeSupportingType
4
+ register symbols: :array, klasses: Array
5
+
6
+ option :min # Minimal number of elements
7
+ option :max # Maximal number of elements
8
+ option :nil # Whether to allow nil values
9
+
10
+ def initialize(*args)
11
+ super
12
+ type(:nil) if option(:nil)
13
+ end
14
+
15
+ def validate(data, collector)
16
+ validate_custom_check(data, collector)
17
+
18
+ if option?(:min) && data.size < option(:min)
19
+ collector.error "Array must have more (>=) than #{option(:min)} elements."
20
+ end
21
+ if option?(:max) && data.size > option(:max)
22
+ collector.error "Array must have less (<=) than #{option(:max)} elements."
23
+ end
24
+ data.each_with_index do |entry, index|
25
+ collector.path("[#{index}]", index, :array) do
26
+ validate_types(entry, collector)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class BooleanValidator < Node
3
3
  register symbols: :boolean, klasses: [TrueClass, FalseClass]
4
4
  end
@@ -0,0 +1,7 @@
1
+ module Schemacop
2
+ module V2
3
+ class FloatValidator < NumberValidator
4
+ register symbols: :float, klasses: Float, before: NumberValidator
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ module Schemacop
2
+ module V2
3
+ class HashValidator < NodeSupportingField
4
+ register symbols: :hash, klasses: Hash
5
+
6
+ option :allow_obsolete_keys
7
+
8
+ def validate(data, collector)
9
+ super
10
+
11
+ if data.is_a? ActiveSupport::HashWithIndifferentAccess
12
+ allowed_fields = @fields.keys.map { |k| k.is_a?(String) ? k.to_sym : k }
13
+ data_keys = data.keys.map { |k| k.is_a?(String) ? k.to_sym : k }
14
+
15
+ # If the same key is specified in the schema as string and symbol, we
16
+ # definitely expect a Ruby hash and not one with indifferent access
17
+ if @fields.keys.length != Set.new(allowed_fields).length
18
+ fail Exceptions::ValidationError, 'Hash expected, but got ActiveSupport::HashWithIndifferentAccess.'
19
+ end
20
+ else
21
+ allowed_fields = @fields.keys
22
+ data_keys = data.keys
23
+ end
24
+
25
+ obsolete_keys = data_keys - allowed_fields
26
+
27
+ if !option?(:allow_obsolete_keys) && obsolete_keys.any?
28
+ collector.error "Obsolete keys: #{obsolete_keys.inspect}."
29
+ end
30
+
31
+ @fields.each_value do |field|
32
+ field.validate(data, collector)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ module Schemacop
2
+ module V2
3
+ class IntegerValidator < NumberValidator
4
+ register symbols: :integer, klasses: Integer, before: NumberValidator
5
+ end
6
+ end
7
+ end
@@ -1,4 +1,4 @@
1
- module Schemacop
1
+ module Schemacop::V2
2
2
  class NilValidator < Node
3
3
  register symbols: :nil, klasses: NilClass
4
4
  end
@@ -0,0 +1,21 @@
1
+ module Schemacop
2
+ module V2
3
+ class NumberValidator < Node
4
+ register symbols: :number, klasses: [Integer, Float]
5
+
6
+ option :min
7
+ option :max
8
+
9
+ def validate(data, collector)
10
+ super
11
+
12
+ if option?(:min) && data < option(:min)
13
+ collector.error "Value must be >= #{option(:min)}."
14
+ end
15
+ if option?(:max) && data > option(:max)
16
+ collector.error "Value must be <= #{option(:max)}."
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module Schemacop
2
+ module V2
3
+ class ObjectValidator < Node
4
+ register symbols: :object, klasses: BasicObject
5
+
6
+ option :classes
7
+ option :strict
8
+
9
+ def type_label
10
+ "#{super} (#{classes.join(', ')})"
11
+ end
12
+
13
+ def type_matches?(data)
14
+ if option(:strict).is_a?(FalseClass)
15
+ sub_or_class = classes.map { |klass| data.class <= klass }.include?(true)
16
+ super && (classes.empty? || sub_or_class) && !data.nil?
17
+ else
18
+ super && (classes.empty? || classes.include?(data.class)) && !data.nil?
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def classes
25
+ [*option(:classes)]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module Schemacop
2
+ module V2
3
+ class StringValidator < Node
4
+ register symbols: :string, klasses: String
5
+
6
+ option :min
7
+ option :max
8
+
9
+ def initialize(options = {})
10
+ super(options)
11
+
12
+ validate_options!
13
+ end
14
+
15
+ def validate(data, collector)
16
+ super
17
+
18
+ if option?(:min) && data.size < option(:min)
19
+ collector.error "String must be longer (>=) than #{option(:min)} characters."
20
+ end
21
+ if option?(:max) && data.size > option(:max)
22
+ collector.error "String must be shorter (<=) than #{option(:max)} characters."
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def validate_options!
29
+ option_schema = Schema.new :integer, min: 0
30
+
31
+ if option?(:min) && option_schema.invalid?(option(:min))
32
+ fail Exceptions::InvalidSchemaError, 'String option :min must be an integer >= 0.'
33
+ elsif option?(:max) && option_schema.invalid?(option(:max))
34
+ fail Exceptions::InvalidSchemaError, 'String option :max must be an integer >= 0.'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end