kwalify 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/kwalify/rule.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  ###
2
- ### $Rev: 18 $
3
- ### $Release: 0.2.0 $
2
+ ### $Rev: 22 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -13,14 +13,16 @@ module Kwalify
13
13
  class Rule
14
14
  include Kwalify::ErrorHelper
15
15
 
16
- def initialize(hash=nil)
16
+ def initialize(hash=nil, parent=nil)
17
17
  configure(hash, "", {}) if hash
18
+ @parent = parent
18
19
  end
20
+ attr_accessor :parent
19
21
 
20
22
  def configure(hash, path="", rule_table={})
21
23
  unless hash.is_a?(Hash)
22
24
  #* key=:schema_notmap msg="schema definition is not a mapping."
23
- raise Kwalify.schema_error(:schema_notmap, nil, "/", nil)
25
+ raise Kwalify.schema_error(:schema_notmap, nil, (!path || path.empty? ? "/" : path), nil)
24
26
  end
25
27
 
26
28
  rule = self
@@ -62,15 +64,22 @@ module Kwalify
62
64
 
63
65
  when "required"
64
66
  @required = val
65
- unless val == nil || val.is_a?(Boolean)
67
+ unless val.is_a?(Boolean) #|| val == nil
66
68
  #* key=:required_notbool msg="not a boolean."
67
69
  raise schema_error(:required_notbool, rule, curr_path, val)
68
70
  end
69
71
 
70
72
  when "pattern"
71
- pat = (val =~ /\A\/(.*)\/([mi]?[mi]?)\z/ ? $1 : val)
73
+ unless val =~ /\A\/(.*)\/([mi]?[mi]?)\z/
74
+ #* key=:pattern_notmatch msg="should be '/..../'."
75
+ raise schema_error(:pattern_notmatch, rule, curr_path, val)
76
+ end
77
+ pat = $1; opt = $2
78
+ flag = 0
79
+ flag += Regexp::IGNORECASE if opt.include?("i")
80
+ flag += Regexp::MULTILINE if opt.include?("m")
72
81
  begin
73
- @pattern = Regexp.compile(pat)
82
+ @pattern = Regexp.compile(pat, flag)
74
83
  rescue RegexpError => ex
75
84
  #* key=:pattern_syntaxerr msg="has regexp error."
76
85
  raise schema_error(:pattern_syntaxerr, rule, curr_path, val)
@@ -100,6 +109,7 @@ module Kwalify
100
109
  end
101
110
 
102
111
  when "assert" # or "cond-expr" ?
112
+ @assert = val
103
113
  unless val.is_a?(String)
104
114
  #* key=:assert_notstr msg="not a string."
105
115
  raise schema_error(:assert_notstr, rule, curr_path, val)
@@ -109,7 +119,6 @@ module Kwalify
109
119
  raise schema_error(:assert_noval, rule, curr_path, val)
110
120
  end
111
121
  begin
112
- @assert = val
113
122
  @assert_proc = eval "proc { |val| #{val} }"
114
123
  rescue SyntaxError => ex
115
124
  #* key=:assert_syntaxerr msg="expression syntax error."
@@ -128,7 +137,7 @@ module Kwalify
128
137
  end
129
138
  val.each do |rkey, rval|
130
139
  case rkey
131
- when 'max', 'min'
140
+ when 'max', 'min', 'max-ex', 'min-ex'
132
141
  unless rval.is_a?(@klass)
133
142
  typename = Kwalify.word(@type) || @type
134
143
  #* key=:range_type_unmatch msg="not a %s."
@@ -139,6 +148,14 @@ module Kwalify
139
148
  raise schema_error(:range_undefined, rule, curr_path, "#{rkey}:")
140
149
  end
141
150
  end
151
+ if val.key?('max') && val.key?('max-ex')
152
+ #* key=:range_twomax msg="both 'max' and 'max-ex' are not available at once."
153
+ raise schema_error(:range_twomax, rule, curr_path, nil)
154
+ end
155
+ if val.key?('min') && val.key?('min-ex')
156
+ #* key=:range_twomin msg="both 'min' and 'min-ex' are not available at once."
157
+ raise schema_error(:range_twomin, rule, curr_path, nil)
158
+ end
142
159
 
143
160
  when "length" # or "width"
144
161
  @length = val
