kwalify 0.1.0 → 0.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.
@@ -1,6 +1,6 @@
1
1
  ###
2
2
  ### $Rev: 5 $
3
- ### $Release: 0.1.0 $
3
+ ### $Release: 0.2.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -1,6 +1,6 @@
1
1
  ###
2
2
  ### $Rev: 6 $
3
- ### $Release: 0.1.0 $
3
+ ### $Release: 0.2.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -1,6 +1,6 @@
1
1
  ###
2
- ### $Rev: 7 $
3
- ### $Release: 0.1.0 $
2
+ ### $Rev: 10 $
3
+ ### $Release: 0.2.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -12,7 +12,7 @@ module YamlHelper
12
12
  ## expand tab character to spaces
13
13
  ##
14
14
  ## ex.
15
- ## untabified_str = YAML::Helper.untabify(tabbed_str)
15
+ ## untabified_str = YamlHelper.untabify(tabbed_str)
16
16
  ##
17
17
  ## input:: String or IO
18
18
  ##
@@ -34,7 +34,7 @@ module YamlHelper
34
34
  ## { "name"=>"Bar", "gender"=>"F", "age"=>25, },
35
35
  ## { "name"=>"Baz", "gender"=>"M", "age"=>30, },
36
36
  ## ]
37
- ## hashtable = YAML::Helper.create_hashtable(hashlist, "name")
37
+ ## hashtable = YamlHelper.create_hashtable(hashlist, "name")
38
38
  ## p hashtable
39
39
  ## # => { "Foo" => { "name"=>"Foo", "gender"=>"M", "age"=>20, },
40
40
  ## "Bar" => { "name"=>"Bar", "gender"=>"F", "age"=>25, },
@@ -60,7 +60,7 @@ module YamlHelper
60
60
  ## get nested value directly.
61
61
  ##
62
62
  ## ex.
63
- ## val = YAML::Helper.get_value(obj, ['aaa', 0, 'xxx'])
63
+ ## val = YamlHelper.get_value(obj, ['aaa', 0, 'xxx'])
64
64
  ##
65
65
  ## This is equal to the following:
66
66
  ## begin
@@ -72,8 +72,8 @@ module YamlHelper
72
72
  def self.get_value(obj, path)
73
73
  val = obj
74
74
  path.each do |key|
75
+ return nil unless val.is_a?(Hash) || val.is_a?(Array)
75
76
  val = val[key]
76
- return val unless val
77
77
  end if path
78
78
  return val
79
79
  end
@@ -1,286 +1,179 @@
1
1
  ###
2
- ### $Rev: 9 $
3
- ### $Release: 0.1.0 $
2
+ ### $Rev: 18 $
3
+ ### $Release: 0.2.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
7
- require 'kwalify/error-msg'
7
+ require 'kwalify/messages'
8
8
  require 'kwalify/errors'
9
9
  require 'kwalify/types'
10
- require 'pp'
10
+ require 'kwalify/rule'
11
11
 
12
12
  module Kwalify
13
13
 
14
14
 
15
15
  ##
16
16
  ## ex.
17
- ## s = File.read('schema.yaml')
18
- ## validator = Kwalify::Validator.new(YAML.load(s))
19
- ## s = File.read('document.yaml')
20
- ## document = YAML.load(s)
17
+ ## schema = YAML.load_file('schema.yaml')
18
+ ## validator = Kwalify::Validator.new(schema)
19
+ ## document = YAML.load_file('document.yaml')
21
20
  ## error_list = validator.validate(document)
22
21
  ## unless error_list.empty?
23
- ## error_list.each { |error| print error.message }
22
+ ## error_list.each do |error|
23
+ ## puts "- [#{error.path}] #{error.message}"
24
+ ## end
24
25
  ## end
25
26
  ##
26
27
  class Validator
28
+ include Kwalify::ErrorHelper
29
+
27
30
  def initialize(hash, &block)
28
- raise KwalifyError.new("Validator#initalize(arg): arg is not a hash(==#{hash.class.name}).") unless hash.is_a?(Hash)
29
- @schema = Schema.new(hash)
31
+ @rule = Rule.new(hash)
30
32
  @block = block
31
33
  end
32
- attr_reader :schema
34
+ attr_reader :rule
33
35
 
34
- def validate(obj)
35
- @schema.validate(obj, &@block)
36
- end
37
36
 
38
- def inspect
39
- @schema.inspect
37
+ def _inspect
38
+ @rule._inspect
40
39
  end
