kwalify 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,326 @@
1
+ ###
2
+ ### $Rev: 18 $
3
+ ### $Release: 0.2.0 $
4
+ ### copyright(c) 2005 kuwata-lab all rights reserved.
5
+ ###
6
+
7
+ require 'kwalify/messages'
8
+ require 'kwalify/errors'
9
+ require 'kwalify/types'
10
+
11
+ module Kwalify
12
+
13
+ class Rule
14
+ include Kwalify::ErrorHelper
15
+
16
+ def initialize(hash=nil)
17
+ configure(hash, "", {}) if hash
18
+ end
19
+
20
+ def configure(hash, path="", rule_table={})
21
+ unless hash.is_a?(Hash)
22
+ #* key=:schema_notmap msg="schema definition is not a mapping."
23
+ raise Kwalify.schema_error(:schema_notmap, nil, "/", nil)
24
+ end
25
+
26
+ rule = self
27
+ rule_table[hash.__id__] = rule
28
+
29
+ ## 'type:'
30
+ @type = hash['type']
31
+ @type = Kwalify::DEFAULT_TYPE if @type == nil
32
+ unless @type.is_a?(String)
33
+ #* key=:type_notstr msg="not a string."
34
+ raise schema_error(:type_notstr, rule, "#{path}/type", @type.to_s)
35
+ end
36
+ @klass = Kwalify.get_type_class(@type)
37
+ #if @klass == nil
38
+ # begin
39
+ # @klass = Kernel.const_get(@type)
40
+ # rescue NameError
41
+ # end
42
+ #end
43
+ unless @klass
44
+ #* key=:type_unknown msg="unknown type."
45
+ raise schema_error(:type_unknown, rule, "#{path}/type", @type.to_s)
46
+ end
47
+
48
+ ## other key
49
+ hash.each do |key, val|
50
+ curr_path = "#{path}/#{key}"
51
+ case key
52
+ #when "id"
53
+ # @id = val
54
+ when "name"
55
+ @name = val
56
+
57
+ when "desc"
58
+ @desc = val
59
+
60
+ when "type"
61
+ # done
62
+
63
+ when "required"
64
+ @required = val
65
+ unless val == nil || val.is_a?(Boolean)
66
+ #* key=:required_notbool msg="not a boolean."
67
+ raise schema_error(:required_notbool, rule, curr_path, val)
68
+ end
69
+
70
+ when "pattern"
71
+ pat = (val =~ /\A\/(.*)\/([mi]?[mi]?)\z/ ? $1 : val)
72
+ begin
73
+ @pattern = Regexp.compile(pat)
74
+ rescue RegexpError => ex
75
+ #* key=:pattern_syntaxerr msg="has regexp error."
76
+ raise schema_error(:pattern_syntaxerr, rule, curr_path, val)
77
+ end
78
+
79
+ when "enum"
80
+ @enum = val
81
+ unless val.is_a?(Array)
82
+ #* key=:enum_notseq msg="not a sequence."
83
+ raise schema_error(:enum_notseq, rule, curr_path, val)
84
+ end
85
+ if @type == 'seq' || @type == 'map' # unless Kwalify.scalar_class?(@klass)
86
+ #* key=:enum_notscalar msg="not available with seq or map."
87
+ raise schema_error(:enum_notscalar, rule, path, 'enum:')
88
+ end
89
+ elem_table = {}
90
+ @enum.each do |elem|
91
+ unless !elem_table[elem]
92
+ #* key=:enum_duplicate msg="duplicated enum value."
93
+ raise schema_error(:enum_duplicate, rule, curr_path, elem.to_s)
94
+ end
95
+ elem_table[elem] = true
96
+ unless elem.is_a?(@klass)
97
+ #* key=:enum_type_unmatch msg="%s type expected."
98
+ raise schema_error(:enum_type_unmatch, rule, curr_path, elem, [Kwalify.word(@type)])
99
+ end
100
+ end
101
+
102
+ when "assert" # or "cond-expr" ?
103
+ unless val.is_a?(String)
104
+ #* key=:assert_notstr msg="not a string."
105
+ raise schema_error(:assert_notstr, rule, curr_path, val)
106
+ end
107
+ unless val =~ /\bval\b/
108
+ #* key=:assert_noval msg="'val' is not used."
109
+ raise schema_error(:assert_noval, rule, curr_path, val)
110
+ end
111
+ begin
112
+ @assert = val
113
+ @assert_proc = eval "proc { |val| #{val} }"
114
+ rescue SyntaxError => ex
115
+ #* key=:assert_syntaxerr msg="expression syntax error."
116
+ raise schema_error(:assert_syntaxerr, rule, curr_path, val)
117
+ end
118
+
119
+ when "range"
120
+ @range = val
121
+ unless val.is_a?(Hash)
122
+ #* key=:range_notmap msg="not a mapping."
123
+ raise schema_error(:range_notmap, rule, curr_path, val)
124
+ end
125
+ if @type == 'map' || @type == 'seq' || @type == 'bool'
126
+ #* key=:range_notscalar msg="is available only with scalar type."
127
+ raise schema_error(:range_notscalar, rule, path, 'range:')
128
+ end
129
+ val.each do |rkey, rval|
130
+ case rkey
131
+ when 'max', 'min'
132
+ unless rval.is_a?(@klass)
133
+ typename = Kwalify.word(@type) || @type
134
+ #* key=:range_type_unmatch msg="not a %s."
135
+ raise schema_error(:range_type_unmatch, rule, "#{curr_path}/#{rkey}", rval, [typename])
136
+ end
137
+ else
138
+ #* key=:range_undefined msg="undefined key."
139
+ raise schema_error(:range_undefined, rule, curr_path, "#{rkey}:")
140
+ end
141
+ end
142
+
143
+ when "length" # or "width"
144
+ @length = val
145
+ unless val.is_a?(Hash)
146
+ #* key=:length_notmap msg="not a mapping."
147
+ raise schema_error(:length_notmap, rule, curr_path, val)
148
+ end
149
+ unless @type == 'str' || @type == 'text'
150
+ #* key=:length_nottext msg="is available only with string or text."
151
+ raise schema_error(:length_nottext, rule, path, 'length:')
152
+ end
153
+ val.each do |lkey, lval|
154
+ case lkey
155
+ when 'max', 'min'
156
+ unless lval.is_a?(Integer)
157
+ #* key=:length_notint msg="not an integer."
158
+ raise schema_error(:length_notint, rule, "#{curr_path}/#{lkey}", lval)
159
+ end
160
+ else
161
+ #* key=:length_undefined msg="undefined key."
162
+ raise schema_error(:length_undefined, rule, curr_path, "#{lkey}:")
163
+ end
164
+ end
165
+
166
+ when "sequence"
167
+ if val != nil && !val.is_a?(Array)
168
+ #* key=:sequence_notseq msg="not a sequence."
169
+ raise schema_error(:sequence_notseq, rule, curr_path, val)
170
+ elsif val == nil || val.empty?
171
+ #* key=:sequence_noelem msg="required one element."
172
+ raise schema_error(:sequence_noelem, rule, curr_path, val)
173
+ elsif val.length > 1
174
+ #* key=:sequence_toomany msg="required just one element."
175
+ raise schema_error(:sequence_toomany, rule, curr_path, val)
176
+ else
177
+ elem = val[0]
178
+ elem ||= {}
179
+ i = 0 # or 1? *index*
180
+ rule = rule_table[elem.__id__]
181
+ rule ||= Rule.new.configure(elem, "#{curr_path}/#{i}", rule_table)
182
+ @sequence = [ rule ]
183
+ end
184
+
185
+ when "mapping"
186
+ if val != nil && !val.is_a?(Hash)
187
+ #* key=:mapping_notmap msg="not a mapping."
188
+ raise schema_error(:mapping_notmap, rule, curr_path, val)
189
+ elsif val == nil || val.empty?
190
+ #* key=:mapping_noelem msg="required at least one element."
191
+ raise schema_error(:mapping_noelem, rule, curr_path, val)
192
+ else
193
+ @mapping = {}
194
+ val.each do |key, elem|
195
+ ##* key=:key_duplicate msg="key duplicated."
196
+ #raise schema_error(:key_duplicate, rule, curr_path, key) if @mapping.key?(key)
197
+ elem ||= {}
198
+ rule = rule_table[elem.__id__]
199
+ rule ||= Rule.new.configure(elem, "#{curr_path}/#{key}", rule_table)
200
+ if key == '*'
201
+ @mapping.default = rule
202
+ else
203
+ @mapping[key] = rule
204
+ end
205
+ end
206
+ end
207
+
208
+ else
209
+ #* key=:key_unknown msg="unknown key."
210
+ raise schema_error(:key_unknown, rule, path, "#{key}:")
211
+ end
212
+ end
213
+
214
+ if @type == 'seq'
215
+ #* key=:seq_nosequence msg="type 'seq' requires 'sequence:'."
216
+ raise Kwalify.validate_error(:seq_nosequence, rule, path, nil) unless hash.key?('sequence')
217
+ #* key=:seq_conflict msg="not available with sequence."
218
+ raise schema_error(:seq_conflict, rule, path, 'enum:') if @enum
219
+ raise schema_error(:seq_conflict, rule, path, 'pattern:') if @pattern
220
+ raise schema_error(:seq_conflict, rule, path, 'mapping:') if @mapping
221
+ raise schema_error(:seq_conflict, rule, path, 'range:') if @range
222
+ raise schema_error(:seq_conflict, rule, path, 'length:') if @length
223
+ elsif @type == 'map'
224
+ #* key=:map_nomapping msg="type 'map' requires 'mapping:'."
225
+ raise Kwalify.validate_error(:map_nomapping, rule, path, nil) unless hash.key?('mapping')
226
+ #* key=:map_conflict msg="not available with mapping."
227
+ raise schema_error(:map_conflict, rule, path, 'enum:') if @enum
228
+ raise schema_error(:map_conflict, rule, path, 'pattern:') if @pattern
229
+ raise schema_error(:map_conflict, rule, path, 'sequence:') if @sequence
230
+ raise schema_error(:map_conflict, rule, path, 'range:') if @range
231
+ raise schema_error(:map_conflict, rule, path, 'length:') if @length
232
+ else
233
+ #* key=:scalar_conflict msg="not available with scalar type."
234
+ raise schema_error(:scalar_conflict, rule, path, 'sequence:') if @sequence
235
+ raise schema_error(:scalar_conflict, rule, path, 'mapping:') if @mapping
236
+ if @enum
237
+ #* key=:enum_conflict msg="not available with 'enum:'."
238
+ raise schema_error(:enum_conflict, rule, path, 'range:') if @range
239
+ raise schema_error(:enum_conflict, rule, path, 'length:') if @length
240
+ raise schema_error(:enum_conflict, rule, path, 'pattern:') if @pattern
241
+ end
242
+ end
243
+
244
+ return self
245
+
246
+ end # end of def configure
247
+
248
+ #attr_reader :id
249
+ attr_reader :name
250
+ attr_reader :desc
251
+ attr_reader :enum
252
+ attr_reader :required
253
+ attr_reader :type
254
+ attr_reader :klass
255
+ attr_reader :pattern
256
+ attr_reader :sequence
257
+ attr_reader :mapping
258
+ attr_reader :assert
259
+ attr_reader :assert_proc
260
+ attr_reader :range
261
+ attr_reader :length
262
+
263
+
264
+ #def inspect()
265
+ # str = ""; level = 0; done = {}
266
+ # _inspect(str, level, done)
267
+ # return str
268
+ #end
269
+
270
+
271
+ #protected
272
+
273
+
274
+ def _inspect(str="", level=0, done={})
275
+ done[self.__id__] = true
276
+ str << " " * level << "name: #{@name}\n" if @name != nil
277
+ str << " " * level << "desc: #{@desc}\n" if @desc != nil
278
+ str << " " * level << "type: #{@type}\n" if @type != nil
279
+ str << " " * level << "klass: #{@klass.name}\n" if @klass != nil
280
+ str << " " * level << "required: #{@required}\n" if @required != nil
281
+ str << " " * level << "pattern: #{@pattern.inspect}\n" if @pattern != nil
282
+ str << " " * level << "assert: #{@assert}\n" if @assert != nil
283
+ if @enum != nil
284
+ str << " " * level << "enum:\n"
285
+ @enum.each do |item|
286
+ str << " " * (level+1) << "- #{item}\n"
287
+ end
288
+ end
289
+ if @range != nil
290
+ str << " " * level
291
+ str << "range: { "
292
+ str << "max: #{@range['max'].inspect}" if @range['max'] != nil
293
+ str << ", " if @range['max'] != nil && @range['min'] != nil
294
+ str << "min: #{@range['min'].inspect}" if @range['min'] != nil
295
+ str << "}\n"
296
+ end
297
+ if @length != nil
298
+ str << " " * level
299
+ str << "length: { "
300
+ str << "max: #{@length['max'].inspect}" if @length['max'] != nil
301
+ str << ", " if @length['max'] != nil && @length['min'] != nil
302
+ str << "min: #{@length['min'].inspect}" if @length['min'] != nil
303
+ str << "}\n"
304
+ end
305
+ @sequence.each do |rule|
306
+ if done[rule.__id__]
307
+ str << " " * (level+1) << "- ...\n"
308
+ else
309
+ str << " " * (level+1) << "- \n"
310
+ rule._inspect(str, level+2, done)
311
+ end
312
+ end if @sequence
313
+ @mapping.each do |key, rule|
314
+ if done[rule.__id__]
315
+ str << ' ' * (level+1) << '"' << key << "\": ...\n"
316
+ else
317
+ str << ' ' * (level+1) << '"' << key << "\":\n"
318
+ rule._inspect(str, level+2, done)
319
+ end
320
+ end if @mapping
321
+ return str
322
+ end
323
+
324
+ end
325
+
326
+ end
@@ -1,6 +1,6 @@
1
1
  ###