@@ -152,7 +169,7 @@ module Kwalify
152
169
  end
153
170
  val.each do |lkey, lval|
154
171
  case lkey
155
- when 'max', 'min'
172
+ when 'max', 'min', 'max-ex', 'min-ex'
156
173
  unless lval.is_a?(Integer)
157
174
  #* key=:length_notint msg="not an integer."
158
175
  raise schema_error(:length_notint, rule, "#{curr_path}/#{lkey}", lval)
@@ -162,6 +179,53 @@ module Kwalify
162
179
  raise schema_error(:length_undefined, rule, curr_path, "#{lkey}:")
163
180
  end
164
181
  end
182
+ if val.key?('max') && val.key?('max-ex')
183
+ #* key=:length_twomax msg="both 'max' and 'max-ex' are not available at once."
184
+ raise schema_error(:length_twomax, rule, curr_path, nil)
185
+ end
186
+ if val.key?('min') && val.key?('min-ex')
187
+ #* key=:length_twomin msg="both 'min' and 'min-ex' are not available at once."
188
+ raise schema_error(:length_twomin, rule, curr_path, nil)
189
+ end
190
+
191
+ when "ident"
192
+ @ident = val
193
+ @required = true
194
+ unless val.is_a?(Boolean)
195
+ #* key=:ident_notbool msg="not a boolean."
196
+ raise schema_error(:ident_notbool, rule, curr_path, val)
197
+ end
198
+ if @type == 'map' || @type == 'seq'
199
+ #* key=:ident_notscalar msg="is available only with a scalar type."
200
+ raise schema_error(:ident_notscalar, rule, path, "ident:")
201
+ end
202
+ if path.empty?
203
+ #* key=:ident_onroot msg="is not available on root element."
204
+ raise schema_error(:ident_onroot, rule, "/", "ident:")
205
+ end
206
+ unless @parent && @parent.type == 'map'
207
+ #* key=:ident_notmap msg="is available only with an element of mapping."
208
+ raise schema_error(:ident_notmap, rule, path, "ident:")
209
+ end
210
+
211
+ when "unique"
212
+ @unique = val
213
+ unless val.is_a?(Boolean)
214
+ #* key=:unique_notbool msg="not a boolean."
215
+ raise schema_error(:unique_notbool, rule, curr_path, val)
216
+ end
217
+ if @type == 'map' || @type == 'seq'
218
+ #* key=:unique_notscalar msg="is available only with a scalar type."
219
+ raise schema_error(:unique_notscalar, rule, path, "unique:")
220
+ end
221
+ if path.empty?
222
+ #* key=:unique_onroot msg="is not available on root element."
223
+ raise schema_error(:unique_onroot, rule, "/", "unique:")
224
+ end
225
+ #unless @parent && @parent.type == 'map'
226
+ # #* key=:unique_notmap msg="is available only with an element of mapping."
227
+ # raise schema_error(:unique_notmap, rule, path, "unique:")
228
+ #end
165
229
 
166
230
  when "sequence"
167
231
  if val != nil && !val.is_a?(Array)
@@ -178,7 +242,7 @@ module Kwalify
178
242
  elem ||= {}
179
243
  i = 0 # or 1? *index*
180
244
  rule = rule_table[elem.__id__]
181
- rule ||= Rule.new.configure(elem, "#{curr_path}/#{i}", rule_table)
245
+ rule ||= Rule.new(nil, self).configure(elem, "#{curr_path}/#{i}", rule_table)
182
246
  @sequence = [ rule ]
183
247
  end
184
248
 
@@ -196,7 +260,7 @@ module Kwalify
196
260
  #raise schema_error(:key_duplicate, rule, curr_path, key) if @mapping.key?(key)
197
261
  elem ||= {}
198
262
  rule = rule_table[elem.__id__]
199
- rule ||= Rule.new.configure(elem, "#{curr_path}/#{key}", rule_table)
263
+ rule ||= Rule.new(nil, self).configure(elem, "#{curr_path}/#{key}", rule_table)
200
264
  if key == '*'
201
265
  @mapping.default = rule
202
266
  else
@@ -259,6 +323,8 @@ module Kwalify
259
323
  attr_reader :assert_proc
260
324
  attr_reader :range
261
325
  attr_reader :length
326
+ attr_reader :ident
327
+ attr_reader :unique
262
328
 