41
- end
42
-
43
-
44
- class Schema
45
- include Kwalify::Errors
46
-
47
- def initialize(hash, path="", schema_table={})
48
- schema_table[hash.__id__] = self
49
- hash.each do |key, val|
50
- case key
51
- #when "id"
52
- # @id = val
53
- when "name"
54
- @name = val.to_s
55
- when "desc"
56
- @desc = val.to_s
57
- when "type"
58
- @type = val
59
- @klass = Kwalify::type_table[val.downcase]
60
- if @klass == nil
61
- begin
62
- @klass = Kernel.const_get(val)
63
- rescue NameError
64
- raise schema_error(:type_not_found, self, path)
65
- end
66
- end
67
- when "required"
68
- unless val.is_a?(Boolean)
69
- raise schema_error(:required_not_boolean, self, path)
70
- else
71
- @required = val
72
- end
73
- when "pattern"
74
- pat = (val =~ /\A\/(.*)\/\z/ ? $1 : val)
75
- begin
76
- @pattern = Regexp.compile(pat)
77
- rescue RegexpError => ex
78
- raise schema_error(:regexp_error, self, path)
79
- end
80
- when "enum"
81
- unless val.is_a?(Array)
82
- raise schema_error(:enum_not_seq, self, path)
83
- else
84
- @enum = val
85
- end
86
- when "sequence"
87
- if !val.is_a?(Array)
88
- raise schema_error(:sequence_not_seq, self, path)
89
- elsif val.length == 0
90
- raise schema_error(:sequence_no_elem, self, path)
91
- elsif val.length > 1
92
- raise schema_error(:sequence_too_many, self, path)
93
- else
94
- elem = val[0]
95
- elem ||= {}
96
- schema = schema_table[elem.__id__]
97
- i = 1
98
- schema ||= Schema.new(elem, "#{path}/#{i}", schema_table)
99
- @sequence = [ schema ]
100
- end
101
- when "mapping"
102
- if !val.is_a?(Hash)
103
- raise schema_error(:mapping_not_map, self, path)
104
- elsif val.empty?
105
- raise schema_error(:mapping_no_elem, self, path)
106
- else
107
- @mapping = {}
108
- val.each do |key, elem|
109
- raise schema_error(:duplicate_key, self, path, key) if @mapping.key?(key)
110
- elem ||= {}
111
- schema = schema_table[elem.__id__]
112
- schema ||= Schema.new(elem, "#{path}/#{key}", schema_table)
113
- if key == '*'
114
- @mapping.default = schema
115
- else
116
- @mapping[key] = schema
117
- end
118
- end
119
- end
120
- else
121
- raise schema_error(:unexpected_key, self, path, key)
122
- end
123
- end
124
40
 
125
- if @type == nil
126
- if @sequence
127
- @type = 'seq'
128
- elsif @mapping
129
- @type = 'map'
130
- else
131
- @type = Kwalify::DEFAULT_TYPE
132
- end
133
- @klass = Kwalify::type_table[@type]
134
- end
135
-
136
- if @klass == Array
137
- raise schema_error(:seq_has_enum, self, path) if @enum
138
- raise schema_error(:seq_has_pattern, self, path) if @pattern
139
- raise schema_error(:seq_has_mapping, self, path) if @mapping
140
- elsif @klass == Hash
141
- raise schema_error(:map_has_enum, self, path) if @enum
142
- raise schema_error(:map_has_pattern, self, path) if @pattern
143
- raise schema_error(:map_has_sequence, self, path) if @sequence
144
- else
145
- raise schema_error(:scalar_has_sequence, self, path) if @sequence
146
- raise schema_error(:scalar_has_mapping, self, path) if @mapping
147
- end
148
41
 
42
+ def validate(val)
43
+ path = ""; errors = []; done = {}
44
+ _validate(val, @rule, path, errors, done)
45
+ return errors
149
46
  end
150
47
 
151
- #attr_reader :id
152
- attr_accessor :name
153
- attr_accessor :desc
154
- attr_accessor :enum
155
- attr_accessor :required
156
- attr_accessor :type
157
- attr_accessor :klass
158
- attr_accessor :pattern
159
- attr_accessor :elements
160
- #attr_accessor :constraints
161
48
 
49
+ protected
162
50
 
163
- def inspect()
164
- str = ""; level = 0; done = {}
165
- _inspect(str, level, done)
166
- return str
167
- end
168
51
 