2
- ### $Rev: 7 $
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
 
@@ -11,60 +11,126 @@ module Kwalify
11
11
  module Boolean
12
12
  end
13
13
  end
14
- #module Boolean; end
15
14
  class TrueClass
16
15
  include Kwalify::Boolean
17
- #include Boolean
18
16
  end
19
17
  class FalseClass
20
18
  include Kwalify::Boolean
21
- #include Boolean
22
19
  end
20
+ #module Boolean; end
21
+ #class TrueClass
22
+ # include Boolean
23
+ #end
24
+ #class FalseClass
25
+ # include Boolean
26
+ #end
23
27
 
24
28
 
25
29
  module Kwalify
26
30
  module Text
27
31
  end
28
32
  end
29
- #module Text; end
30
33
  class String
31
34
  include Kwalify::Text
32
- #include Text
33
35
  end
34
36
  class Numeric
35
37
  include Kwalify::Text
36
- #include Text
38
+ end
39
+ #module Text; end
40
+ #class String
41
+ # include Text
42
+ #end
43
+ #class Numeric
44
+ # include Text
45
+ #end
46
+
47
+
48
+ module Kwalify
49
+ module Scalar
50
+ end
51
+ end
52
+ class String
53
+ include Kwalify::Scalar
54
+ end
55
+ class Numeric
56
+ include Kwalify::Scalar
57
+ end
58
+ class Date
59
+ include Kwalify::Scalar
60
+ end
61
+ class Time
62
+ include Kwalify::Scalar
63
+ end
64
+ class TrueClass
65
+ include Kwalify::Scalar
66
+ end
67
+ class FalseClass
68
+ include Kwalify::Scalar
69
+ end
70
+ class NilClass
71
+ include Kwalify::Scalar
72
+ end
73
+ module Kwalify
74
+ module Text
75
+ include Kwalify::Scalar
76
+ end
37
77
  end
