kwalify 0.2.0 → 0.3.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.
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
+ #