169
- def _inspect(str, level, done)
170
- done[self.__id__] = true
171
- str << " " * level << "name: #{@name}\n" if @name != nil
172
- str << " " * level << "type: #{@type}\n" if @type != nil
173
- str << " " * level << "klass: #{@klass.name}\n" if @klass != nil
174
- str << " " * level << "required: #{@required}\n" if @required != nil
175
- str << " " * level << "pattern: #{@pattern.inspect}\n" if @pattern != nil
176
- if @enum != nil
177
- str << " " * level << "enum:\n"
178
- @enum.each do |item|
179
- str << " " * (level+1) << "- #{item}\n"
180
- end
181
- end
182
- @sequence.each do |schema|
183
- if done[schema.__id__]
184
- str << " " * (level+1) << "- ...\n"
185
- else
186
- str << " " * (level+1) << "- \n"
187
- schema._inspect(str, level+2, done)
188
- end
189
- end if @sequence
190
- @mapping.each do |key, schema|
191
- if done[schema.__id__]
192
- str << " " * (level+1) << key << ": ...\n"
193
- else
194
- str << " " * (level+1) << key << ":\n"
195
- schema._inspect(str, level+2, done)
196
- end
197
- end if @mapping
52
+ def validate_hook(val, rule, path, errors)
198
53
  end
199
- protected :_inspect
200
54
 
201
55
 
202
- def validate(obj, &block)
203
- errors = []; path = ""; done = {}
204
- _validate(obj, errors, path, done, &block)
205
- return errors
206
- end
207
-
208
-
209
- def _validate(obj, errors=[], path="", done={}, &block)
210
- return true if done[obj.__id__]
211
- done[obj.__id__] = true
212
- if @required && obj == nil
213
- errors << validate_error(:missing_value, self, path, obj)
56
+ def _validate(val, rule, path, errors, done)
57
+ if Kwalify.collection_class?(val.class)
58
+ return true if done[val.__id__] # avoid infinite loop
59
+ done[val.__id__] = true
60
+ end
61
+ if rule.required && val == nil
62
+ #* key=:required_novalue msg="value required but none."
63
+ errors << validate_error(:required_novalue, rule, path, val)
214
64
  return
215
65
  end
216
- if @klass && obj != nil && !obj.is_a?(@klass)
217
- errors << validate_error(:invalid_type, self, path, obj)
66
+ if rule.klass && val != nil && !val.is_a?(rule.klass)
67
+ #* key=:type_unmatch msg="not a %s."
68
+ errors << validate_error(:type_unmatch, rule, path, val, [Kwalify.word(rule.type)])
218
69
  return
219
70
  end
220
71
  #
221
72
  n = errors.length
222
- if @sequence
223
- _validate_sequence(obj, errors, path, done, &block)
224
- elsif @mapping
225
- _validate_mapping(obj, errors, path, done, &block)
73
+ if rule.sequence
74
+ _validate_sequence(val, rule, path, errors, done)
75
+ elsif rule.mapping
76
+ _validate_mapping(val, rule, path, errors, done)
226
77
  else
227
- _validate_scalar(obj, errors, path, done, &block)
78
+ _validate_scalar(val, rule, path, errors, done)
228
79
  end
229
80
  return unless errors.length == n
230
81
  #
231
- yield(self, obj, errors, path) if block
82
+ validate_hook(val, rule, path, errors)
83
+ @block.call(val, rule, path, errors) if @block
232
84
  end
233
- protected :_validate
234
85
 
235
86
 
236
- def _validate_scalar(obj, errors, path, done, &block)
237
- assert_error("@sequence.class==#{@sequence.class.name} (expected NilClass)") if @sequence
238
- assert_error("@mapping.class==#{@mapping.class.name} (expected NilClass)") if @mapping
239
- return if obj == nil
240
- if @enum && !@enum.include?(obj)
241
- errors << validate_error(:invalid_enum, self, path, obj)
87
+ private
88
+
89
+
90
+ def _validate_scalar(val, rule, path, errors, done)
91
+ assert_error("rule.sequence.class==#{rule.sequence.class.name} (expected NilClass)") if rule.sequence
92
+ assert_error("rule.mapping.class==#{rule.mapping.class.name} (expected NilClass)") if rule.mapping
93
+ if rule.assert_proc
94
+ unless rule.assert_proc.call(val)
95
+ #* key=:assert_failed msg="assertion expression failed (%s)."
96
+ errors << validate_error(:assert_failed, rule, path, val, [rule.assert])
97
+ end
98
+ end
99
+ if rule.enum
100
+ unless rule.enum.include?(val)
101
+ keyname = File.basename(path)
102
+ keyname = 'enum' if keyname =~ /\A\d+\z/
103
+ #* key=:enum_notexist msg="invalid %s value."
104
+ errors << validate_error(:enum_notexist, rule, path, val, [keyname])
105
+ end
106
+ end
107
+ #
108
+ return if val == nil
109
+ #
110
+ if rule.pattern
111
+ unless val.to_s =~ rule.pattern
112
+ #* key=:pattern_unmatch msg="not matched to pattern %s."
113
+ errors << validate_error(:pattern_unmatch, rule, path, val, [rule.pattern.inspect])
114
+ end
242
115
  end
