rng 0.1.2 → 0.3.4
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/.github/workflows/docs.yml +63 -0
- data/.github/workflows/release.yml +8 -3
- data/.gitignore +11 -0
- data/.rubocop.yml +10 -7
- data/.rubocop_todo.yml +229 -23
- data/CHANGELOG.md +317 -0
- data/CLAUDE.md +139 -0
- data/Gemfile +11 -12
- data/README.adoc +1538 -11
- data/Rakefile +11 -3
- data/docs/Gemfile +8 -0
- data/docs/_config.yml +23 -0
- data/docs/getting-started/index.adoc +75 -0
- data/docs/guides/error-handling.adoc +137 -0
- data/docs/guides/external-references.adoc +128 -0
- data/docs/guides/index.adoc +24 -0
- data/docs/guides/parsing-rnc.adoc +141 -0
- data/docs/guides/parsing-rng-xml.adoc +81 -0
- data/docs/guides/rng-to-rnc.adoc +101 -0
- data/docs/guides/validation.adoc +85 -0
- data/docs/index.adoc +52 -0
- data/docs/reference/api.adoc +126 -0
- data/docs/reference/cli.adoc +182 -0
- data/docs/understanding/architecture.adoc +58 -0
- data/docs/understanding/rng-vs-rnc.adoc +118 -0
- data/exe/rng +5 -0
- data/lib/rng/any_name.rb +10 -8
- data/lib/rng/attribute.rb +28 -26
- data/lib/rng/choice.rb +24 -24
- data/lib/rng/cli.rb +607 -0
- data/lib/rng/data.rb +10 -10
- data/lib/rng/datatype_declaration.rb +26 -0
- data/lib/rng/define.rb +44 -41
- data/lib/rng/div.rb +36 -0
- data/lib/rng/documentation.rb +9 -0
- data/lib/rng/element.rb +39 -37
- data/lib/rng/empty.rb +7 -7
- data/lib/rng/except.rb +25 -25
- data/lib/rng/external_ref.rb +8 -8
- data/lib/rng/external_ref_resolver.rb +602 -0
- data/lib/rng/foreign_attribute.rb +26 -0
- data/lib/rng/foreign_element.rb +33 -0
- data/lib/rng/grammar.rb +14 -12
- data/lib/rng/group.rb +26 -24
- data/lib/rng/include.rb +5 -6
- data/lib/rng/include_processor.rb +461 -0
- data/lib/rng/interleave.rb +23 -23
- data/lib/rng/list.rb +22 -22
- data/lib/rng/mixed.rb +23 -23
- data/lib/rng/name.rb +6 -7
- data/lib/rng/namespace_declaration.rb +47 -0
- data/lib/rng/namespaces.rb +15 -0
- data/lib/rng/not_allowed.rb +7 -7
- data/lib/rng/ns_name.rb +9 -9
- data/lib/rng/one_or_more.rb +23 -23
- data/lib/rng/optional.rb +23 -23
- data/lib/rng/param.rb +7 -8
- data/lib/rng/parent_ref.rb +8 -8
- data/lib/rng/parse_tree_processor.rb +695 -0
- data/lib/rng/pattern.rb +7 -7
- data/lib/rng/ref.rb +8 -8
- data/lib/rng/rnc_builder.rb +927 -0
- data/lib/rng/rnc_parser.rb +605 -305
- data/lib/rng/rnc_to_rng_converter.rb +1408 -0
- data/lib/rng/schema_preamble.rb +73 -0
- data/lib/rng/schema_validator.rb +1622 -0
- data/lib/rng/start.rb +27 -25
- data/lib/rng/test_suite_parser.rb +168 -0
- data/lib/rng/text.rb +11 -8
- data/lib/rng/to_rnc.rb +4 -35
- data/lib/rng/value.rb +6 -7
- data/lib/rng/version.rb +1 -1
- data/lib/rng/zero_or_more.rb +23 -23
- data/lib/rng.rb +68 -17
- data/rng.gemspec +18 -19
- data/scripts/extract_spectest_resources.rb +96 -0
- data/spec/fixtures/compacttest.xml +2511 -0
- data/spec/fixtures/external/circular_a.rng +7 -0
- data/spec/fixtures/external/circular_b.rng +7 -0
- data/spec/fixtures/external/circular_main.rng +7 -0
- data/spec/fixtures/external/external_ref_lib.rng +7 -0
- data/spec/fixtures/external/external_ref_main.rng +7 -0
- data/spec/fixtures/external/include_lib.rng +7 -0
- data/spec/fixtures/external/include_main.rng +3 -0
- data/spec/fixtures/external/nested_chain.rng +6 -0
- data/spec/fixtures/external/nested_leaf.rng +7 -0
- data/spec/fixtures/external/nested_mid.rng +8 -0
- data/spec/fixtures/metanorma/3gpp.rnc +35 -0
- data/spec/fixtures/metanorma/3gpp.rng +105 -0
- data/spec/fixtures/metanorma/basicdoc.rnc +11 -0
- data/spec/fixtures/metanorma/bipm.rnc +148 -0
- data/spec/fixtures/metanorma/bipm.rng +376 -0
- data/spec/fixtures/metanorma/bsi.rnc +104 -0
- data/spec/fixtures/metanorma/bsi.rng +332 -0
- data/spec/fixtures/metanorma/csa.rnc +45 -0
- data/spec/fixtures/metanorma/csa.rng +131 -0
- data/spec/fixtures/metanorma/csd.rnc +43 -0
- data/spec/fixtures/metanorma/csd.rng +132 -0
- data/spec/fixtures/metanorma/gbstandard.rnc +99 -0
- data/spec/fixtures/metanorma/gbstandard.rng +316 -0
- data/spec/fixtures/metanorma/iec.rnc +49 -0
- data/spec/fixtures/metanorma/iec.rng +193 -0
- data/spec/fixtures/metanorma/ietf.rnc +275 -0
- data/spec/fixtures/metanorma/ietf.rng +925 -0
- data/spec/fixtures/metanorma/iho.rnc +58 -0
- data/spec/fixtures/metanorma/iho.rng +179 -0
- data/spec/fixtures/metanorma/isodoc.rnc +873 -0
- data/spec/fixtures/metanorma/isodoc.rng +2704 -0
- data/spec/fixtures/metanorma/isostandard-amd.rnc +43 -0
- data/spec/fixtures/metanorma/isostandard-amd.rng +108 -0
- data/spec/fixtures/metanorma/isostandard.rnc +166 -0
- data/spec/fixtures/metanorma/isostandard.rng +494 -0
- data/spec/fixtures/metanorma/itu.rnc +122 -0
- data/spec/fixtures/metanorma/itu.rng +377 -0
- data/spec/fixtures/metanorma/m3d.rnc +41 -0
- data/spec/fixtures/metanorma/m3d.rng +122 -0
- data/spec/fixtures/metanorma/mpfd.rnc +36 -0
- data/spec/fixtures/metanorma/mpfd.rng +95 -0
- data/spec/fixtures/metanorma/nist.rnc +77 -0
- data/spec/fixtures/metanorma/nist.rng +216 -0
- data/spec/fixtures/metanorma/ogc.rnc +51 -0
- data/spec/fixtures/metanorma/ogc.rng +151 -0
- data/spec/fixtures/metanorma/reqt.rnc +6 -0
- data/spec/fixtures/metanorma/rsd.rnc +36 -0
- data/spec/fixtures/metanorma/rsd.rng +95 -0
- data/spec/fixtures/metanorma/un.rnc +103 -0
- data/spec/fixtures/metanorma/un.rng +367 -0
- data/spec/fixtures/rnc/base.rnc +4 -0
- data/spec/fixtures/rnc/grammar_with_trailing.rnc +8 -0
- data/spec/fixtures/rnc/main_include_trailing.rnc +3 -0
- data/spec/fixtures/rnc/main_with_include.rnc +5 -0
- data/spec/fixtures/rnc/test_augment.rnc +10 -0
- data/spec/fixtures/rnc/test_isodoc_simple.rnc +9 -0
- data/spec/fixtures/rnc/top_level_include.rnc +8 -0
- data/spec/fixtures/spectest_external/case_10_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_10_4.7/y +7 -0
- data/spec/fixtures/spectest_external/case_11_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_12_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_13_4.7/x +3 -0
- data/spec/fixtures/spectest_external/case_13_4.7/y +3 -0
- data/spec/fixtures/spectest_external/case_14_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_15_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_16_4.7/x +5 -0
- data/spec/fixtures/spectest_external/case_17_4.7/x +5 -0
- data/spec/fixtures/spectest_external/case_18_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_19_4.7/level1.rng +9 -0
- data/spec/fixtures/spectest_external/case_19_4.7/level2.rng +7 -0
- data/spec/fixtures/spectest_external/case_1_4.5/sub1/x +3 -0
- data/spec/fixtures/spectest_external/case_1_4.5/sub3/x +3 -0
- data/spec/fixtures/spectest_external/case_1_4.5/x +3 -0
- data/spec/fixtures/spectest_external/case_20_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_2_4.5/x +3 -0
- data/spec/fixtures/spectest_external/case_3_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_4_4.6/x +3 -0
- data/spec/fixtures/spectest_external/case_5_4.6/x +1 -0
- data/spec/fixtures/spectest_external/case_6_4.6/x +5 -0
- data/spec/fixtures/spectest_external/case_7_4.6/x +1 -0
- data/spec/fixtures/spectest_external/case_7_4.6/y +1 -0
- data/spec/fixtures/spectest_external/case_8_4.7/x +7 -0
- data/spec/fixtures/spectest_external/case_9_4.7/x +7 -0
- data/spec/fixtures/spectest_external/resources.json +149 -0
- data/spec/rng/advanced_rnc_spec.rb +101 -0
- data/spec/rng/compacttest_spec.rb +197 -0
- data/spec/rng/datatype_declaration_spec.rb +28 -0
- data/spec/rng/div_spec.rb +207 -0
- data/spec/rng/external_ref_resolver_spec.rb +122 -0
- data/spec/rng/metanorma_conversion_spec.rb +159 -0
- data/spec/rng/namespace_declaration_spec.rb +60 -0
- data/spec/rng/namespace_support_spec.rb +199 -0
- data/spec/rng/rnc_parser_spec.rb +498 -22
- data/spec/rng/rnc_roundtrip_spec.rb +96 -82
- data/spec/rng/rng_generation_spec.rb +288 -0
- data/spec/rng/roundtrip_spec.rb +342 -0
- data/spec/rng/schema_preamble_spec.rb +145 -0
- data/spec/rng/schema_spec.rb +68 -64
- data/spec/rng/spectest_spec.rb +168 -90
- data/spec/rng_spec.rb +2 -2
- data/spec/spec_helper.rb +7 -42
- metadata +141 -8
|
@@ -1,120 +1,134 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require 'spec_helper'
|
|
4
4
|
|
|
5
|
-
RSpec.describe
|
|
6
|
-
describe
|
|
7
|
-
it
|
|
8
|
-
expect(defined?(Rng::RncParser)).to eq(
|
|
5
|
+
RSpec.describe 'RNG to RNC Round-trip Tests' do
|
|
6
|
+
describe 'RNC Parser' do
|
|
7
|
+
it 'exists as a class' do
|
|
8
|
+
expect(defined?(Rng::RncParser)).to eq('constant')
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
it
|
|
11
|
+
it 'has a parse method' do
|
|
12
12
|
expect(Rng::RncParser).to respond_to(:parse)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
describe
|
|
17
|
-
context
|
|
18
|
-
let(:
|
|
19
|
-
|
|
20
|
-
<element name="foo" xmlns="http://relaxng.org/ns/structure/1.0">
|
|
21
|
-
<empty/>
|
|
22
|
-
</element>
|
|
23
|
-
XML
|
|
16
|
+
describe 'RNG to RNC Conversion' do
|
|
17
|
+
context 'with the address book schema' do
|
|
18
|
+
let(:address_book_rng) do
|
|
19
|
+
File.read('spec/fixtures/rng/address_book.rng')
|
|
24
20
|
end
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
let(:address_book_rnc) do
|
|
23
|
+
File.read('spec/fixtures/rnc/address_book.rnc')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'can convert RNG to RNC' do
|
|
27
|
+
rng_schema = Rng.parse(address_book_rng)
|
|
29
28
|
expect(rng_schema).not_to be_nil
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
rnc = Rng.to_rnc(rng_schema)
|
|
31
|
+
expect(rnc).to include('element addressBook')
|
|
32
|
+
expect(rnc).to include('element card')
|
|
33
|
+
expect(rnc).to include('element name')
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
it
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
it 'can parse RNC back to RNG' do
|
|
37
|
+
rng_schema = Rng.parse_rnc(address_book_rnc)
|
|
38
|
+
expect(rng_schema).to be_a(Rng::Grammar)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# expect(rng_schema.element.name).to eq("foo")
|
|
40
|
+
xml = rng_schema.to_xml
|
|
41
|
+
expect(xml).to include('<grammar')
|
|
42
|
+
expect(xml).to include('name="addressBook"')
|
|
44
43
|
end
|
|
45
44
|
|
|
46
|
-
it
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# rng_schema_from_rnc = Rng.parse_rnc(rnc)
|
|
45
|
+
it 'supports round-trip conversion' do
|
|
46
|
+
rng_schema = Rng.parse(address_book_rng)
|
|
47
|
+
rnc = Rng.to_rnc(rng_schema)
|
|
48
|
+
rng_schema_from_rnc = Rng.parse_rnc(rnc)
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
expect(rng_schema_from_rnc).to be_a(Rng::Grammar)
|
|
51
|
+
|
|
52
|
+
xml = rng_schema_from_rnc.to_xml
|
|
53
|
+
expect(xml).to include('name="addressBook"')
|
|
54
|
+
expect(xml).to include('name="card"')
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
|
|
58
|
-
describe
|
|
59
|
-
context
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
RNC
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Sample expected XML output after conversion
|
|
74
|
-
let(:expected_rng_pattern) do
|
|
75
|
-
%r{<element.*name="addressBook".*>.*<element.*name="card".*>.*<element.*name="name".*>.*<text/>.*</element>.*<element.*name="email".*>.*<text/>.*</element>.*<optional>.*<element.*name="note".*>.*<text/>.*</element>.*</optional>.*</element>.*</element>}m
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it "can convert RNC to RNG" do
|
|
79
|
-
skip "RNC to RNG conversion not yet implemented"
|
|
80
|
-
# xml_output = Rng::RncParser.parse(address_book_rnc)
|
|
81
|
-
# expect(xml_output).to match(expected_rng_pattern)
|
|
82
|
-
|
|
83
|
-
# Check if we can load the generated XML as a schema
|
|
84
|
-
# rng_schema = Rng::Grammar.from_xml(xml_output)
|
|
85
|
-
# expect(rng_schema).not_to be_nil
|
|
59
|
+
describe 'RNC to RNG Conversion' do
|
|
60
|
+
context 'with the address book example' do
|
|
61
|
+
it 'can convert RNC to RNG' do
|
|
62
|
+
rnc = File.read('spec/fixtures/rnc/address_book.rnc')
|
|
63
|
+
xml_output = Rng.parse_rnc(rnc).to_xml
|
|
64
|
+
expect(xml_output).to include('<grammar')
|
|
65
|
+
expect(xml_output).to include('xmlns="http://relaxng.org/ns/structure/1.0"')
|
|
66
|
+
|
|
67
|
+
# Verify it produces parseable RNG XML
|
|
68
|
+
rng_schema = Rng.parse(xml_output)
|
|
69
|
+
expect(rng_schema).to be_a(Rng::Grammar)
|
|
86
70
|
end
|
|
87
71
|
end
|
|
88
72
|
end
|
|
89
73
|
|
|
90
|
-
describe
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
74
|
+
describe 'Round-trip with complex features' do
|
|
75
|
+
describe 'RELAX NG pattern features' do
|
|
76
|
+
it 'preserves attribute definitions' do
|
|
77
|
+
rnc = <<~RNC
|
|
78
|
+
start = element foo {
|
|
79
|
+
attribute bar { text },
|
|
80
|
+
element baz { text }
|
|
81
|
+
}
|
|
82
|
+
RNC
|
|
83
|
+
grammar = Rng.parse_rnc(rnc)
|
|
84
|
+
xml = grammar.to_xml
|
|
85
|
+
expect(xml).to include('<attribute')
|
|
86
|
+
expect(xml).to include('name="bar"')
|
|
98
87
|
end
|
|
99
88
|
|
|
100
|
-
it
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
it 'preserves choices' do
|
|
90
|
+
rnc = <<~RNC
|
|
91
|
+
start = element foo {
|
|
92
|
+
(element a { text } | element b { text })
|
|
93
|
+
}
|
|
94
|
+
RNC
|
|
95
|
+
grammar = Rng.parse_rnc(rnc)
|
|
96
|
+
xml = grammar.to_xml
|
|
97
|
+
expect(xml).to include('<choice')
|
|
103
98
|
end
|
|
104
99
|
|
|
105
|
-
it
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
it 'preserves interleave' do
|
|
101
|
+
rnc = <<~RNC
|
|
102
|
+
start = element foo {
|
|
103
|
+
element a { text } & element b { text }
|
|
104
|
+
}
|
|
105
|
+
RNC
|
|
106
|
+
grammar = Rng.parse_rnc(rnc)
|
|
107
|
+
xml = grammar.to_xml
|
|
108
|
+
expect(xml).to include('<interleave')
|
|
108
109
|
end
|
|
109
110
|
|
|
110
|
-
it
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
it 'preserves data types' do
|
|
112
|
+
rnc = <<~RNC
|
|
113
|
+
start = element foo {
|
|
114
|
+
attribute count { xsd:integer }
|
|
115
|
+
}
|
|
116
|
+
RNC
|
|
117
|
+
grammar = Rng.parse_rnc(rnc)
|
|
118
|
+
xml = grammar.to_xml
|
|
119
|
+
expect(xml).to include('<data')
|
|
120
|
+
expect(xml).to include('integer')
|
|
113
121
|
end
|
|
114
122
|
|
|
115
|
-
it
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
it 'preserves namespaces' do
|
|
124
|
+
rnc = <<~RNC
|
|
125
|
+
default namespace = "http://example.org"
|
|
126
|
+
start = element foo { text }
|
|
127
|
+
RNC
|
|
128
|
+
grammar = Rng.parse_rnc(rnc)
|
|
129
|
+
xml = grammar.to_xml
|
|
130
|
+
expect(xml).to include('<grammar')
|
|
131
|
+
expect(grammar.ns).to eq('http://example.org')
|
|
118
132
|
end
|
|
119
133
|
end
|
|
120
134
|
end
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe 'RNG Generation' do
|
|
6
|
+
describe 'Grammar.to_xml()' do
|
|
7
|
+
context 'with simple manually constructed schema' do
|
|
8
|
+
it 'generates valid RNG XML from a Grammar object' do
|
|
9
|
+
# Create simple grammar manually
|
|
10
|
+
grammar = Rng::Grammar.new
|
|
11
|
+
grammar.datatypeLibrary = 'http://www.w3.org/2001/XMLSchema-datatypes'
|
|
12
|
+
|
|
13
|
+
# Create start with element
|
|
14
|
+
start = Rng::Start.new
|
|
15
|
+
element = Rng::Element.new
|
|
16
|
+
element.attr_name = 'root'
|
|
17
|
+
text = Rng::Text.new
|
|
18
|
+
element.text = text
|
|
19
|
+
start.element = element
|
|
20
|
+
grammar.start = [start]
|
|
21
|
+
|
|
22
|
+
# Generate XML (note: to_xml() doesn't include XML declaration)
|
|
23
|
+
xml = grammar.to_xml
|
|
24
|
+
|
|
25
|
+
# Verify structure
|
|
26
|
+
expect(xml).to include('<grammar')
|
|
27
|
+
expect(xml).to include('xmlns="http://relaxng.org/ns/structure/1.0"')
|
|
28
|
+
expect(xml).to include('<start')
|
|
29
|
+
expect(xml).to include('<element name="root"')
|
|
30
|
+
expect(xml).to include('<text')
|
|
31
|
+
expect(xml).to include('</element>')
|
|
32
|
+
expect(xml).to include('</start>')
|
|
33
|
+
expect(xml).to include('</grammar>')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'handles namespace attributes correctly' do
|
|
37
|
+
grammar = Rng::Grammar.new
|
|
38
|
+
grammar.ns = 'http://example.com/ns'
|
|
39
|
+
|
|
40
|
+
xml = grammar.to_xml
|
|
41
|
+
expect(xml).to include('ns="http://example.com/ns"')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'handles datatype library attributes correctly' do
|
|
45
|
+
grammar = Rng::Grammar.new
|
|
46
|
+
grammar.datatypeLibrary = 'http://www.w3.org/2001/XMLSchema-datatypes'
|
|
47
|
+
|
|
48
|
+
xml = grammar.to_xml
|
|
49
|
+
expect(xml).to include('datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'with parsed RNG schema' do
|
|
54
|
+
let(:address_book_rng) { File.read('spec/fixtures/rng/address_book.rng') }
|
|
55
|
+
|
|
56
|
+
it 'regenerates XML from parsed RNG' do
|
|
57
|
+
parsed = Rng.parse(address_book_rng)
|
|
58
|
+
regenerated = parsed.to_xml
|
|
59
|
+
|
|
60
|
+
# Verify it's valid XML (note: to_xml() doesn't include XML declaration)
|
|
61
|
+
expect(regenerated).to include('<grammar')
|
|
62
|
+
expect(regenerated).to include('xmlns="http://relaxng.org/ns/structure/1.0"')
|
|
63
|
+
|
|
64
|
+
# Verify structure
|
|
65
|
+
expect(regenerated).to include('<start')
|
|
66
|
+
expect(regenerated).to include('<element name="addressBook"')
|
|
67
|
+
expect(regenerated).to include('name="cardContent"')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'maintains semantic equivalence (round-trip)' do
|
|
71
|
+
parsed = Rng.parse(address_book_rng)
|
|
72
|
+
regenerated = parsed.to_xml
|
|
73
|
+
reparsed = Rng.parse(regenerated)
|
|
74
|
+
|
|
75
|
+
# Verify key structure is maintained
|
|
76
|
+
expect(reparsed.start.first.element.attr_name).to eq('addressBook')
|
|
77
|
+
expect(reparsed.define.first.name).to eq('cardContent')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'with parsed RNC schema' do
|
|
82
|
+
let(:address_book_rnc) { File.read('spec/fixtures/rnc/address_book.rnc') }
|
|
83
|
+
|
|
84
|
+
it 'generates RNG XML from parsed RNC' do
|
|
85
|
+
parsed = Rng.parse_rnc(address_book_rnc)
|
|
86
|
+
xml = parsed.to_xml
|
|
87
|
+
|
|
88
|
+
# Verify it's valid RNG XML (note: to_xml() doesn't include XML declaration)
|
|
89
|
+
expect(xml).to include('<grammar')
|
|
90
|
+
expect(xml).to include('xmlns="http://relaxng.org/ns/structure/1.0"')
|
|
91
|
+
expect(xml).to include('<start')
|
|
92
|
+
expect(xml).to include('<element name="addressBook"')
|
|
93
|
+
# NOTE: Current RNC parser doesn't populate Grammar.define array
|
|
94
|
+
# It inlines pattern definitions instead of creating separate <define> elements
|
|
95
|
+
expect(xml).to include('<ref name="cardContent"')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'produces semantically valid schema' do
|
|
99
|
+
parsed = Rng.parse_rnc(address_book_rnc)
|
|
100
|
+
xml = parsed.to_xml
|
|
101
|
+
reparsed = Rng.parse(xml)
|
|
102
|
+
|
|
103
|
+
# Verify structure
|
|
104
|
+
expect(reparsed).to be_a(Rng::Grammar)
|
|
105
|
+
expect(reparsed.start.first.element.attr_name).to eq('addressBook')
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'with special attribute values' do
|
|
110
|
+
it 'handles nil namespace (attribute omitted)' do
|
|
111
|
+
grammar = Rng::Grammar.new
|
|
112
|
+
grammar.ns = nil
|
|
113
|
+
|
|
114
|
+
xml = grammar.to_xml
|
|
115
|
+
# nil values cause the attribute to be omitted
|
|
116
|
+
expect(xml).not_to match(/\bns="/)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'handles nil datatype library (attribute omitted)' do
|
|
120
|
+
grammar = Rng::Grammar.new
|
|
121
|
+
grammar.datatypeLibrary = nil
|
|
122
|
+
|
|
123
|
+
xml = grammar.to_xml
|
|
124
|
+
# nil values cause the attribute to be omitted
|
|
125
|
+
expect(xml).not_to include('datatypeLibrary=')
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'handles empty strings correctly' do
|
|
129
|
+
grammar = Rng::Grammar.new
|
|
130
|
+
grammar.ns = ''
|
|
131
|
+
|
|
132
|
+
xml = grammar.to_xml
|
|
133
|
+
expect(xml).to include('ns=""')
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context 'with complex patterns' do
|
|
138
|
+
it 'handles choice patterns' do
|
|
139
|
+
grammar = Rng::Grammar.new
|
|
140
|
+
start = Rng::Start.new
|
|
141
|
+
choice = Rng::Choice.new
|
|
142
|
+
|
|
143
|
+
elem1 = Rng::Element.new
|
|
144
|
+
elem1.attr_name = 'option1'
|
|
145
|
+
elem1.text = Rng::Text.new
|
|
146
|
+
|
|
147
|
+
elem2 = Rng::Element.new
|
|
148
|
+
elem2.attr_name = 'option2'
|
|
149
|
+
elem2.text = Rng::Text.new
|
|
150
|
+
|
|
151
|
+
choice.element = [elem1, elem2]
|
|
152
|
+
start.choice = choice
|
|
153
|
+
grammar.start = [start]
|
|
154
|
+
|
|
155
|
+
xml = grammar.to_xml
|
|
156
|
+
expect(xml).to include('<choice>')
|
|
157
|
+
expect(xml).to include('<element name="option1">')
|
|
158
|
+
expect(xml).to include('<element name="option2">')
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it 'handles group patterns' do
|
|
162
|
+
grammar = Rng::Grammar.new
|
|
163
|
+
start = Rng::Start.new
|
|
164
|
+
group = Rng::Group.new
|
|
165
|
+
|
|
166
|
+
elem1 = Rng::Element.new
|
|
167
|
+
elem1.attr_name = 'first'
|
|
168
|
+
elem1.text = Rng::Text.new
|
|
169
|
+
|
|
170
|
+
elem2 = Rng::Element.new
|
|
171
|
+
elem2.attr_name = 'second'
|
|
172
|
+
elem2.text = Rng::Text.new
|
|
173
|
+
|
|
174
|
+
group.element = [elem1, elem2]
|
|
175
|
+
start.group = group
|
|
176
|
+
grammar.start = [start]
|
|
177
|
+
|
|
178
|
+
xml = grammar.to_xml
|
|
179
|
+
expect(xml).to include('<group>')
|
|
180
|
+
expect(xml).to include('<element name="first">')
|
|
181
|
+
expect(xml).to include('<element name="second">')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'handles optional patterns' do
|
|
185
|
+
grammar = Rng::Grammar.new
|
|
186
|
+
start = Rng::Start.new
|
|
187
|
+
optional = Rng::Optional.new
|
|
188
|
+
|
|
189
|
+
elem = Rng::Element.new
|
|
190
|
+
elem.attr_name = 'optional'
|
|
191
|
+
elem.text = Rng::Text.new
|
|
192
|
+
optional.element = elem
|
|
193
|
+
|
|
194
|
+
start.optional = optional
|
|
195
|
+
grammar.start = [start]
|
|
196
|
+
|
|
197
|
+
xml = grammar.to_xml
|
|
198
|
+
expect(xml).to include('<optional>')
|
|
199
|
+
expect(xml).to include('<element name="optional">')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'handles zeroOrMore patterns' do
|
|
203
|
+
grammar = Rng::Grammar.new
|
|
204
|
+
start = Rng::Start.new
|
|
205
|
+
zero_or_more = Rng::ZeroOrMore.new
|
|
206
|
+
|
|
207
|
+
elem = Rng::Element.new
|
|
208
|
+
elem.attr_name = 'item'
|
|
209
|
+
elem.text = Rng::Text.new
|
|
210
|
+
zero_or_more.element = elem
|
|
211
|
+
|
|
212
|
+
start.zeroOrMore = zero_or_more
|
|
213
|
+
grammar.start = [start]
|
|
214
|
+
|
|
215
|
+
xml = grammar.to_xml
|
|
216
|
+
expect(xml).to include('<zeroOrMore>')
|
|
217
|
+
expect(xml).to include('<element name="item">')
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'handles oneOrMore patterns' do
|
|
221
|
+
grammar = Rng::Grammar.new
|
|
222
|
+
start = Rng::Start.new
|
|
223
|
+
one_or_more = Rng::OneOrMore.new
|
|
224
|
+
|
|
225
|
+
elem = Rng::Element.new
|
|
226
|
+
elem.attr_name = 'item'
|
|
227
|
+
elem.text = Rng::Text.new
|
|
228
|
+
one_or_more.element = elem
|
|
229
|
+
|
|
230
|
+
start.oneOrMore = [one_or_more]
|
|
231
|
+
grammar.start = [start]
|
|
232
|
+
|
|
233
|
+
xml = grammar.to_xml
|
|
234
|
+
expect(xml).to include('<oneOrMore>')
|
|
235
|
+
expect(xml).to include('<element name="item">')
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
context 'with references' do
|
|
240
|
+
it 'handles ref patterns' do
|
|
241
|
+
grammar = Rng::Grammar.new
|
|
242
|
+
|
|
243
|
+
# Define pattern
|
|
244
|
+
define = Rng::Define.new
|
|
245
|
+
define.name = 'myPattern'
|
|
246
|
+
elem = Rng::Element.new
|
|
247
|
+
elem.attr_name = 'test'
|
|
248
|
+
elem.text = Rng::Text.new
|
|
249
|
+
define.element = elem
|
|
250
|
+
grammar.define = [define]
|
|
251
|
+
|
|
252
|
+
# Reference pattern
|
|
253
|
+
start = Rng::Start.new
|
|
254
|
+
ref = Rng::Ref.new
|
|
255
|
+
ref.name = 'myPattern'
|
|
256
|
+
start.ref = ref
|
|
257
|
+
grammar.start = [start]
|
|
258
|
+
|
|
259
|
+
xml = grammar.to_xml
|
|
260
|
+
expect(xml).to match(/<define[^>]+name="myPattern"/)
|
|
261
|
+
expect(xml).to include('<ref name="myPattern"')
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
context 'with attributes' do
|
|
266
|
+
it 'handles attribute patterns' do
|
|
267
|
+
grammar = Rng::Grammar.new
|
|
268
|
+
start = Rng::Start.new
|
|
269
|
+
|
|
270
|
+
elem = Rng::Element.new
|
|
271
|
+
elem.attr_name = 'root'
|
|
272
|
+
|
|
273
|
+
attr = Rng::Attribute.new
|
|
274
|
+
attr.attr_name = 'id'
|
|
275
|
+
attr.text = Rng::Text.new
|
|
276
|
+
elem.attribute = attr
|
|
277
|
+
|
|
278
|
+
start.element = elem
|
|
279
|
+
grammar.start = [start]
|
|
280
|
+
|
|
281
|
+
xml = grammar.to_xml
|
|
282
|
+
expect(xml).to include('<element name="root"')
|
|
283
|
+
expect(xml).to include('<attribute name="id"')
|
|
284
|
+
expect(xml).to include('<text/')
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|