kwalify 0.1.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/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
|
+
|