respect 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/MIT-LICENSE +20 -0
- data/README.md +289 -0
- data/RELATED_WORK.md +40 -0
- data/RELEASE_NOTES.md +23 -0
- data/Rakefile +31 -0
- data/STATUS_MATRIX.html +137 -0
- data/lib/respect.rb +231 -0
- data/lib/respect/any_schema.rb +22 -0
- data/lib/respect/array_def.rb +28 -0
- data/lib/respect/array_schema.rb +203 -0
- data/lib/respect/boolean_schema.rb +32 -0
- data/lib/respect/composite_schema.rb +86 -0
- data/lib/respect/core_statements.rb +206 -0
- data/lib/respect/datetime_schema.rb +27 -0
- data/lib/respect/def_without_name.rb +6 -0
- data/lib/respect/divisible_by_validator.rb +20 -0
- data/lib/respect/doc_helper.rb +24 -0
- data/lib/respect/doc_parser.rb +37 -0
- data/lib/respect/dsl_dumper.rb +181 -0
- data/lib/respect/equal_to_validator.rb +20 -0
- data/lib/respect/fake_name_proxy.rb +116 -0
- data/lib/respect/float_schema.rb +27 -0
- data/lib/respect/format_validator.rb +136 -0
- data/lib/respect/global_def.rb +79 -0
- data/lib/respect/greater_than_or_equal_to_validator.rb +19 -0
- data/lib/respect/greater_than_validator.rb +19 -0
- data/lib/respect/has_constraints.rb +34 -0
- data/lib/respect/hash_def.rb +40 -0
- data/lib/respect/hash_schema.rb +218 -0
- data/lib/respect/in_validator.rb +19 -0
- data/lib/respect/integer_schema.rb +27 -0
- data/lib/respect/ip_addr_schema.rb +23 -0
- data/lib/respect/ipv4_addr_schema.rb +27 -0
- data/lib/respect/ipv6_addr_schema.rb +27 -0
- data/lib/respect/items_def.rb +21 -0
- data/lib/respect/json_schema_html_formatter.rb +143 -0
- data/lib/respect/less_than_or_equal_to_validator.rb +19 -0
- data/lib/respect/less_than_validator.rb +19 -0
- data/lib/respect/match_validator.rb +19 -0
- data/lib/respect/max_length_validator.rb +20 -0
- data/lib/respect/min_length_validator.rb +20 -0
- data/lib/respect/multiple_of_validator.rb +10 -0
- data/lib/respect/null_schema.rb +26 -0
- data/lib/respect/numeric_schema.rb +33 -0
- data/lib/respect/org3_dumper.rb +213 -0
- data/lib/respect/regexp_schema.rb +19 -0
- data/lib/respect/schema.rb +285 -0
- data/lib/respect/schema_def.rb +16 -0
- data/lib/respect/string_schema.rb +21 -0
- data/lib/respect/unit_test_helper.rb +37 -0
- data/lib/respect/uri_schema.rb +23 -0
- data/lib/respect/utc_time_schema.rb +17 -0
- data/lib/respect/validator.rb +51 -0
- data/lib/respect/version.rb +3 -0
- data/test/any_schema_test.rb +79 -0
- data/test/array_def_test.rb +113 -0
- data/test/array_schema_test.rb +487 -0
- data/test/boolean_schema_test.rb +89 -0
- data/test/composite_schema_test.rb +30 -0
- data/test/datetime_schema_test.rb +83 -0
- data/test/doc_helper_test.rb +34 -0
- data/test/doc_parser_test.rb +109 -0
- data/test/dsl_dumper_test.rb +395 -0
- data/test/fake_name_proxy_test.rb +138 -0
- data/test/float_schema_test.rb +146 -0
- data/test/format_validator_test.rb +224 -0
- data/test/hash_def_test.rb +126 -0
- data/test/hash_schema_test.rb +613 -0
- data/test/integer_schema_test.rb +142 -0
- data/test/ip_addr_schema_test.rb +78 -0
- data/test/ipv4_addr_schema_test.rb +71 -0
- data/test/ipv6_addr_schema_test.rb +71 -0
- data/test/json_schema_html_formatter_test.rb +214 -0
- data/test/null_schema_test.rb +46 -0
- data/test/numeric_schema_test.rb +294 -0
- data/test/org3_dumper_test.rb +784 -0
- data/test/regexp_schema_test.rb +54 -0
- data/test/respect_test.rb +108 -0
- data/test/schema_def_test.rb +405 -0
- data/test/schema_test.rb +290 -0
- data/test/string_schema_test.rb +209 -0
- data/test/support/circle.rb +11 -0
- data/test/support/color.rb +24 -0
- data/test/support/point.rb +11 -0
- data/test/support/respect/circle_schema.rb +16 -0
- data/test/support/respect/color_def.rb +19 -0
- data/test/support/respect/color_schema.rb +33 -0
- data/test/support/respect/point_schema.rb +19 -0
- data/test/support/respect/rgba_schema.rb +20 -0
- data/test/support/respect/universal_validator.rb +25 -0
- data/test/support/respect/user_macros.rb +12 -0
- data/test/support/rgba.rb +11 -0
- data/test/test_helper.rb +90 -0
- data/test/uri_schema_test.rb +54 -0
- data/test/utc_time_schema_test.rb +63 -0
- data/test/validator_test.rb +22 -0
- metadata +288 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module Respect
|
4
|
+
# Validate a string containing an IPv4 or IPv6 address but also accept IPAddr object.
|
5
|
+
class IPAddrSchema < StringSchema
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when NilClass
|
10
|
+
if allow_nil?
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
14
|
+
end
|
15
|
+
when IPAddr
|
16
|
+
object
|
17
|
+
else
|
18
|
+
FormatValidator.new(:ip_addr).validate(object)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class IPAddrSchema
|
23
|
+
end # module Respect
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module Respect
|
4
|
+
# Validate a string containing an IPv4 address but also accept IPAddr object.
|
5
|
+
class Ipv4AddrSchema < StringSchema
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when NilClass
|
10
|
+
if allow_nil?
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
14
|
+
end
|
15
|
+
when IPAddr
|
16
|
+
if object.ipv4?
|
17
|
+
object
|
18
|
+
else
|
19
|
+
raise ValidationError, "IPAddr object '#{object}' is not IPv4"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
FormatValidator.new(:ipv4_addr).validate(object)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end # class Ipv4AddrSchema
|
27
|
+
end # module Respect
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
3
|
+
module Respect
|
4
|
+
# Validate a string containing an IPv6 address but also accept IPAddr object.
|
5
|
+
class Ipv6AddrSchema < StringSchema
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when NilClass
|
10
|
+
if allow_nil?
|
11
|
+
nil
|
12
|
+
else
|
13
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
14
|
+
end
|
15
|
+
when IPAddr
|
16
|
+
if object.ipv6?
|
17
|
+
object
|
18
|
+
else
|
19
|
+
raise ValidationError, "IPAddr object '#{object}' is not IPv6"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
FormatValidator.new(:ipv6_addr).validate(object)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end # class Ipv6AddrSchema
|
27
|
+
end # module Respect
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Respect
|
2
|
+
class ItemsDef < GlobalDef
|
3
|
+
include_core_statements
|
4
|
+
include DefWithoutName
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@items = []
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def evaluation_result
|
13
|
+
@items
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_context(name, schema)
|
17
|
+
@items << schema
|
18
|
+
schema
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Respect
|
2
|
+
# Format a JSON schema in HTML with CSS class to allow highlighting.
|
3
|
+
class JSONSchemaHTMLFormatter
|
4
|
+
def initialize(json_schema)
|
5
|
+
@indent_level = 0
|
6
|
+
@indent_size = 2
|
7
|
+
@json_schema = json_schema
|
8
|
+
@css_class ||= {
|
9
|
+
json_highlight: "json_highlight",
|
10
|
+
plain: "plain",
|
11
|
+
key: "key",
|
12
|
+
keyword: "keyword",
|
13
|
+
string: "string",
|
14
|
+
numeric: "numeric",
|
15
|
+
comment: "comment",
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :css_class
|
20
|
+
|
21
|
+
def dump(output = "")
|
22
|
+
@output = output
|
23
|
+
@output ||= String.new
|
24
|
+
@output << "<div class=\"#{css_class[:json_highlight]}\"><pre>"
|
25
|
+
@output << dump_json(@json_schema)
|
26
|
+
@output << "</pre></div>\n"
|
27
|
+
@output
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def indent(&block)
|
33
|
+
@indent_level += 1
|
34
|
+
block.call
|
35
|
+
@indent_level -= 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def newline
|
39
|
+
"\n#{indentation}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def indentation
|
43
|
+
" " * @indent_level * @indent_size
|
44
|
+
end
|
45
|
+
|
46
|
+
def dump_json(json)
|
47
|
+
case json
|
48
|
+
when Hash
|
49
|
+
dump_hash(json)
|
50
|
+
when Array
|
51
|
+
dump_array(json)
|
52
|
+
else
|
53
|
+
dump_terminal(json)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def dump_hash(json)
|
58
|
+
result = plain_text("{")
|
59
|
+
indent do
|
60
|
+
result << newline
|
61
|
+
keys = json.keys
|
62
|
+
keys.each_with_index do |key, i|
|
63
|
+
if json[key].is_a? Hash
|
64
|
+
doc = ""
|
65
|
+
if json[key].key? "title"
|
66
|
+
doc << json[key]["title"]
|
67
|
+
json[key].delete("title")
|
68
|
+
end
|
69
|
+
if json[key].key? "description"
|
70
|
+
doc << "\n\n"
|
71
|
+
doc << json[key]["description"]
|
72
|
+
json[key].delete("description")
|
73
|
+
end
|
74
|
+
unless doc.empty?
|
75
|
+
result << comment(doc)
|
76
|
+
result << newline
|
77
|
+
end
|
78
|
+
end
|
79
|
+
result << span(css_class[:key], key.to_s.inspect) << plain_text(":") << " "
|
80
|
+
result << dump_json(json[key])
|
81
|
+
if i < keys.size - 1
|
82
|
+
result << plain_text(",")
|
83
|
+
result << newline
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
result << newline
|
88
|
+
result << plain_text("}")
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
def dump_array(json)
|
93
|
+
result = plain_text("[")
|
94
|
+
indent do
|
95
|
+
result << newline
|
96
|
+
json.each_with_index do |item, i|
|
97
|
+
result << dump_json(item)
|
98
|
+
if i < json.size - 1
|
99
|
+
result << plain_text(",")
|
100
|
+
result << newline
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
result << newline
|
105
|
+
result << plain_text("]")
|
106
|
+
result
|
107
|
+
end
|
108
|
+
|
109
|
+
def dump_terminal(json)
|
110
|
+
css = (case json
|
111
|
+
when TrueClass, FalseClass
|
112
|
+
css_class[:keyword]
|
113
|
+
when String
|
114
|
+
css_class[:string]
|
115
|
+
when Numeric
|
116
|
+
css_class[:numeric]
|
117
|
+
else
|
118
|
+
css_class[:plain]
|
119
|
+
end)
|
120
|
+
span(css, json.inspect)
|
121
|
+
end
|
122
|
+
|
123
|
+
def plain_text(text)
|
124
|
+
span(css_class[:plain], text)
|
125
|
+
end
|
126
|
+
|
127
|
+
def tag(tag, klass, value)
|
128
|
+
"<#{tag} class=\"#{klass}\">#{value}</#{tag}>"
|
129
|
+
end
|
130
|
+
|
131
|
+
def span(klass, value)
|
132
|
+
tag("span", klass, value)
|
133
|
+
end
|
134
|
+
|
135
|
+
def comment(text)
|
136
|
+
s = text.dup
|
137
|
+
s.sub!(/\n*\Z/m, '')
|
138
|
+
s.gsub!(/\n/m, "\n#{indentation}// ")
|
139
|
+
s.gsub!(/\s*\n/m, "\n")
|
140
|
+
span(css_class[:comment], "// #{s}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end # module Respect
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Respect
|
2
|
+
class LessThanOrEqualToValidator < Validator
|
3
|
+
def initialize(max)
|
4
|
+
@max = max
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless value <= @max
|
9
|
+
raise ValidationError, "#{value} is not less than or equal to #@max"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def to_h_org3
|
16
|
+
{ "maximum" => @max }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Respect
|
2
|
+
class LessThanValidator < Validator
|
3
|
+
def initialize(max)
|
4
|
+
@max = max
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless value < @max
|
9
|
+
raise ValidationError, "#{value} is not less than #@max"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def to_h_org3
|
16
|
+
{ "maximum" => @max, "exclusiveMaximum" => true }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Respect
|
2
|
+
class MatchValidator < Validator
|
3
|
+
def initialize(pattern)
|
4
|
+
@pattern = pattern
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless value =~ @pattern
|
9
|
+
raise ValidationError, "#{value.inspect} does not match #{@pattern.inspect}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def to_h_org3
|
16
|
+
{ "pattern" => @pattern.source }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Respect
|
2
|
+
class MaxLengthValidator < Validator
|
3
|
+
def initialize(max_length)
|
4
|
+
@max_length = max_length
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless value.length <= @max_length
|
9
|
+
raise ValidationError,
|
10
|
+
"#{value.inspect} must be at most #@max_length long but is #{value.length}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def to_h_org3
|
17
|
+
{ 'maxLength' => @max_length }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Respect
|
2
|
+
class MinLengthValidator < Validator
|
3
|
+
def initialize(min_length)
|
4
|
+
@min_length = min_length
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate(value)
|
8
|
+
unless value.length >= @min_length
|
9
|
+
raise ValidationError,
|
10
|
+
"#{value.inspect} must be at least #@min_length long but is #{value.length}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def to_h_org3
|
17
|
+
{ 'minLength' => @min_length }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Respect
|
2
|
+
class NullSchema < Schema
|
3
|
+
|
4
|
+
public_class_method :new
|
5
|
+
|
6
|
+
def validate(object)
|
7
|
+
case object
|
8
|
+
when String
|
9
|
+
if object == "null"
|
10
|
+
self.sanitized_object = nil
|
11
|
+
true
|
12
|
+
else
|
13
|
+
raise ValidationError,
|
14
|
+
"expected 'null' value but got '#{object}:#{object.class}'"
|
15
|
+
end
|
16
|
+
when NilClass
|
17
|
+
self.sanitized_object = nil
|
18
|
+
true
|
19
|
+
else
|
20
|
+
raise ValidationError,
|
21
|
+
"object is not of null type but a #{object.class}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end # class NullSchema
|
26
|
+
end # module Respect
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Respect
|
2
|
+
class NumericSchema < Schema
|
3
|
+
include HasConstraints
|
4
|
+
|
5
|
+
public_class_method :new
|
6
|
+
|
7
|
+
def validate_type(object)
|
8
|
+
case object
|
9
|
+
when String
|
10
|
+
if match_data = /^[-+]?\d+(\.\d+)?$/.match(object)
|
11
|
+
if match_data[1]
|
12
|
+
object.to_f
|
13
|
+
else
|
14
|
+
object.to_i
|
15
|
+
end
|
16
|
+
else
|
17
|
+
raise ValidationError, "malformed numeric value: `#{object}'"
|
18
|
+
end
|
19
|
+
when Integer, Float
|
20
|
+
object
|
21
|
+
when NilClass
|
22
|
+
if allow_nil?
|
23
|
+
nil
|
24
|
+
else
|
25
|
+
raise ValidationError, "object is nil but this #{self.class} does not allow nil"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise ValidationError, "object is not a numeric but a '#{object.class}'"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end # class NumericSchema
|
33
|
+
end # module Respect
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Respect
|
2
|
+
# Dump a schema to a hash representation following the format specified
|
3
|
+
# on {json-schema.org standard draft v3}[http://tools.ietf.org/id/draft-zyp-json-schema-03.html].
|
4
|
+
#
|
5
|
+
# The current implementation covers all the _Schema_ and _Validator_ classes
|
6
|
+
# defined in this package. User-defined {Schema} and {Validator} are not guarantee
|
7
|
+
# to work and may never work in the future. The _JSON-Schema_ standard is
|
8
|
+
# a general purpose standard and include only primitive type so it is
|
9
|
+
# very unlikely that it will include your custom schema and validator
|
10
|
+
# out of the box. However, if you can translate your schema/validator
|
11
|
+
# as a composition of primitive type mentioned in the standard it will work.
|
12
|
+
# That's why it is recommended to sub-class {CompositeSchema} when creating
|
13
|
+
# your own schema. User-defined are not properly supported yet as the
|
14
|
+
# API of this dumper is *experimental*. However, an easy way to extend
|
15
|
+
# both the schema and validator class hierarchies will be added in
|
16
|
+
# future releases.
|
17
|
+
class Org3Dumper
|
18
|
+
|
19
|
+
# Translation table mapping DSL options with json-schema.org v3
|
20
|
+
# options. The associated hash is injected in the output. Values
|
21
|
+
# are interpreted as follow:
|
22
|
+
# - :option_value represent the option value passed to the DSL option parameter.
|
23
|
+
# - a proc is called with the option value as argument and the result is used
|
24
|
+
# as the value for the output key if it is not nil.
|
25
|
+
# - other value are inserted verbatim.
|
26
|
+
OPTION_MAP = {
|
27
|
+
min_size: { 'minItems' => :option_value },
|
28
|
+
max_size: { 'maxItems' => :option_value },
|
29
|
+
uniq: { "uniqueItems" => Proc.new{|v| v if v } },
|
30
|
+
default: { "default" => Proc.new{|v| v unless v.nil? } },
|
31
|
+
required: { "required" => Proc.new{|v| true if required? } },
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def initialize(schema)
|
35
|
+
@schema = schema
|
36
|
+
end
|
37
|
+
|
38
|
+
def dump(output = nil)
|
39
|
+
@output = output
|
40
|
+
@output ||= Hash.new
|
41
|
+
@output = dump_schema(@schema, ignore: [:required])
|
42
|
+
@output
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :output
|
46
|
+
|
47
|
+
def dump_schema(schema, *args)
|
48
|
+
dispatch("dump_schema", schema.class, schema, *args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump_schema_for_schema(schema, params = {})
|
52
|
+
return nil if !schema.documented?
|
53
|
+
h = {}
|
54
|
+
h['type'] = dump_statement_name(schema)
|
55
|
+
# Dump generic options.
|
56
|
+
schema.options.each do |opt, opt_value|
|
57
|
+
next if params[:ignore] && params[:ignore].include?(opt)
|
58
|
+
if validator_class = Respect.validator_for(opt)
|
59
|
+
h.merge!(validator_class.new(opt_value).to_h(:org3))
|
60
|
+
elsif Org3Dumper::OPTION_MAP.has_key?(opt)
|
61
|
+
Org3Dumper::OPTION_MAP[opt].each do |k, v|
|
62
|
+
if v == :option_value
|
63
|
+
h[k] = (opt_value.is_a?(Numeric) ? opt_value : opt_value.dup)
|
64
|
+
elsif v.is_a?(Proc)
|
65
|
+
result = schema.instance_exec(opt_value, &v)
|
66
|
+
h[k] = result unless result.nil?
|
67
|
+
else
|
68
|
+
h[k] = v
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
h.merge!(dump_options(schema))
|
74
|
+
# Dump documentation
|
75
|
+
h["title"] = schema.title if schema.title
|
76
|
+
h["description"] = schema.description if schema.description
|
77
|
+
h
|
78
|
+
end
|
79
|
+
|
80
|
+
def dump_schema_for_hash_schema(schema, params = {})
|
81
|
+
h = dump_schema_for_schema(schema, params)
|
82
|
+
return nil if h.nil?
|
83
|
+
props = {}
|
84
|
+
pattern_props = {}
|
85
|
+
additional_props = {}
|
86
|
+
schema.properties.each do |prop, schema|
|
87
|
+
if prop.is_a?(Regexp)
|
88
|
+
if schema.optional?
|
89
|
+
# FIXME(Nicolas Despres): Find a better warning reporting system.
|
90
|
+
warn "pattern properties cannot be optional in json-schema.org draft v3"
|
91
|
+
else
|
92
|
+
# FIXME(Nicolas Despres): What do we do with regexp options such as 'i'?
|
93
|
+
schema_dump = dump_schema(schema)
|
94
|
+
pattern_props[prop.source] = schema_dump if schema_dump
|
95
|
+
end
|
96
|
+
else
|
97
|
+
if schema.optional?
|
98
|
+
schema_dump = dump_schema(schema)
|
99
|
+
additional_props[prop.to_s] = schema_dump if schema_dump
|
100
|
+
else
|
101
|
+
schema_dump = dump_schema(schema)
|
102
|
+
props[prop.to_s] = schema_dump if schema_dump
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
h['properties'] = props unless props.empty?
|
107
|
+
h['patternProperties'] = pattern_props unless pattern_props.empty?
|
108
|
+
if additional_props.empty?
|
109
|
+
if schema.options[:strict]
|
110
|
+
h['additionalProperties'] = false
|
111
|
+
end
|
112
|
+
else
|
113
|
+
h['additionalProperties'] = additional_props
|
114
|
+
end
|
115
|
+
h
|
116
|
+
end
|
117
|
+
|
118
|
+
def dump_schema_for_array_schema(schema, params = {})
|
119
|
+
h = dump_schema_for_schema(schema, params)
|
120
|
+
return nil if h.nil?
|
121
|
+
if schema.item
|
122
|
+
h['items'] = dump_schema(schema.item, ignore: [:required])
|
123
|
+
else
|
124
|
+
if schema.items && !schema.items.empty?
|
125
|
+
h['items'] = schema.items.map do |x|
|
126
|
+
dump_schema(x, ignore: [:required])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
if schema.extra_items && !schema.extra_items.empty?
|
130
|
+
h['additionalItems'] = schema.extra_items.map do |x|
|
131
|
+
dump_schema(x, ignore: [:required])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
h
|
136
|
+
end
|
137
|
+
|
138
|
+
def dump_schema_for_composite_schema(schema, params = {})
|
139
|
+
dump_schema(schema.schema, params)
|
140
|
+
end
|
141
|
+
|
142
|
+
def dump_statement_name(schema, *args)
|
143
|
+
dispatch("dump_statement_name", schema.class, schema, *args)
|
144
|
+
end
|
145
|
+
|
146
|
+
def dump_statement_name_for_schema(schema)
|
147
|
+
schema.class.statement_name
|
148
|
+
end
|
149
|
+
|
150
|
+
def dump_statement_name_for_hash_schema(schema)
|
151
|
+
"object"
|
152
|
+
end
|
153
|
+
|
154
|
+
def dump_statement_name_for_numeric_schema(schema)
|
155
|
+
"number"
|
156
|
+
end
|
157
|
+
|
158
|
+
def dump_statement_name_for_integer_schema(schema)
|
159
|
+
"integer"
|
160
|
+
end
|
161
|
+
|
162
|
+
def dump_statement_name_for_string_schema(schema)
|
163
|
+
"string"
|
164
|
+
end
|
165
|
+
|
166
|
+
def dump_options(schema, *args)
|
167
|
+
dispatch("dump_options", schema.class, schema, *args)
|
168
|
+
end
|
169
|
+
|
170
|
+
def dump_options_for_schema(schema)
|
171
|
+
{}
|
172
|
+
end
|
173
|
+
|
174
|
+
def dump_options_for_uri_schema(schema)
|
175
|
+
{ "format" => "uri" }
|
176
|
+
end
|
177
|
+
|
178
|
+
def dump_options_for_regexp_schema(schema)
|
179
|
+
{ "format" => "regex" }
|
180
|
+
end
|
181
|
+
|
182
|
+
def dump_options_for_datetime_schema(schema)
|
183
|
+
{ "format" => "date-time" }
|
184
|
+
end
|
185
|
+
|
186
|
+
def dump_options_for_ipv4_addr_schema(schema)
|
187
|
+
{ "format" => "ip-address" }
|
188
|
+
end
|
189
|
+
|
190
|
+
def dump_options_for_ipv6_addr_schema(schema)
|
191
|
+
{ "format" => "ipv6" }
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# Perform a virtual dispatch on a single object.
|
197
|
+
# FIXME(Nicolas Despres): Get me out of here and test me.
|
198
|
+
def dispatch(prefix, klass, object, *args, &block)
|
199
|
+
symbol = "#{prefix}_for_#{klass.name.demodulize.underscore}"
|
200
|
+
if respond_to? symbol
|
201
|
+
send(symbol, object, *args, &block)
|
202
|
+
else
|
203
|
+
if klass == BasicObject
|
204
|
+
raise NoMethodError, "undefined method '#{symbol}' for schema class #{object.class}"
|
205
|
+
else
|
206
|
+
dispatch(prefix, klass.superclass, object, *args, &block)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|