243
- if @pattern && obj.to_s !~ @pattern
244
- errors << validate_error(:invalid_pattern, self, path, obj)
116
+ if rule.range
117
+ assert_error("val.class=#{val.class.name}") unless Kwalify.scalar?(val)
118
+ if rule.range['max'] && rule.range['max'] < val
119
+ #* key=:range_toolarge msg="too large (> max %s)."
120
+ errors << validate_error(:range_toolarge, rule, path, val, [rule.range['max'].to_s])
121
+ end
122
+ if rule.range['min'] && rule.range['min'] > val
123
+ #* key=:range_toosmall msg="too small (< min %s)."
124
+ errors << validate_error(:range_toosmall, rule, path, val, [rule.range['min'].to_s])
125
+ end
126
+ end
127
+ if rule.length
128
+ assert_error("val.class=#{val.class.name}") unless val.is_a?(String) || val.is_a?(Text)
129
+ len = val.to_s.length
130
+ if rule.length['max'] && rule.length['max'] < len
131
+ #* key=:length_toolong msg="too long (length %d > max %d)."
132
+ errors << validate_error(:length_toolong, rule, path, val, [len, rule.length['max']])
133
+ end
134
+ if rule.length['min'] && rule.length['min'] > len
135
+ #* key=:length_tooshort msg="too short (length %d < min %d)."
136
+ errors << validate_error(:length_tooshort, rule, path, val, [len, rule.length['min']])
137
+ end
245
138
  end
246
139
  end
247
- private :_validate_scalar
248
140
 
249
141
 
250
- def _validate_sequence(list, errors, path, done, &block)
251
- assert_error("@sequence.class==#{@sequence.class.name} (expected Array)") unless @sequence.is_a?(Array)
252
- assert_error("@sequence.length==#{@sequence.length} (expected 1)") unless @sequence.length == 1
142
+ def _validate_sequence(list, seq_rule, path, errors, done)
143
+ assert_error("seq_rule.sequence.class==#{seq_rule.sequence.class.name} (expected Array)") unless seq_rule.sequence.is_a?(Array)
144
+ assert_error("seq_rule.sequence.length==#{seq_rule.sequence.length} (expected 1)") unless seq_rule.sequence.length == 1
253
145
  return if list == nil
254
- schema = @sequence[0]
255
- i = 0
256
- list.each do |obj|
146
+ rule = seq_rule.sequence[0]
147
+ i = -1 # or 0? *index*
148
+ list.each do |val|
257
149
  i += 1
258
- schema._validate(obj, errors, "#{path}/#{i}", done, &block) ## validate recursively
150
+ _validate(val, rule, "#{path}/#{i}", errors, done) ## validate recursively
259
151
  end
260
152
  end
261
- private :_validate_sequence
262
153
 
263
154
 
264
- def _validate_mapping(hash, errors, path, done, &block)
265
- assert_error("@mapping.class==#{@mapping.class.name} (expected Hash)") unless @mapping.is_a?(Hash)
155
+ def _validate_mapping(hash, map_rule, path, errors, done)
156
+ assert_error("map_rule.mapping.class==#{map_rule.mapping.class.name} (expected Hash)") unless map_rule.mapping.is_a?(Hash)
266
157
  return if hash == nil
267
- @mapping.each do |key, schema|
268
- if schema.required && !hash.key?(key)
269
- errors << validate_error(:missing_key, schema, path, hash, key)
158
+ map_rule.mapping.each do |key, rule|
159
+ if rule.required && !hash.key?(key)
160
+ #* key=:required_nokey msg="key '%s:' is required."
161
+ errors << validate_error(:required_nokey, rule, path, hash, [key])
270
162
  end
271
163
  end
272
164
  hash.each do |key, value|
273
- schema = @mapping[key]
274
- if schema
275
- ret = schema._validate(value, errors, "#{path}/#{key}", done, &block) ## validate recursively
165
+ rule = map_rule.mapping[key]
166
+ if rule
167
+ _validate(value, rule, "#{path}/#{key}", errors, done) ## validate recursively
276
168
  else
277
- errors << validate_error(:invalid_key, schema, path, hash, key)
169
+ #* key=:key_undefined msg="key '%s' is undefined."
170
+ errors << validate_error(:key_undefined, rule, path, nil, ["#{key}:"])
171
+ ##* key=:key_undefined msg="undefined key."
172
+ #errors << validate_error(:key_undefined, rule, path, "#{key}:")
278
173
  end
279
174
  end
280
175
  end
281
- private :_validate_mapping
282
-
283
176
 
284
177
  end
285
- end
286
178
 
179
+ end