rng 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +64 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +3 -1
- data/README.adoc +402 -0
- data/lib/rng/any_name.rb +26 -0
- data/lib/rng/attribute.rb +58 -4
- data/lib/rng/choice.rb +60 -0
- data/lib/rng/data.rb +32 -0
- data/lib/rng/define.rb +51 -3
- data/lib/rng/element.rb +62 -16
- data/lib/rng/empty.rb +23 -0
- data/lib/rng/except.rb +62 -0
- data/lib/rng/external_ref.rb +28 -0
- data/lib/rng/grammar.rb +36 -0
- data/lib/rng/group.rb +60 -0
- data/lib/rng/include.rb +24 -0
- data/lib/rng/interleave.rb +58 -0
- data/lib/rng/list.rb +56 -0
- data/lib/rng/mixed.rb +58 -0
- data/lib/rng/name.rb +28 -0
- data/lib/rng/not_allowed.rb +23 -0
- data/lib/rng/ns_name.rb +31 -0
- data/lib/rng/one_or_more.rb +58 -0
- data/lib/rng/optional.rb +58 -0
- data/lib/rng/param.rb +30 -0
- data/lib/rng/parent_ref.rb +28 -0
- data/lib/rng/parse_rnc.rb +26 -0
- data/lib/rng/pattern.rb +24 -0
- data/lib/rng/ref.rb +28 -0
- data/lib/rng/rnc_parser.rb +351 -94
- data/lib/rng/start.rb +54 -5
- data/lib/rng/text.rb +26 -0
- data/lib/rng/to_rnc.rb +55 -0
- data/lib/rng/value.rb +29 -0
- data/lib/rng/version.rb +1 -1
- data/lib/rng/zero_or_more.rb +58 -0
- data/lib/rng.rb +29 -5
- data/rng.gemspec +3 -2
- data/spec/fixtures/rnc/address_book.rnc +10 -0
- data/spec/fixtures/rnc/complex_example.rnc +61 -0
- data/spec/fixtures/rng/address_book.rng +20 -0
- data/spec/fixtures/rng/relaxng.rng +335 -0
- data/spec/fixtures/rng/testSuite.rng +163 -0
- data/spec/fixtures/spectest.xml +6845 -0
- data/spec/rng/rnc_parser_spec.rb +6 -4
- data/spec/rng/rnc_roundtrip_spec.rb +121 -0
- data/spec/rng/schema_spec.rb +115 -166
- data/spec/rng/spectest_spec.rb +195 -0
- data/spec/spec_helper.rb +33 -0
- metadata +54 -7
- data/lib/rng/builder.rb +0 -158
- data/lib/rng/rng_parser.rb +0 -107
- data/lib/rng/schema.rb +0 -18
- data/spec/rng/rng_parser_spec.rb +0 -102
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
require "rng"
|
4
4
|
require "xml/c14n"
|
5
|
+
require "equivalent-xml"
|
6
|
+
require "nokogiri"
|
7
|
+
require "diffy"
|
5
8
|
|
6
9
|
RSpec.configure do |config|
|
7
10
|
# Enable flags like --only-failures and --next-failure
|
@@ -19,5 +22,35 @@ RSpec.configure do |config|
|
|
19
22
|
def normalize_xml(xml)
|
20
23
|
Xml::C14n.format(xml)
|
21
24
|
end
|
25
|
+
|
26
|
+
def format_xml(xml_string)
|
27
|
+
# Use Nokogiri to parse and format the XML
|
28
|
+
# Strip comments for comparison purposes - we care about structure, not documentation
|
29
|
+
doc = Nokogiri::XML(xml_string, &:noblanks)
|
30
|
+
|
31
|
+
# Remove all comment nodes before comparison
|
32
|
+
doc.xpath("//comment()").remove
|
33
|
+
|
34
|
+
doc.to_xml(indent: 2)
|
35
|
+
end
|
36
|
+
|
37
|
+
def diff_xml(actual, expected)
|
38
|
+
# Generate a readable diff between the formatted XML strings
|
39
|
+
Diffy::Diff.new(expected, actual, context: 3).to_s
|
40
|
+
end
|
22
41
|
end)
|
42
|
+
|
43
|
+
# Add custom matchers for XML comparison
|
44
|
+
RSpec::Matchers.define :be_equivalent_to_xml do |expected|
|
45
|
+
match do |actual|
|
46
|
+
@actual_formatted = format_xml(actual.to_s)
|
47
|
+
@expected_formatted = format_xml(expected.to_s)
|
48
|
+
EquivalentXml.equivalent?(@actual_formatted, @expected_formatted)
|
49
|
+
end
|
50
|
+
|
51
|
+
failure_message do
|
52
|
+
diff = diff_xml(@actual_formatted, @expected_formatted)
|
53
|
+
"Expected XML to be equivalent, but it wasn't.\n\nDiff:\n#{diff}"
|
54
|
+
end
|
55
|
+
end
|
23
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rng
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lutaml-model
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: parslet
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,7 +53,7 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: zeitwerk
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
@@ -64,23 +78,56 @@ files:
|
|
64
78
|
- ".gitignore"
|
65
79
|
- ".rspec"
|
66
80
|
- ".rubocop.yml"
|
81
|
+
- ".rubocop_todo.yml"
|
82
|
+
- CODE_OF_CONDUCT.md
|
67
83
|
- Gemfile
|
84
|
+
- README.adoc
|
68
85
|
- Rakefile
|
69
86
|
- lib/rng.rb
|
87
|
+
- lib/rng/any_name.rb
|
70
88
|
- lib/rng/attribute.rb
|
71
|
-
- lib/rng/
|
89
|
+
- lib/rng/choice.rb
|
90
|
+
- lib/rng/data.rb
|
72
91
|
- lib/rng/define.rb
|
73
92
|
- lib/rng/element.rb
|
93
|
+
- lib/rng/empty.rb
|
94
|
+
- lib/rng/except.rb
|
95
|
+
- lib/rng/external_ref.rb
|
96
|
+
- lib/rng/grammar.rb
|
97
|
+
- lib/rng/group.rb
|
98
|
+
- lib/rng/include.rb
|
99
|
+
- lib/rng/interleave.rb
|
100
|
+
- lib/rng/list.rb
|
101
|
+
- lib/rng/mixed.rb
|
102
|
+
- lib/rng/name.rb
|
103
|
+
- lib/rng/not_allowed.rb
|
104
|
+
- lib/rng/ns_name.rb
|
105
|
+
- lib/rng/one_or_more.rb
|
106
|
+
- lib/rng/optional.rb
|
107
|
+
- lib/rng/param.rb
|
108
|
+
- lib/rng/parent_ref.rb
|
109
|
+
- lib/rng/parse_rnc.rb
|
110
|
+
- lib/rng/pattern.rb
|
111
|
+
- lib/rng/ref.rb
|
74
112
|
- lib/rng/rnc_parser.rb
|
75
|
-
- lib/rng/rng_parser.rb
|
76
|
-
- lib/rng/schema.rb
|
77
113
|
- lib/rng/start.rb
|
114
|
+
- lib/rng/text.rb
|
115
|
+
- lib/rng/to_rnc.rb
|
116
|
+
- lib/rng/value.rb
|
78
117
|
- lib/rng/version.rb
|
118
|
+
- lib/rng/zero_or_more.rb
|
79
119
|
- rng.gemspec
|
80
120
|
- sig/rng.rbs
|
121
|
+
- spec/fixtures/rnc/address_book.rnc
|
122
|
+
- spec/fixtures/rnc/complex_example.rnc
|
123
|
+
- spec/fixtures/rng/address_book.rng
|
124
|
+
- spec/fixtures/rng/relaxng.rng
|
125
|
+
- spec/fixtures/rng/testSuite.rng
|
126
|
+
- spec/fixtures/spectest.xml
|
81
127
|
- spec/rng/rnc_parser_spec.rb
|
82
|
-
- spec/rng/
|
128
|
+
- spec/rng/rnc_roundtrip_spec.rb
|
83
129
|
- spec/rng/schema_spec.rb
|
130
|
+
- spec/rng/spectest_spec.rb
|
84
131
|
- spec/rng_spec.rb
|
85
132
|
- spec/spec_helper.rb
|
86
133
|
homepage: https://github.com/lutaml/rng
|
data/lib/rng/builder.rb
DELETED
@@ -1,158 +0,0 @@
|
|
1
|
-
module Rng
|
2
|
-
class Builder
|
3
|
-
def build(schema, format:)
|
4
|
-
case format
|
5
|
-
when :rng
|
6
|
-
build_rng(schema)
|
7
|
-
when :rnc
|
8
|
-
build_rnc(schema)
|
9
|
-
else
|
10
|
-
raise ArgumentError, "Unsupported format: #{format}"
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def build_rng(schema)
|
17
|
-
doc = Nokogiri::XML::Document.new
|
18
|
-
doc.encoding = "UTF-8"
|
19
|
-
|
20
|
-
if schema.is_a?(Rng::Schema)
|
21
|
-
root = Nokogiri::XML::Node.new("grammar", doc)
|
22
|
-
root["xmlns"] = "http://relaxng.org/ns/structure/1.0"
|
23
|
-
doc.root = root
|
24
|
-
|
25
|
-
start = Nokogiri::XML::Node.new("start", doc)
|
26
|
-
root.add_child(start)
|
27
|
-
|
28
|
-
if schema.start.ref
|
29
|
-
ref = Nokogiri::XML::Node.new("ref", doc)
|
30
|
-
ref["name"] = schema.start.ref
|
31
|
-
start.add_child(ref)
|
32
|
-
end
|
33
|
-
|
34
|
-
schema.start.elements.each do |element|
|
35
|
-
start.add_child(build_rng_element(element, doc))
|
36
|
-
end
|
37
|
-
|
38
|
-
schema.define&.each do |define|
|
39
|
-
define_node = Nokogiri::XML::Node.new("define", doc)
|
40
|
-
define_node["name"] = define.name
|
41
|
-
define.elements.each do |element|
|
42
|
-
define_node.add_child(build_rng_element(element, doc))
|
43
|
-
end
|
44
|
-
root.add_child(define_node)
|
45
|
-
end
|
46
|
-
elsif schema.is_a?(Rng::Element)
|
47
|
-
el = build_rng_element(schema, doc)
|
48
|
-
el["xmlns"] = "http://relaxng.org/ns/structure/1.0"
|
49
|
-
doc.root = el
|
50
|
-
end
|
51
|
-
|
52
|
-
doc.to_xml
|
53
|
-
end
|
54
|
-
|
55
|
-
def build_rng_element(element, doc)
|
56
|
-
if element.zero_or_more&.any?
|
57
|
-
zero_or_more = Nokogiri::XML::Node.new("zeroOrMore", doc)
|
58
|
-
el = Nokogiri::XML::Node.new("element", doc)
|
59
|
-
el["name"] = element.name
|
60
|
-
add_element_content(element, el, doc)
|
61
|
-
zero_or_more.add_child(el)
|
62
|
-
return zero_or_more
|
63
|
-
elsif element.one_or_more&.any?
|
64
|
-
one_or_more = Nokogiri::XML::Node.new("oneOrMore", doc)
|
65
|
-
el = Nokogiri::XML::Node.new("element", doc)
|
66
|
-
el["name"] = element.name
|
67
|
-
add_element_content(element, el, doc)
|
68
|
-
one_or_more.add_child(el)
|
69
|
-
return one_or_more
|
70
|
-
elsif element.optional&.any?
|
71
|
-
optional = Nokogiri::XML::Node.new("optional", doc)
|
72
|
-
el = Nokogiri::XML::Node.new("element", doc)
|
73
|
-
el["name"] = element.name
|
74
|
-
add_element_content(element, el, doc)
|
75
|
-
optional.add_child(el)
|
76
|
-
return optional
|
77
|
-
else
|
78
|
-
el = Nokogiri::XML::Node.new("element", doc)
|
79
|
-
el["name"] = element.name
|
80
|
-
add_element_content(element, el, doc)
|
81
|
-
return el
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def add_element_content(element, el, doc)
|
86
|
-
element.attributes&.each do |attr|
|
87
|
-
attr_node = Nokogiri::XML::Node.new("attribute", doc)
|
88
|
-
attr_node["name"] = attr.name
|
89
|
-
if attr.type&.any?
|
90
|
-
data = Nokogiri::XML::Node.new("data", doc)
|
91
|
-
data["type"] = attr.type.first
|
92
|
-
attr_node.add_child(data)
|
93
|
-
else
|
94
|
-
text = Nokogiri::XML::Node.new("text", doc)
|
95
|
-
attr_node.add_child(text)
|
96
|
-
end
|
97
|
-
el.add_child(attr_node)
|
98
|
-
end
|
99
|
-
|
100
|
-
element.elements&.each do |child|
|
101
|
-
el.add_child(build_rng_element(child, doc))
|
102
|
-
end
|
103
|
-
|
104
|
-
if element.text
|
105
|
-
text = Nokogiri::XML::Node.new("text", doc)
|
106
|
-
el.add_child(text)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def build_rnc(schema)
|
111
|
-
result = ""
|
112
|
-
elements = schema.is_a?(Rng::Schema) ? schema.start.elements : [schema]
|
113
|
-
elements.each do |element|
|
114
|
-
result += build_rnc_element(element)
|
115
|
-
end
|
116
|
-
result
|
117
|
-
end
|
118
|
-
|
119
|
-
def build_rnc_element(element, indent = 0)
|
120
|
-
return "" unless element # Handle nil elements
|
121
|
-
|
122
|
-
result = " " * indent
|
123
|
-
result += "element #{element.name} {\n"
|
124
|
-
|
125
|
-
element.attributes&.each do |attr|
|
126
|
-
result += " " * (indent + 1)
|
127
|
-
result += "attribute #{attr.name} { text }"
|
128
|
-
result += ",\n" unless element.attributes.last == attr && !element.elements&.any? && !element.text
|
129
|
-
end
|
130
|
-
|
131
|
-
element.elements&.each_with_index do |child, index|
|
132
|
-
child_result = build_rnc_element(child, indent + 1)
|
133
|
-
result += child_result
|
134
|
-
result += "," unless index == element.elements.size - 1 && !element.text
|
135
|
-
result += "\n"
|
136
|
-
end
|
137
|
-
|
138
|
-
if element.text
|
139
|
-
result += " " * (indent + 1)
|
140
|
-
result += "text"
|
141
|
-
result += "\n"
|
142
|
-
end
|
143
|
-
|
144
|
-
result += " " * indent
|
145
|
-
result += "}"
|
146
|
-
|
147
|
-
if element.zero_or_more&.any?
|
148
|
-
result += "*"
|
149
|
-
elsif element.one_or_more&.any?
|
150
|
-
result += "+"
|
151
|
-
elsif element.optional&.any?
|
152
|
-
result += "?"
|
153
|
-
end
|
154
|
-
|
155
|
-
result
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
data/lib/rng/rng_parser.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
require "nokogiri"
|
2
|
-
require_relative "schema"
|
3
|
-
require_relative "element"
|
4
|
-
|
5
|
-
module Rng
|
6
|
-
class RngParser
|
7
|
-
RELAXNG_NS = "http://relaxng.org/ns/structure/1.0"
|
8
|
-
|
9
|
-
def parse(input)
|
10
|
-
doc = Nokogiri::XML(input)
|
11
|
-
doc.remove_namespaces! # This simplifies namespace handling
|
12
|
-
|
13
|
-
root = doc.root
|
14
|
-
case root.name
|
15
|
-
when "grammar"
|
16
|
-
parse_grammar(doc)
|
17
|
-
when "element"
|
18
|
-
parse_element(doc)
|
19
|
-
else
|
20
|
-
raise Rng::Error, "Unexpected root element: #{root.name}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def parse_grammar(doc)
|
27
|
-
Schema.new(
|
28
|
-
start: parse_start(doc.at_xpath("//start")),
|
29
|
-
define: doc.xpath("//define").map { |define| parse_define(define) },
|
30
|
-
)
|
31
|
-
end
|
32
|
-
|
33
|
-
def parse_start(node)
|
34
|
-
return nil unless node
|
35
|
-
|
36
|
-
Start.new(
|
37
|
-
ref: node.at_xpath(".//ref")&.attr("name"),
|
38
|
-
elements: node.xpath(".//element").map { |element| parse_element(element) },
|
39
|
-
)
|
40
|
-
end
|
41
|
-
|
42
|
-
def parse_define(node)
|
43
|
-
Define.new(
|
44
|
-
name: node["name"],
|
45
|
-
elements: node.xpath(".//element").map { |element| parse_element(element) },
|
46
|
-
)
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse_element(node)
|
50
|
-
return nil unless node["name"]
|
51
|
-
|
52
|
-
element = Element.new(
|
53
|
-
name: node["name"],
|
54
|
-
attributes: [],
|
55
|
-
elements: [],
|
56
|
-
text: false,
|
57
|
-
)
|
58
|
-
|
59
|
-
node.children.each do |child|
|
60
|
-
parse_child(child, element)
|
61
|
-
end
|
62
|
-
|
63
|
-
element
|
64
|
-
end
|
65
|
-
|
66
|
-
def parse_child(node, element)
|
67
|
-
case node.name
|
68
|
-
when "attribute"
|
69
|
-
element.attributes << parse_attribute(node)
|
70
|
-
when "element"
|
71
|
-
element.elements << parse_element(node)
|
72
|
-
when "text"
|
73
|
-
element.text = true
|
74
|
-
when "zeroOrMore"
|
75
|
-
parse_zero_or_more(node).each { |el| element.zero_or_more << el }
|
76
|
-
when "oneOrMore"
|
77
|
-
parse_one_or_more(node).each { |el| element.one_or_more << el }
|
78
|
-
when "optional"
|
79
|
-
parse_optional(node).each { |el| element.optional << el }
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def parse_attribute(node)
|
84
|
-
data_node = node.at_xpath(".//data")
|
85
|
-
Attribute.new(
|
86
|
-
name: node["name"],
|
87
|
-
type: data_node ? [data_node["type"]] : ["string"],
|
88
|
-
)
|
89
|
-
end
|
90
|
-
|
91
|
-
def parse_zero_or_more(node)
|
92
|
-
node.xpath("./element").map { |el| parse_element(el) }
|
93
|
-
end
|
94
|
-
|
95
|
-
def parse_one_or_more(node)
|
96
|
-
node.xpath("./element").map { |el| parse_element(el) }
|
97
|
-
end
|
98
|
-
|
99
|
-
def parse_optional(node)
|
100
|
-
node.xpath("./element").map { |el| parse_element(el) }
|
101
|
-
end
|
102
|
-
|
103
|
-
def parse_choice(node)
|
104
|
-
node.xpath(".//choice/element").map { |el| parse_element(el) }
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
data/lib/rng/schema.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require "lutaml/model"
|
2
|
-
require_relative "start"
|
3
|
-
require_relative "define"
|
4
|
-
|
5
|
-
module Rng
|
6
|
-
class Schema < Lutaml::Model::Serializable
|
7
|
-
attribute :start, Start
|
8
|
-
attribute :define, Define, collection: true
|
9
|
-
|
10
|
-
xml do
|
11
|
-
root "grammar"
|
12
|
-
namespace "http://relaxng.org/ns/structure/1.0"
|
13
|
-
|
14
|
-
map_element "start", to: :start
|
15
|
-
map_element "define", to: :define
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/spec/rng/rng_parser_spec.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
RSpec.describe Rng::RngParser do
|
4
|
-
let(:parser) { described_class.new }
|
5
|
-
|
6
|
-
describe "#parse" do
|
7
|
-
context "with a simple RNG schema" do
|
8
|
-
let(:input) do
|
9
|
-
<<~RNG
|
10
|
-
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
11
|
-
<start>
|
12
|
-
<element name="addressBook">
|
13
|
-
<zeroOrMore>
|
14
|
-
<element name="card">
|
15
|
-
<element name="name">
|
16
|
-
<text/>
|
17
|
-
</element>
|
18
|
-
<element name="email">
|
19
|
-
<text/>
|
20
|
-
</element>
|
21
|
-
</element>
|
22
|
-
</zeroOrMore>
|
23
|
-
</element>
|
24
|
-
</start>
|
25
|
-
</grammar>
|
26
|
-
RNG
|
27
|
-
end
|
28
|
-
|
29
|
-
it "correctly parses the schema" do
|
30
|
-
result = parser.parse(input)
|
31
|
-
expect(result).to be_a(Rng::Schema)
|
32
|
-
expect(result.start.elements.first.name).to eq("addressBook")
|
33
|
-
expect(result.start.elements.first.zero_or_more.first.name).to eq("card")
|
34
|
-
expect(result.start.elements.first.zero_or_more.first.elements.map(&:name)).to eq(["name", "email"])
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
context "with a complex RNG schema" do
|
39
|
-
let(:input) do
|
40
|
-
<<~RNG
|
41
|
-
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
42
|
-
<start>
|
43
|
-
<ref name="addressBook"/>
|
44
|
-
</start>
|
45
|
-
<define name="addressBook">
|
46
|
-
<element name="addressBook">
|
47
|
-
<zeroOrMore>
|
48
|
-
<element name="card">
|
49
|
-
<element name="name">
|
50
|
-
<text/>
|
51
|
-
</element>
|
52
|
-
<element name="email">
|
53
|
-
<text/>
|
54
|
-
</element>
|
55
|
-
<optional>
|
56
|
-
<element name="note">
|
57
|
-
<text/>
|
58
|
-
</element>
|
59
|
-
</optional>
|
60
|
-
</element>
|
61
|
-
</zeroOrMore>
|
62
|
-
</element>
|
63
|
-
</define>
|
64
|
-
</grammar>
|
65
|
-
RNG
|
66
|
-
end
|
67
|
-
|
68
|
-
it "correctly parses the schema" do
|
69
|
-
result = parser.parse(input)
|
70
|
-
expect(result).to be_a(Rng::Schema)
|
71
|
-
expect(result.start.ref).to eq("addressBook")
|
72
|
-
expect(result.define.first.name).to eq("addressBook")
|
73
|
-
expect(result.define.first.elements.first.name).to eq("addressBook")
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "with attributes" do
|
78
|
-
let(:input) do
|
79
|
-
<<~RNG
|
80
|
-
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
81
|
-
<start>
|
82
|
-
<element name="person">
|
83
|
-
<attribute name="id">
|
84
|
-
<data type="ID"/>
|
85
|
-
</attribute>
|
86
|
-
<element name="name">
|
87
|
-
<text/>
|
88
|
-
</element>
|
89
|
-
</element>
|
90
|
-
</start>
|
91
|
-
</grammar>
|
92
|
-
RNG
|
93
|
-
end
|
94
|
-
|
95
|
-
it "correctly parses attributes" do
|
96
|
-
result = parser.parse(input)
|
97
|
-
expect(result.start.elements.first.attributes.first.name).to eq("id")
|
98
|
-
expect(result.start.elements.first.attributes.first.type).to eq(["ID"])
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|