263
329
 
264
330
  #def inspect()
@@ -280,6 +346,8 @@ module Kwalify
280
346
  str << " " * level << "required: #{@required}\n" if @required != nil
281
347
  str << " " * level << "pattern: #{@pattern.inspect}\n" if @pattern != nil
282
348
  str << " " * level << "assert: #{@assert}\n" if @assert != nil
349
+ str << " " * level << "ident: #{@ident}\n" if @ident != nil
350
+ str << " " * level << "unique: #{@unique}\n" if @unique != nil
283
351
  if @enum != nil
284
352
  str << " " * level << "enum:\n"
285
353
  @enum.each do |item|
@@ -290,16 +358,20 @@ module Kwalify
290
358
  str << " " * level
291
359
  str << "range: { "
292
360
  str << "max: #{@range['max'].inspect}" if @range['max'] != nil
293
- str << ", " if @range['max'] != nil && @range['min'] != nil
361
+ str << "max-ex: #{@range['max-ex'].inspect}" if @range['max-ex'] != nil
362
+ str << ", " if (@range['max'] || @range['max-ex']) && (@range['min'] || @range['min-ex'])
294
363
  str << "min: #{@range['min'].inspect}" if @range['min'] != nil
364
+ str << "min-ex: #{@range['min-ex'].inspect}" if @range['min-ex'] != nil
295
365
  str << "}\n"
296
366
  end
297
367
  if @length != nil
298
368
  str << " " * level
299
369
  str << "length: { "
300
370
  str << "max: #{@length['max'].inspect}" if @length['max'] != nil
301
- str << ", " if @length['max'] != nil && @length['min'] != nil
371
+ str << "max-ex: #{@length['max-ex'].inspect}" if @length['max-ex'] != nil
372
+ str << ", " if (@length['max'] || @length['max-ex']) && (@length['min'] || @length['min-ex'])
302
373
  str << "min: #{@length['min'].inspect}" if @length['min'] != nil
374
+ str << "min-ex: #{@length['min-ex'].inspect}" if @length['min-ex'] != nil
303
375
  str << "}\n"
304
376
  end
305
377
  @sequence.each do |rule|
data/lib/kwalify/types.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  ###
2
2
  ### $Rev: 18 $
3
- ### $Release: 0.2.0 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -1,6 +1,6 @@
1
1
  ###
2
2
  ### $Rev: 5 $
3
- ### $Release: 0.2.0 $
3
+ ### $Release: 0.3.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.2.0 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -1,6 +1,6 @@
1
1
  ###
2
2
  ### $Rev: 10 $
3
- ### $Release: 0.2.0 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -1,6 +1,6 @@
1
1
  ###
2
- ### $Rev: 18 $
3
- ### $Release: 0.2.0 $
2
+ ### $Rev: 21 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -123,6 +123,14 @@ module Kwalify
123
123
  #* key=:range_toosmall msg="too small (< min %s)."
124
124
  errors << validate_error(:range_toosmall, rule, path, val, [rule.range['min'].to_s])
125
125
  end
126
+ if rule.range['max-ex'] && rule.range['max-ex'] <= val
127
+ #* key=:range_toolargeex msg="too large (>= max %s)."
128
+ errors << validate_error(:range_toolargeex, rule, path, val, [rule.range['max-ex'].to_s])
129
+ end
130
+ if rule.range['min-ex'] && rule.range['min-ex'] >= val
131
+ #* key=:range_toosmallex msg="too small (<= min %s)."
132
+ errors << validate_error(:range_toosmallex, rule, path, val, [rule.range['min-ex'].to_s])
133
+ end
126
134
  end
127
135
  if rule.length
128
136
  assert_error("val.class=#{val.class.name}") unless val.is_a?(String) || val.is_a?(Text)
@@ -135,6 +143,14 @@ module Kwalify
135
143
  #* key=:length_tooshort msg="too short (length %d < min %d)."
136
144
  errors << validate_error(:length_tooshort, rule, path, val, [len, rule.length['min']])
137
145
  end
