kwalify 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/ChangeLog +62 -0
- data/README.txt +38 -0
- data/bin/kwalify +185 -0
- data/doc/docstyle.css +188 -0
- data/doc/users-guide.html +706 -0
- data/examples/address-book/Makefile +4 -0
- data/examples/address-book/address-book.schema.yaml +43 -0
- data/examples/address-book/address-book.yaml +36 -0
- data/examples/invoice/Makefile +3 -0
- data/examples/invoice/invoice.schema.yaml +59 -0
- data/examples/invoice/invoice.yaml +32 -0
- data/lib/kwalify.rb +10 -0
- data/lib/kwalify/error-msg.rb +41 -0
- data/lib/kwalify/errors.rb +112 -0
- data/lib/kwalify/meta-validator.rb +137 -0
- data/lib/kwalify/types.rb +70 -0
- data/lib/kwalify/util/assert-diff.rb +44 -0
- data/lib/kwalify/util/option-parser.rb +220 -0
- data/lib/kwalify/util/yaml-helper.rb +82 -0
- data/lib/kwalify/validator.rb +286 -0
- data/setup.rb +1331 -0
- data/test/test.rb +364 -0
- data/todo.txt +21 -0
- metadata +66 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
##
|
2
|
+
## Kwalify schema example for address book
|
3
|
+
##
|
4
|
+
## $Release: 0.1.0 $
|
5
|
+
## copyright(c) 2005 kuwata-lab all rights reserved.
|
6
|
+
##
|
7
|
+
##
|
8
|
+
## NOTE: 'type: string' is omitted in this example because 'string' is default type.
|
9
|
+
##
|
10
|
+
|
11
|
+
type: seq
|
12
|
+
required: yes
|
13
|
+
sequence:
|
14
|
+
- type: map
|
15
|
+
required: yes
|
16
|
+
mapping:
|
17
|
+
"name":
|
18
|
+
#type: string
|
19
|
+
required: yes
|
20
|
+
"email":
|
21
|
+
#type: string
|
22
|
+
pattern: /@/
|
23
|
+
"tel":
|
24
|
+
#type: string
|
25
|
+
pattern: /^\d+-\d+-\d+$/
|
26
|
+
"birth":
|
27
|
+
type: date
|
28
|
+
"age":
|
29
|
+
type: integer
|
30
|
+
"zip":
|
31
|
+
#type: string
|
32
|
+
pattern: /^\d+-\d+$/
|
33
|
+
"addr":
|
34
|
+
#type: string
|
35
|
+
"blood":
|
36
|
+
#type: string
|
37
|
+
enum:
|
38
|
+
- A
|
39
|
+
- B
|
40
|
+
- O
|
41
|
+
- AB
|
42
|
+
"memo":
|
43
|
+
type: object
|
@@ -0,0 +1,36 @@
|
|
1
|
+
##
|
2
|
+
## address book example
|
3
|
+
##
|
4
|
+
|
5
|
+
- name: Sumire
|
6
|
+
email: amethyst@mail.com
|
7
|
+
tel: 012-111-1111
|
8
|
+
birth: 1985-01-01
|
9
|
+
age: 20
|
10
|
+
zip: 123-4567
|
11
|
+
addr: xxxx xxxx xxxx
|
12
|
+
blood: B
|
13
|
+
memo: My friend
|
14
|
+
|
15
|
+
- name: Kasumi
|
16
|
+
email: mist@mail.net
|
17
|
+
tel: 012-222-2222
|
18
|
+
birth: 1980-02-02
|
19
|
+
age: 25
|
20
|
+
blood: AB
|
21
|
+
memo:
|
22
|
+
- Home tutor
|
23
|
+
- A doctor (university)
|
24
|
+
|
25
|
+
- name: Nana
|
26
|
+
email: seven@mail.com
|
27
|
+
tel: 012-333-3333
|
28
|
+
birth: 1990-03-03
|
29
|
+
age: 15
|
30
|
+
blood: O
|
31
|
+
memo: My sister
|
32
|
+
|
33
|
+
- name: Sizuka
|
34
|
+
email: quiet@mail.org
|
35
|
+
memo: Firend of Sumire
|
36
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
###
|
2
|
+
### Kwalify schema example for invoice
|
3
|
+
###
|
4
|
+
### $Rev$
|
5
|
+
### $Release: 0.1.0 $
|
6
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
7
|
+
###
|
8
|
+
|
9
|
+
type: map
|
10
|
+
required: yes
|
11
|
+
mapping:
|
12
|
+
"invoice":
|
13
|
+
type: integer
|
14
|
+
required: yes
|
15
|
+
"date":
|
16
|
+
type: date
|
17
|
+
required: yes
|
18
|
+
"bill-to": &customer
|
19
|
+
type: map
|
20
|
+
required: yes
|
21
|
+
mapping:
|
22
|
+
"given":
|
23
|
+
required: yes
|
24
|
+
"family":
|
25
|
+
required: yes
|
26
|
+
"address":
|
27
|
+
type: map
|
28
|
+
required: yes
|
29
|
+
mapping:
|
30
|
+
"lines":
|
31
|
+
"city":
|
32
|
+
"state":
|
33
|
+
"postal":
|
34
|
+
type: integer
|
35
|
+
"ship-to": *customer
|
36
|
+
"product":
|
37
|
+
type: seq
|
38
|
+
required: yes
|
39
|
+
sequence:
|
40
|
+
- type: map
|
41
|
+
required: yes
|
42
|
+
mapping:
|
43
|
+
"sku":
|
44
|
+
required: yes
|
45
|
+
pattern: /^[A-Z0-9]+$/
|
46
|
+
"quantity":
|
47
|
+
type: integer
|
48
|
+
required: yes
|
49
|
+
"description":
|
50
|
+
"price":
|
51
|
+
type: float
|
52
|
+
"tax":
|
53
|
+
type: float
|
54
|
+
"total":
|
55
|
+
type: float
|
56
|
+
required: yes
|
57
|
+
"comments":
|
58
|
+
type: text
|
59
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
##
|
2
|
+
## from http://www.yaml.org/start.html
|
3
|
+
##
|
4
|
+
--- !clarkevans.com/^invoice
|
5
|
+
invoice: 34843
|
6
|
+
date : 2001-01-23
|
7
|
+
bill-to: &id001
|
8
|
+
given : Chris
|
9
|
+
family : Dumars
|
10
|
+
address:
|
11
|
+
lines: |
|
12
|
+
458 Walkman Dr.
|
13
|
+
Suite #292
|
14
|
+
city : Royal Oak
|
15
|
+
state : MI
|
16
|
+
postal : 48046
|
17
|
+
ship-to: *id001
|
18
|
+
product:
|
19
|
+
- sku : BL394D
|
20
|
+
quantity : 4
|
21
|
+
description : Basketball
|
22
|
+
price : 450.00
|
23
|
+
- sku : BL4438H
|
24
|
+
quantity : 1
|
25
|
+
description : Super Hoop
|
26
|
+
price : 2392.00
|
27
|
+
tax : 251.42
|
28
|
+
total: 4443.52
|
29
|
+
comments: >
|
30
|
+
Late afternoon is best.
|
31
|
+
Backup contact is Nancy
|
32
|
+
Billsmer @ 338-4338.
|
data/lib/kwalify.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
###
|
2
|
+
### $Rev: 8 $
|
3
|
+
### $Release: 0.1.0 $
|
4
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
5
|
+
###
|
6
|
+
|
7
|
+
module Kwalify
|
8
|
+
|
9
|
+
ERROR_MESSAGES = {}
|
10
|
+
|
11
|
+
## validation error
|
12
|
+
ERROR_MESSAGES[:missing_value] = "value required but not found."
|
13
|
+
ERROR_MESSAGES[:invalid_type] = "%s type expected but got %s."
|
14
|
+
ERROR_MESSAGES[:invalid_enum] = "'%s' is invalid enum value."
|
15
|
+
ERROR_MESSAGES[:invalid_pattern] = "'%s' is not matched to pattern %s."
|
16
|
+
ERROR_MESSAGES[:missing_key] = "key '%s' required but not found."
|
17
|
+
ERROR_MESSAGES[:invalid_key] = "key '%s' is not expected."
|
18
|
+
|
19
|
+
## ruleset error
|
20
|
+
ERROR_MESSAGES[:ruleset_not_hash] = "ruleset is not a hash."
|
21
|
+
ERROR_MESSAGES[:type_not_found] = "type '%s' is not found."
|
22
|
+
ERROR_MESSAGES[:required_not_boolean] = "'required:' is not 'true' nor 'false'."
|
23
|
+
ERROR_MESSAGES[:regexp_error] = "'pattern:' has regexp error (= %s)."
|
24
|
+
ERROR_MESSAGES[:enum_not_seq] = "'enum:' is not a sequence."
|
25
|
+
ERROR_MESSAGES[:sequence_not_seq] = "'sequence:' should be a sequence."
|
26
|
+
ERROR_MESSAGES[:sequence_no_elem] = "'sequence:' requires one element."
|
27
|
+
ERROR_MESSAGES[:sequence_too_many] = "'sequence:' requires just one element."
|
28
|
+
ERROR_MESSAGES[:mapping_not_map] = "'mapping:' should be a mapping."
|
29
|
+
ERROR_MESSAGES[:mapping_no_elem] = "'mapping:' requires at least one element."
|
30
|
+
ERROR_MESSAGES[:duplicate_key] = "key '%s' is duplicated."
|
31
|
+
ERROR_MESSAGES[:unexpected_key] = "'%s' is not expected."
|
32
|
+
ERROR_MESSAGES[:seq_has_enum] = "'enum:' is not available with sequence type."
|
33
|
+
ERROR_MESSAGES[:seq_has_pattern] = "'pattern:' is not available with sequence type."
|
34
|
+
ERROR_MESSAGES[:seq_has_mapping] = "'mapping:' is not available with sequence type."
|
35
|
+
ERROR_MESSAGES[:map_has_enum] = "'enum:' is not available with mapping type."
|
36
|
+
ERROR_MESSAGES[:map_has_pattern] = "'pattern:' is not available with mapping type."
|
37
|
+
ERROR_MESSAGES[:map_has_sequence] = "'sequence:' is not available with mapping type."
|
38
|
+
ERROR_MESSAGES[:scalar_has_sequence] = "scalar cannot take 'sequence:'."
|
39
|
+
ERROR_MESSAGES[:scalar_has_mapping] = "scalar cannot take 'mapping:'."
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
###
|
2
|
+
### $Rev: 8 $
|
3
|
+
### $Release: 0.1.0 $
|
4
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
5
|
+
###
|
6
|
+
|
7
|
+
module Kwalify
|
8
|
+
|
9
|
+
class KwalifyError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class AssertionError < KwalifyError
|
14
|
+
def initialize(msg)
|
15
|
+
super("*** assertion error: " + msg)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
class BaseError < KwalifyError
|
21
|
+
def initialize(message="", path=nil, schema=nil, error_symbol=nil)
|
22
|
+
super(message)
|
23
|
+
@error_symbol = error_symbol
|
24
|
+
@schema = schema
|
25
|
+
@path = path
|
26
|
+
end
|
27
|
+
attr_reader :error_symbol, :schema, :path
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
return super() if @path == nil
|
31
|
+
return "[/] #{super()}" if @path.empty?
|
32
|
+
return "[#{@path}] #{super()}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class SchemaError < BaseError
|
38
|
+
def initialize(message="", path=nil, schema=nil, error_symbol=nil)
|
39
|
+
super(message, path, schema, error_symbol)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
class ValidationError < BaseError
|
45
|
+
def initialize(message="", path=nil, schema=nil, error_symbol=nil, data=nil)
|
46
|
+
super(message, path, schema, error_symbol)
|
47
|
+
@data = data
|
48
|
+
end
|
49
|
+
attr_reader :data
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
module Errors
|
54
|
+
|
55
|
+
def assert_error(message="")
|
56
|
+
raise AssertionError.new(message)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def error_message(error_symbol, schema, data, arg)
|
61
|
+
msg = nil
|
62
|
+
case error_symbol
|
63
|
+
|
64
|
+
## validation error
|
65
|
+
when :missing_value
|
66
|
+
msg = ERROR_MESSAGES[error_symbol]
|
67
|
+
when :invalid_type
|
68
|
+
msg = ERROR_MESSAGES[error_symbol] % [ schema.type, data.class.name ]
|
69
|
+
when :invalid_enum
|
70
|
+
msg = ERROR_MESSAGES[error_symbol] % [ data.to_s ]
|
71
|
+
when :invalid_pattern
|
72
|
+
msg = ERROR_MESSAGES[error_symbol] % [ data.to_s, schema.pattern.inspect ]
|
73
|
+
when :missing_key
|
74
|
+
msg = ERROR_MESSAGES[error_symbol] % [ arg ] # arg is keyname
|
75
|
+
when :invalid_key
|
76
|
+
msg = ERROR_MESSAGES[error_symbol] % [ arg ] # arg is keyname
|
77
|
+
|
78
|
+
## schema error
|
79
|
+
when :unexpected_key
|
80
|
+
msg = ERROR_MESSAGES[error_symbol] % [ arg ] # arg is keyname
|
81
|
+
when :duplicate_key
|
82
|
+
msg = ERROR_MESSAGES[error_symbol] % [ arg ] # arg is keyname
|
83
|
+
when :type_not_found
|
84
|
+
msg = ERROR_MESSAGES[error_symbol] % [ arg ] # arg is typename
|
85
|
+
when :regexp_error
|
86
|
+
msg = ERROR_MESSAGES[error_symbol] % [ data ]
|
87
|
+
else
|
88
|
+
msg = ERROR_MESSAGES[error_symbol]
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
return msg
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def validate_error(error_symbol, schema, path, data, arg=nil)
|
97
|
+
msg = error_message(error_symbol, schema, data, arg)
|
98
|
+
return ValidationError.new(msg, path, schema, error_symbol, data)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def schema_error(error_symbol, schema, path, arg=nil)
|
103
|
+
msg = error_message(error_symbol, schema, nil, arg)
|
104
|
+
return SchemaError.new(msg, path, schema, error_symbol)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
module_function :assert_error, :validate_error, :schema_error, :error_message
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
###
|
2
|
+
### $Rev: 9 $
|
3
|
+
### $Release: 0.1.0 $
|
4
|
+
### copyright(c) 2005 kuwata-lab all rights reserved.
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'kwalify/errors'
|
8
|
+
require 'kwalify/validator'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
module Kwalify
|
12
|
+
|
13
|
+
META_SCHEMA_DEF = <<'END'
|
14
|
+
|
15
|
+
name: MAIN
|
16
|
+
type: map
|
17
|
+
required: yes
|
18
|
+
mapping: &main-schema
|
19
|
+
"name":
|
20
|
+
type: string
|
21
|
+
"desc":
|
22
|
+
type: text
|
23
|
+
"type":
|
24
|
+
type: string
|
25
|
+
enum:
|
26
|
+
- seq
|
27
|
+
- sequence
|
28
|
+
#- list
|
29
|
+
- map
|
30
|
+
- mapping
|
31
|
+
#- hash
|
32
|
+
- string
|
33
|
+
- integer
|
34
|
+
- float
|
35
|
+
- number
|
36
|
+
- numeric
|
37
|
+
- bool
|
38
|
+
- boolean
|
39
|
+
- text
|
40
|
+
- date
|
41
|
+
- time
|
42
|
+
- object
|
43
|
+
- any
|
44
|
+
"required":
|
45
|
+
type: boolean
|
46
|
+
"enum":
|
47
|
+
type: seq
|
48
|
+
sequence:
|
49
|
+
- type: object
|
50
|
+
"pattern":
|
51
|
+
type: string
|
52
|
+
"sequence":
|
53
|
+
name: SEQUENCE
|
54
|
+
type: seq
|
55
|
+
sequence:
|
56
|
+
- type: map
|
57
|
+
mapping: *main-schema
|
58
|
+
name: MAIN
|
59
|
+
"mapping":
|
60
|
+
name: MAPPING
|
61
|
+
type: map
|
62
|
+
mapping:
|
63
|
+
'*':
|
64
|
+
type: map
|
65
|
+
mapping: *main-schema
|
66
|
+
name: MAIN
|
67
|
+
|
68
|
+
END
|
69
|
+
|
70
|
+
|
71
|
+
def self.meta_validator()
|
72
|
+
yaml = YAML.load(META_SCHEMA_DEF)
|
73
|
+
validator = Kwalify::Validator.new(yaml)
|
74
|
+
return validator
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def self.meta_validator2()
|
79
|
+
yaml = YAML.load(META_SCHEMA_DEF)
|
80
|
+
validator = Kwalify::Validator.new(yaml) { |schema, obj, errors, path|
|
81
|
+
next if obj == nil ## realy?
|
82
|
+
if schema.name == "MAIN"
|
83
|
+
hash = obj
|
84
|
+
if hash.key?('pattern')
|
85
|
+
val = hash['pattern']
|
86
|
+
pat = (val =~ /\A\/(.*)\/\z/ ? $1 : val)
|
87
|
+
begin
|
88
|
+
Regexp.compile(pat)
|
89
|
+
rescue RegexpError => ex
|
90
|
+
errors << Kwalify::Errors.validate_error(:regexp_error, schema, path, obj)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
type = hash['type']
|
94
|
+
if type == nil
|
95
|
+
if hash.key?('sequence')
|
96
|
+
type = 'seq'
|
97
|
+
elsif hash.key?('mapping')
|
98
|
+
type = 'map'
|
99
|
+
else
|
100
|
+
type = Kwalify::DEFAULT_TYPE
|
101
|
+
end
|
102
|
+
end
|
103
|
+
klass = Kwalify.type_table[type]
|
104
|
+
if klass == nil
|
105
|
+
errors << Kwalify::Errors.validate_error(:invalid_type, schema, path, obj)
|
106
|
+
elsif klass == Array
|
107
|
+
errors << Kwalify::Errors.validate_error(:seq_has_enum, schema, path, obj) if hash.key?('enum')
|
108
|
+
errors << Kwalify::Errors.validate_error(:seq_has_pattern, schema, path, obj) if hash.key?('pattern')
|
109
|
+
errors << Kwalify::Errors.validate_error(:seq_has_mapping, schema, path, obj) if hash.key?('mapping')
|
110
|
+
elsif klass == Hash
|
111
|
+
errors << Kwalify::Errors.validate_error(:map_has_enum, schema, path, obj) if hash.key?('enum')
|
112
|
+
errors << Kwalify::Errors.validate_error(:map_has_pattern, schema, path, obj) if hash.key?('pattern')
|
113
|
+
errors << Kwalify::Errors.validate_error(:map_has_sequence, schema, path, obj) if hash.key?('sequence')
|
114
|
+
else
|
115
|
+
errors << Kwalify::Errors.validate_error(:scalar_has_sequence, schema, path, obj) if hash.key?('sequence')
|
116
|
+
errors << Kwalify::Errors.validate_error(:scalar_has_mapping, schema, path, obj) if hash.key?('mapping')
|
117
|
+
end
|
118
|
+
elsif schema.name == "SEQUENCE"
|
119
|
+
if !obj.is_a?(Array)
|
120
|
+
errors << Kwalify::Errors.validate_error(:sequence_not_seq, schema, path, obj)
|
121
|
+
else
|
122
|
+
errors << Kwalify::Errors.validate_error(:sequence_no_elem, schema, path, obj) if obj.empty?
|
123
|
+
errors << Kwalify::Errors.validate_error(:sequence_too_many, schema, path, obj) if obj.length > 1
|
124
|
+
end
|
125
|
+
elsif schema.name == "MAPPING"
|
126
|
+
if !obj.is_a?(Hash)
|
127
|
+
errors << Kwalify::Errors.validate_error(:mapping_not_map, schema, path, obj)
|
128
|
+
else
|
129
|
+
errors << Kwalify::Errors.validate_error(:mapping_no_elem, schema, path, obj) if obj.empty?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
}
|
133
|
+
return validator
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|