kwalify 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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