146
+ if rule.length['max-ex'] && rule.length['max-ex'] <= len
147
+ #* key=:length_toolongex msg="too long (length %d >= max %d)."
148
+ errors << validate_error(:length_toolongex, rule, path, val, [len, rule.length['max-ex']])
149
+ end
150
+ if rule.length['min-ex'] && rule.length['min-ex'] >= len
151
+ #* key=:length_tooshortex msg="too short (length %d <= min %d)."
152
+ errors << validate_error(:length_tooshortex, rule, path, val, [len, rule.length['min-ex']])
153
+ end
138
154
  end
139
155
  end
140
156
 
@@ -144,11 +160,41 @@ module Kwalify
144
160
  assert_error("seq_rule.sequence.length==#{seq_rule.sequence.length} (expected 1)") unless seq_rule.sequence.length == 1
145
161
  return if list == nil
146
162
  rule = seq_rule.sequence[0]
147
- i = -1 # or 0? *index*
148
- list.each do |val|
149
- i += 1
163
+ list.each_with_index do |val, i|
150
164
  _validate(val, rule, "#{path}/#{i}", errors, done) ## validate recursively
151
165
  end
166
+ if rule.type == 'map'
167
+ unique_keys = []
168
+ rule.mapping.keys.each do |key|
169
+ map_rule = rule.mapping[key]
170
+ unique_keys << key if map_rule.unique || map_rule.ident
171
+ end
172
+ unique_keys.each do |key|
173
+ table = {}
174
+ list.each_with_index do |map, i|
175
+ val = map[key]
176
+ next if val == nil
177
+ curr_path = "#{path}/#{i}/#{key}"
178
+ if table[val]
179
+ #* key=:value_notunique msg="is already used at '%s'."
180
+ errors << validate_error(:value_notunique, rule, "#{path}/#{i}/#{key}", val, "#{path}/#{table[val]}/#{key}")
181
+ else
182
+ table[val] = i
183
+ end
184
+ end
185
+ end if !unique_keys.empty?
186
+ elsif rule.unique
187
+ table = {}
188
+ list.each_with_index do |val, i|
189
+ next if val == nil
190
+ if table[val]
191
+ # #* key=:value_notunique msg="is already used at '%s'."
192
+ errors << validate_error(:value_notunique, rule, "#{path}/#{i}", val, "#{path}/#{table[val]}")
193
+ else
194
+ table[val] = i
195
+ end
196
+ end
197
+ end
152
198
  end
153
199
 
154
200
 
@@ -1,6 +1,6 @@
1
1
  ###
2
- ### $Rev: 18 $
3
- ### $Release: 0.2.0 $
2
+ ### $Rev: 21 $
3
+ ### $Release: 0.3.0 $
4
4
  ### copyright(c) 2005 kuwata-lab all rights reserved.
5
5
  ###
6
6
 
@@ -338,6 +338,20 @@ rule-msg: |
338
338
  # :range_undefined : [/range] key 'mim:' undefined key.
339
339
  #
340
340
  ---
341
+ name: range_twomax
342
+ desc: range_twomax
343
+ #
344
+ schema: |
345
+ type: float
346
+ range: { max: 10.0, max-ex: 1.0 }
347
+ #
348
+ meta-msg: |
349
+ :range_twomax : [/range] both 'max' and 'max-ex' are not available at once.
350
+ #
351
+ rule-msg: |
352
+ :range_twomax : [/range] both 'max' and 'max-ex' are not available at once.
353
+ #
354
+ ---
341
355
  name: length_notmap
342
356
  desc: length_notmap
343
357
  #
@@ -396,6 +410,20 @@ rule-msg: |
396
410
  # :length_undefined : [/length] key 'maximum:' is undefined.
397
411
  #
398
412
  ---
413
+ name: length_twomax
414
+ desc: length_twomax
415
+ #
416
+ schema: |
417
+ type: str
418
+ length: { max: 10, max-ex: 1 }
419
+ #
420
+ meta-msg: |
421
+ :length_twomax : [/length] both 'max' and 'max-ex' are not available at once.
422
+ #
423
+ rule-msg: |
424
+ :length_twomax : [/length] both 'max' and 'max-ex' are not available at once.
425
+ #
426
+ ---
399
427
  name: sequence_notseq
400
428
  desc: sequence_notseq
401
429
  #
@@ -606,3 +634,145 @@ rule-msg: |
606
634
  # :enum_conflict : [/] 'length:': not available with 'enum:'.
607
635
  # :enum_conflict : [/] 'pattern:': not available with 'enum:'.
608
636
  #