38
78
 
39
79
 
40
80
  module Kwalify
41
81
 
42
- DEFAULT_TYPE = "string" ## use "string" as default of @type
82
+ DEFAULT_TYPE = "str" ## use "str" as default of @type
43
83
 
44
84
  @@type_table = {
45
85
  "seq" => Array,
46
- "sequence" => Array,
47
- #"list" => Array,
48
- #"array" => Array,
49
86
  "map" => Hash,
50
- "mapping" => Hash,
51
- #"hash" => Hash,
87
+ "str" => String,
88
+ #"string" => String,
52
89
  "text" => Text,
53
- "string" => String,
54
- "integer" => Integer,
90
+ "int" => Integer,
91
+ #"integer" => Integer,
55
92
  "float" => Float,
56
93
  "number" => Numeric,
57
94
  #"numeric" => Numeric,
58
95
  "date" => Date,
59
96
  "time" => Time,
60
- "boolean" => Boolean,
61
- #"bool" => Boolean,
62
- "object" => Object,
97
+ "timestamp" => Time,
98
+ "bool" => Boolean,
99
+ #"boolean" => Boolean,
100
+ #"object" => Object,
63
101
  "any" => Object,
102
+ "scalar" => Scalar,
64
103
  }
65
-
104
+
66
105
  def self.type_table
67
106
  return @@type_table
68
107
  end
69
108
 
109
+ def self.get_type_class(type)
110
+ klass = @@type_table[type]
111
+ #assert_error('type=#{type.inspect}') unless klass
112
+ return klass
113
+ end
114
+
115
+
116
+ module TypeHelper
117
+ def collection_class?(klass)
118
+ return klass == Array || klass == Hash
119
+ end
120
+
121
+ def scalar_class?(klass)
122
+ return klass != Array && klass != Hash && klass != Object
123
+ end
124
+
125
+ def collection?(val)
126
+ return collection_class?(val.class)
127
+ end
128
+
129
+ def scalar?(val)
130
+ return scalar_class?(val.class)
131
+ end
132
+ end
133
+
134
+ extend TypeHelper
135
+
70
136
  end