637
+ ---
638
+ name: unique_notbool
639
+ desc: unique_notbool
640
+ #
641
+ schema: |
642
+ type: seq
643
+ sequence:
644
+ - type: map
645
+ mapping:
646
+ "id":
647
+ type: int
648
+ unique: 'yes'
649
+ "name":
650
+ type: str
651
+ #
652
+ meta-msg: |
653
+ :type_unmatch : [/sequence/0/mapping/id/unique] 'yes': not a boolean.
654
+ #
655
+ rule-msg: |
656
+ :unique_notbool : [/sequence/0/mapping/id/unique] 'yes': not a boolean.
657
+ #
658
+ ---
659
+ name: unique_notscalar
660
+ desc: unique_notscalar
661
+ #
662
+ schema: |
663
+ type: seq
664
+ sequence:
665
+ - type: map
666
+ mapping:
667
+ "id":
668
+ type: int
669
+ unique: yes
670
+ "values":
671
+ type: seq
672
+ unique: yes
673
+ sequence:
674
+ - type: str
675
+ #
676
+ meta-msg: |
677
+ :unique_notscalar : [/sequence/0/mapping/values] 'unique:': is available only with a scalar type.
678
+ #
679
+ rule-msg: |
680
+ :unique_notscalar : [/sequence/0/mapping/values] 'unique:': is available only with a scalar type.
681
+ #
682
+ ---
683
+ name: unique_root
684
+ desc: unique_root
685
+ #
686
+ schema: |
687
+ type: str
688
+ unique: yes
689
+ #
690
+ meta-msg: |
691
+ :unique_onroot : [/] 'unique:': is not available on root element.
692
+ #
693
+ rule-msg: |
694
+ :unique_onroot : [/] 'unique:': is not available on root element.
695
+ #
696
+ ---
697
+ name: ident_notbool
698
+ desc: ident_notbool
699
+ #
700
+ schema: |
701
+ type: seq
702
+ sequence:
703
+ - type: map
704
+ mapping:
705
+ "id":
706
+ type: int
707
+ ident: 'yes'
708
+ "name":
709
+ type: str
710
+ #
711
+ meta-msg: |
712
+ :type_unmatch : [/sequence/0/mapping/id/ident] 'yes': not a boolean.
713
+ #
714
+ rule-msg: |
715
+ :ident_notbool : [/sequence/0/mapping/id/ident] 'yes': not a boolean.
716
+ #
717
+ ---
718
+ name: ident_notscalar
719
+ desc: ident_notscalar
720
+ #
721
+ schema: |
722
+ type: seq
723
+ sequence:
724
+ - type: map
725
+ mapping:
726
+ "id":
727
+ type: int
728
+ ident: yes
729
+ "values":
730
+ type: seq
731
+ ident: yes
732
+ sequence:
733
+ - type: str
734
+ #
735
+ meta-msg: |
736
+ :ident_notscalar : [/sequence/0/mapping/values] 'ident:': is available only with a scalar type.
737
+ #
738
+ rule-msg: |
739
+ :ident_notscalar : [/sequence/0/mapping/values] 'ident:': is available only with a scalar type.
740
+ #
741
+ ---
742
+ name: ident_root
743
+ desc: ident_root
744
+ #
745
+ schema: |
746
+ type: str
747
+ ident: yes
748
+ #
749
+ meta-msg: |
750
+ :ident_onroot : [/] 'ident:': is not available on root element.
751
+ #
752
+ rule-msg: |
753
+ :ident_onroot : [/] 'ident:': is not available on root element.
754
+ #
755
+ ---
756
+ name: ident_notmap
757
+ desc: ident_notmap
758
+ #
759
+ schema: |
760
+ type: seq
761
+ sequence:
762
+ - type: map
763
+ mapping:
764
+ "id":
765
+ type: int
766
+ ident: yes
767
+ "values":
768
+ type: seq
769
+ sequence:
770
+ - type: str
771
+ ident: yes
772
+ #
773
+ meta-msg: |
774
+ :ident_notmap : [/sequence/0/mapping/values/sequence/0] 'ident:': is available only with an element of mapping.
775
+ #
776
+ rule-msg: |
777
+ :ident_notmap : [/sequence/0/mapping/values/sequence/0] 'ident:': is available only with an element of mapping.
778
+ #