rng 0.1.1 → 0.3.3

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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/release.yml +8 -3
  4. data/.gitignore +11 -0
  5. data/.rubocop.yml +11 -6
  6. data/.rubocop_todo.yml +270 -0
  7. data/CHANGELOG.md +317 -0
  8. data/CLAUDE.md +139 -0
  9. data/CODE_OF_CONDUCT.md +132 -0
  10. data/Gemfile +11 -10
  11. data/README.adoc +1929 -0
  12. data/Rakefile +11 -3
  13. data/docs/Gemfile +8 -0
  14. data/docs/_config.yml +23 -0
  15. data/docs/getting-started/index.adoc +75 -0
  16. data/docs/guides/error-handling.adoc +137 -0
  17. data/docs/guides/external-references.adoc +128 -0
  18. data/docs/guides/index.adoc +24 -0
  19. data/docs/guides/parsing-rnc.adoc +141 -0
  20. data/docs/guides/parsing-rng-xml.adoc +81 -0
  21. data/docs/guides/rng-to-rnc.adoc +101 -0
  22. data/docs/guides/validation.adoc +85 -0
  23. data/docs/index.adoc +52 -0
  24. data/docs/reference/api.adoc +126 -0
  25. data/docs/reference/cli.adoc +182 -0
  26. data/docs/understanding/architecture.adoc +58 -0
  27. data/docs/understanding/rng-vs-rnc.adoc +118 -0
  28. data/exe/rng +5 -0
  29. data/lib/rng/any_name.rb +28 -0
  30. data/lib/rng/attribute.rb +61 -5
  31. data/lib/rng/choice.rb +60 -0
  32. data/lib/rng/cli.rb +607 -0
  33. data/lib/rng/data.rb +32 -0
  34. data/lib/rng/datatype_declaration.rb +26 -0
  35. data/lib/rng/define.rb +56 -5
  36. data/lib/rng/div.rb +36 -0
  37. data/lib/rng/documentation.rb +9 -0
  38. data/lib/rng/element.rb +66 -18
  39. data/lib/rng/empty.rb +23 -0
  40. data/lib/rng/except.rb +62 -0
  41. data/lib/rng/external_ref.rb +28 -0
  42. data/lib/rng/external_ref_resolver.rb +582 -0
  43. data/lib/rng/foreign_attribute.rb +26 -0
  44. data/lib/rng/foreign_element.rb +33 -0
  45. data/lib/rng/grammar.rb +38 -0
  46. data/lib/rng/group.rb +62 -0
  47. data/lib/rng/include.rb +23 -0
  48. data/lib/rng/include_processor.rb +461 -0
  49. data/lib/rng/interleave.rb +58 -0
  50. data/lib/rng/list.rb +56 -0
  51. data/lib/rng/mixed.rb +58 -0
  52. data/lib/rng/name.rb +28 -0
  53. data/lib/rng/namespace_declaration.rb +47 -0
  54. data/lib/rng/namespaces.rb +15 -0
  55. data/lib/rng/not_allowed.rb +23 -0
  56. data/lib/rng/ns_name.rb +31 -0
  57. data/lib/rng/one_or_more.rb +58 -0
  58. data/lib/rng/optional.rb +58 -0
  59. data/lib/rng/param.rb +30 -0
  60. data/lib/rng/parent_ref.rb +28 -0
  61. data/lib/rng/parse_rnc.rb +26 -0
  62. data/lib/rng/parse_tree_processor.rb +695 -0
  63. data/lib/rng/pattern.rb +24 -0
  64. data/lib/rng/ref.rb +28 -0
  65. data/lib/rng/rnc_builder.rb +927 -0
  66. data/lib/rng/rnc_parser.rb +672 -115
  67. data/lib/rng/rnc_to_rng_converter.rb +1408 -0
  68. data/lib/rng/schema_preamble.rb +73 -0
  69. data/lib/rng/schema_validator.rb +1622 -0
  70. data/lib/rng/start.rb +57 -6
  71. data/lib/rng/test_suite_parser.rb +168 -0
  72. data/lib/rng/text.rb +29 -0
  73. data/lib/rng/to_rnc.rb +24 -0
  74. data/lib/rng/value.rb +28 -0
  75. data/lib/rng/version.rb +1 -1
  76. data/lib/rng/zero_or_more.rb +58 -0
  77. data/lib/rng.rb +80 -5
  78. data/rng.gemspec +19 -19
  79. data/scripts/extract_spectest_resources.rb +96 -0
  80. data/spec/fixtures/compacttest.xml +2511 -0
  81. data/spec/fixtures/external/circular_a.rng +7 -0
  82. data/spec/fixtures/external/circular_b.rng +7 -0
  83. data/spec/fixtures/external/circular_main.rng +7 -0
  84. data/spec/fixtures/external/external_ref_lib.rng +7 -0
  85. data/spec/fixtures/external/external_ref_main.rng +7 -0
  86. data/spec/fixtures/external/include_lib.rng +7 -0
  87. data/spec/fixtures/external/include_main.rng +3 -0
  88. data/spec/fixtures/external/nested_chain.rng +6 -0
  89. data/spec/fixtures/external/nested_leaf.rng +7 -0
  90. data/spec/fixtures/external/nested_mid.rng +8 -0
  91. data/spec/fixtures/metanorma/3gpp.rnc +35 -0
  92. data/spec/fixtures/metanorma/3gpp.rng +105 -0
  93. data/spec/fixtures/metanorma/basicdoc.rnc +11 -0
  94. data/spec/fixtures/metanorma/bipm.rnc +148 -0
  95. data/spec/fixtures/metanorma/bipm.rng +376 -0
  96. data/spec/fixtures/metanorma/bsi.rnc +104 -0
  97. data/spec/fixtures/metanorma/bsi.rng +332 -0
  98. data/spec/fixtures/metanorma/csa.rnc +45 -0
  99. data/spec/fixtures/metanorma/csa.rng +131 -0
  100. data/spec/fixtures/metanorma/csd.rnc +43 -0
  101. data/spec/fixtures/metanorma/csd.rng +132 -0
  102. data/spec/fixtures/metanorma/gbstandard.rnc +99 -0
  103. data/spec/fixtures/metanorma/gbstandard.rng +316 -0
  104. data/spec/fixtures/metanorma/iec.rnc +49 -0
  105. data/spec/fixtures/metanorma/iec.rng +193 -0
  106. data/spec/fixtures/metanorma/ietf.rnc +275 -0
  107. data/spec/fixtures/metanorma/ietf.rng +925 -0
  108. data/spec/fixtures/metanorma/iho.rnc +58 -0
  109. data/spec/fixtures/metanorma/iho.rng +179 -0
  110. data/spec/fixtures/metanorma/isodoc.rnc +873 -0
  111. data/spec/fixtures/metanorma/isodoc.rng +2704 -0
  112. data/spec/fixtures/metanorma/isostandard-amd.rnc +43 -0
  113. data/spec/fixtures/metanorma/isostandard-amd.rng +108 -0
  114. data/spec/fixtures/metanorma/isostandard.rnc +166 -0
  115. data/spec/fixtures/metanorma/isostandard.rng +494 -0
  116. data/spec/fixtures/metanorma/itu.rnc +122 -0
  117. data/spec/fixtures/metanorma/itu.rng +377 -0
  118. data/spec/fixtures/metanorma/m3d.rnc +41 -0
  119. data/spec/fixtures/metanorma/m3d.rng +122 -0
  120. data/spec/fixtures/metanorma/mpfd.rnc +36 -0
  121. data/spec/fixtures/metanorma/mpfd.rng +95 -0
  122. data/spec/fixtures/metanorma/nist.rnc +77 -0
  123. data/spec/fixtures/metanorma/nist.rng +216 -0
  124. data/spec/fixtures/metanorma/ogc.rnc +51 -0
  125. data/spec/fixtures/metanorma/ogc.rng +151 -0
  126. data/spec/fixtures/metanorma/reqt.rnc +6 -0
  127. data/spec/fixtures/metanorma/rsd.rnc +36 -0
  128. data/spec/fixtures/metanorma/rsd.rng +95 -0
  129. data/spec/fixtures/metanorma/un.rnc +103 -0
  130. data/spec/fixtures/metanorma/un.rng +367 -0
  131. data/spec/fixtures/rnc/address_book.rnc +10 -0
  132. data/spec/fixtures/rnc/base.rnc +4 -0
  133. data/spec/fixtures/rnc/complex_example.rnc +61 -0
  134. data/spec/fixtures/rnc/grammar_with_trailing.rnc +8 -0
  135. data/spec/fixtures/rnc/main_include_trailing.rnc +3 -0
  136. data/spec/fixtures/rnc/main_with_include.rnc +5 -0
  137. data/spec/fixtures/rnc/test_augment.rnc +10 -0
  138. data/spec/fixtures/rnc/test_isodoc_simple.rnc +9 -0
  139. data/spec/fixtures/rnc/top_level_include.rnc +8 -0
  140. data/spec/fixtures/rng/address_book.rng +20 -0
  141. data/spec/fixtures/rng/relaxng.rng +335 -0
  142. data/spec/fixtures/rng/testSuite.rng +163 -0
  143. data/spec/fixtures/spectest.xml +6845 -0
  144. data/spec/fixtures/spectest_external/case_10_4.7/x +3 -0
  145. data/spec/fixtures/spectest_external/case_10_4.7/y +7 -0
  146. data/spec/fixtures/spectest_external/case_11_4.7/x +3 -0
  147. data/spec/fixtures/spectest_external/case_12_4.7/x +3 -0
  148. data/spec/fixtures/spectest_external/case_13_4.7/x +3 -0
  149. data/spec/fixtures/spectest_external/case_13_4.7/y +3 -0
  150. data/spec/fixtures/spectest_external/case_14_4.7/x +7 -0
  151. data/spec/fixtures/spectest_external/case_15_4.7/x +7 -0
  152. data/spec/fixtures/spectest_external/case_16_4.7/x +5 -0
  153. data/spec/fixtures/spectest_external/case_17_4.7/x +5 -0
  154. data/spec/fixtures/spectest_external/case_18_4.7/x +7 -0
  155. data/spec/fixtures/spectest_external/case_19_4.7/level1.rng +9 -0
  156. data/spec/fixtures/spectest_external/case_19_4.7/level2.rng +7 -0
  157. data/spec/fixtures/spectest_external/case_1_4.5/sub1/x +3 -0
  158. data/spec/fixtures/spectest_external/case_1_4.5/sub3/x +3 -0
  159. data/spec/fixtures/spectest_external/case_1_4.5/x +3 -0
  160. data/spec/fixtures/spectest_external/case_20_4.6/x +3 -0
  161. data/spec/fixtures/spectest_external/case_2_4.5/x +3 -0
  162. data/spec/fixtures/spectest_external/case_3_4.6/x +3 -0
  163. data/spec/fixtures/spectest_external/case_4_4.6/x +3 -0
  164. data/spec/fixtures/spectest_external/case_5_4.6/x +1 -0
  165. data/spec/fixtures/spectest_external/case_6_4.6/x +5 -0
  166. data/spec/fixtures/spectest_external/case_7_4.6/x +1 -0
  167. data/spec/fixtures/spectest_external/case_7_4.6/y +1 -0
  168. data/spec/fixtures/spectest_external/case_8_4.7/x +7 -0
  169. data/spec/fixtures/spectest_external/case_9_4.7/x +7 -0
  170. data/spec/fixtures/spectest_external/resources.json +149 -0
  171. data/spec/rng/advanced_rnc_spec.rb +101 -0
  172. data/spec/rng/compacttest_spec.rb +197 -0
  173. data/spec/rng/datatype_declaration_spec.rb +28 -0
  174. data/spec/rng/div_spec.rb +207 -0
  175. data/spec/rng/external_ref_resolver_spec.rb +122 -0
  176. data/spec/rng/metanorma_conversion_spec.rb +159 -0
  177. data/spec/rng/namespace_declaration_spec.rb +60 -0
  178. data/spec/rng/namespace_support_spec.rb +199 -0
  179. data/spec/rng/rnc_parser_spec.rb +501 -23
  180. data/spec/rng/rnc_roundtrip_spec.rb +135 -0
  181. data/spec/rng/rng_generation_spec.rb +288 -0
  182. data/spec/rng/roundtrip_spec.rb +342 -0
  183. data/spec/rng/schema_preamble_spec.rb +145 -0
  184. data/spec/rng/schema_spec.rb +125 -172
  185. data/spec/rng/spectest_spec.rb +273 -0
  186. data/spec/rng_spec.rb +2 -2
  187. data/spec/spec_helper.rb +7 -9
  188. metadata +188 -8
  189. data/lib/rng/builder.rb +0 -158
  190. data/lib/rng/rng_parser.rb +0 -107
  191. data/lib/rng/schema.rb +0 -18
  192. data/spec/rng/rng_parser_spec.rb +0 -102
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require_relative '../../lib/rng/schema_preamble'
5
+
6
+ RSpec.describe Rng::SchemaPreamble do
7
+ describe '#initialize' do
8
+ it 'creates an empty preamble' do
9
+ preamble = described_class.new
10
+
11
+ expect(preamble.namespaces).to be_empty
12
+ expect(preamble.datatypes).to be_empty
13
+ expect(preamble).to be_empty
14
+ end
15
+ end
16
+
17
+ describe '#add_namespace' do
18
+ it 'adds namespace declarations' do
19
+ preamble = described_class.new
20
+ ns1 = Rng::NamespaceDeclaration.new(uri: 'http://example.com',
21
+ is_default: true)
22
+ ns2 = Rng::NamespaceDeclaration.new(prefix: 'eg', uri: 'http://example.com/eg')
23
+
24
+ preamble.add_namespace(ns1)
25
+ preamble.add_namespace(ns2)
26
+
27
+ expect(preamble.namespaces).to contain_exactly(ns1, ns2)
28
+ expect(preamble).not_to be_empty
29
+ end
30
+ end
31
+
32
+ describe '#add_datatype' do
33
+ it 'adds datatype declarations' do
34
+ preamble = described_class.new
35
+ dt = Rng::DatatypeDeclaration.new(
36
+ prefix: 'xsd',
37
+ uri: 'http://www.w3.org/2001/XMLSchema-datatypes'
38
+ )
39
+
40
+ preamble.add_datatype(dt)
41
+
42
+ expect(preamble.datatypes).to contain_exactly(dt)
43
+ expect(preamble).not_to be_empty
44
+ end
45
+ end
46
+
47
+ describe '#default_namespace' do
48
+ it 'returns the default namespace URI' do
49
+ preamble = described_class.new
50
+ default_ns = Rng::NamespaceDeclaration.new(uri: 'http://example.com',
51
+ is_default: true)
52
+ other_ns = Rng::NamespaceDeclaration.new(prefix: 'eg', uri: 'http://example.com/eg')
53
+
54
+ preamble.add_namespace(default_ns)
55
+ preamble.add_namespace(other_ns)
56
+
57
+ expect(preamble.default_namespace).to eq('http://example.com')
58
+ end
59
+
60
+ it 'returns nil when there is no default namespace' do
61
+ preamble = described_class.new
62
+ ns = Rng::NamespaceDeclaration.new(prefix: 'eg', uri: 'http://example.com')
63
+
64
+ preamble.add_namespace(ns)
65
+
66
+ expect(preamble.default_namespace).to be_nil
67
+ end
68
+ end
69
+
70
+ describe '#namespace_map' do
71
+ it 'returns a map of prefixed namespaces' do
72
+ preamble = described_class.new
73
+ ns1 = Rng::NamespaceDeclaration.new(prefix: 'eg', uri: 'http://example.com')
74
+ ns2 = Rng::NamespaceDeclaration.new(prefix: 'local', uri: '')
75
+ default_ns = Rng::NamespaceDeclaration.new(uri: 'http://default.com',
76
+ is_default: true)
77
+
78
+ preamble.add_namespace(ns1)
79
+ preamble.add_namespace(ns2)
80
+ preamble.add_namespace(default_ns)
81
+
82
+ map = preamble.namespace_map
83
+ expect(map).to eq({
84
+ 'eg' => 'http://example.com',
85
+ 'local' => ''
86
+ })
87
+ end
88
+
89
+ it 'returns empty hash when there are no prefixed namespaces' do
90
+ preamble = described_class.new
91
+ default_ns = Rng::NamespaceDeclaration.new(uri: 'http://example.com',
92
+ is_default: true)
93
+
94
+ preamble.add_namespace(default_ns)
95
+
96
+ expect(preamble.namespace_map).to eq({})
97
+ end
98
+ end
99
+
100
+ describe '#datatype_map' do
101
+ it 'returns a map of datatype prefixes to URIs' do
102
+ preamble = described_class.new
103
+ dt1 = Rng::DatatypeDeclaration.new(prefix: 'xsd', uri: 'http://www.w3.org/2001/XMLSchema-datatypes')
104
+ dt2 = Rng::DatatypeDeclaration.new(prefix: 'custom', uri: 'http://example.com/types')
105
+
106
+ preamble.add_datatype(dt1)
107
+ preamble.add_datatype(dt2)
108
+
109
+ map = preamble.datatype_map
110
+ expect(map).to eq({
111
+ 'xsd' => 'http://www.w3.org/2001/XMLSchema-datatypes',
112
+ 'custom' => 'http://example.com/types'
113
+ })
114
+ end
115
+
116
+ it 'returns empty hash when there are no datatypes' do
117
+ preamble = described_class.new
118
+ expect(preamble.datatype_map).to eq({})
119
+ end
120
+ end
121
+
122
+ describe '#empty?' do
123
+ it 'returns true when preamble has no declarations' do
124
+ preamble = described_class.new
125
+ expect(preamble).to be_empty
126
+ end
127
+
128
+ it 'returns false when preamble has namespace declarations' do
129
+ preamble = described_class.new
130
+ ns = Rng::NamespaceDeclaration.new(uri: 'http://example.com',
131
+ is_default: true)
132
+ preamble.add_namespace(ns)
133
+
134
+ expect(preamble).not_to be_empty
135
+ end
136
+
137
+ it 'returns false when preamble has datatype declarations' do
138
+ preamble = described_class.new
139
+ dt = Rng::DatatypeDeclaration.new(prefix: 'xsd', uri: 'http://www.w3.org/2001/XMLSchema-datatypes')
140
+ preamble.add_datatype(dt)
141
+
142
+ expect(preamble).not_to be_empty
143
+ end
144
+ end
145
+ end
@@ -1,193 +1,146 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
- RSpec.describe Rng::Schema do
4
- let(:rng_parser) { Rng::RngParser.new }
5
- let(:rnc_parser) { Rng::RncParser.new }
6
- let(:builder) { Rng::Builder.new }
3
+ require 'spec_helper'
7
4
 
8
- describe "RNG parsing and building" do
5
+ RSpec.describe Rng::Grammar do
6
+ describe 'RNG parsing' do
9
7
  let(:rng_input) do
10
- <<~RNG
11
- <?xml version="1.0" encoding="UTF-8"?>
12
- <element name="addressBook" xmlns="http://relaxng.org/ns/structure/1.0">
13
- <zeroOrMore>
14
- <element name="card">
15
- <element name="name">
16
- <text/>
17
- </element>
18
- <element name="email">
19
- <text/>
20
- </element>
21
- <optional>
22
- <element name="note">
23
- <text/>
24
- </element>
25
- </optional>
26
- </element>
27
- </zeroOrMore>
28
- </element>
29
- RNG
30
- end
31
-
32
- it "correctly parses and rebuilds RNG" do
33
- parsed = rng_parser.parse(rng_input)
34
- rebuilt = builder.build(parsed, format: :rng)
35
- expect(normalize_xml(rebuilt)).to eq(normalize_xml(rng_input))
8
+ File.read('spec/fixtures/rng/address_book.rng')
9
+ end
10
+
11
+ it 'correctly parses RNG' do
12
+ parsed = Rng.parse(rng_input)
13
+ expect(parsed).to be_a(described_class)
14
+ expect(parsed.element).to be_empty
15
+ expect(parsed.start.first.element.attr_name).to eq('addressBook')
16
+ end
17
+ end
18
+
19
+ describe 'Round-trip testing RNG' do
20
+ # Address Book Tests
21
+ let(:address_book_rng) do
22
+ File.read('spec/fixtures/rng/address_book.rng')
23
+ end
24
+
25
+ # Test Suite Schema Tests
26
+ let(:test_suite_rng) do
27
+ File.read('spec/fixtures/rng/testSuite.rng')
28
+ end
29
+ # RELAX NG Schema Tests
30
+ let(:relaxng_schema) do
31
+ File.read('spec/fixtures/rng/relaxng.rng')
32
+ end
33
+
34
+ it 'correctly round-trips address_book.rng (analogous comparison)' do
35
+ parsed = described_class.from_xml(address_book_rng)
36
+ regenerated = parsed.to_xml
37
+ expect(regenerated.gsub(/<!--.*?-->/m, '')).to be_xml_equivalent_to(address_book_rng.gsub(/<!--.*?-->/m, ''))
38
+ end
39
+
40
+ it 'correctly round-trips address_book.rng (formatted equivalent comparison)' do
41
+ parsed = described_class.from_xml(address_book_rng)
42
+ regenerated = parsed.to_xml
43
+ expect(regenerated).to be_xml_equivalent_to(address_book_rng)
44
+ end
45
+
46
+ it 'correctly round-trips relaxng.rng (analogous comparison)' do
47
+ parsed = described_class.from_xml(relaxng_schema)
48
+ regenerated = parsed.to_xml
49
+ # Strip comments for comparison since Lutaml doesn't preserve them
50
+ expect(regenerated.gsub(/<!--.*?-->/m, '')).to be_xml_equivalent_to(relaxng_schema.gsub(/<!--.*?-->/m, ''))
51
+ end
52
+
53
+ it 'correctly round-trips relaxng.rng (formatted equivalent comparison)' do
54
+ parsed = described_class.from_xml(relaxng_schema)
55
+ regenerated = parsed.to_xml
56
+ expect(regenerated.gsub(/<!--.*?-->/m, '')).to be_xml_equivalent_to(relaxng_schema.gsub(/<!--.*?-->/m, ''))
57
+ end
58
+
59
+ it 'correctly round-trips testSuite.rng (analogous comparison)' do
60
+ parsed = described_class.from_xml(test_suite_rng)
61
+ regenerated = parsed.to_xml
62
+ expect(regenerated.gsub(/<!--.*?-->/m, '')).to be_xml_equivalent_to(test_suite_rng.gsub(/<!--.*?-->/m, ''))
63
+ end
64
+
65
+ it 'correctly round-trips testSuite.rng (formatted equivalent comparison)' do
66
+ parsed = described_class.from_xml(test_suite_rng)
67
+ regenerated = parsed.to_xml
68
+ expect(regenerated.gsub(/<!--.*?-->/m, '')).to be_xml_equivalent_to(test_suite_rng.gsub(/<!--.*?-->/m, ''))
36
69
  end
37
70
  end
38
71
 
39
- describe "RNC parsing and building" do
72
+ describe 'RNC parsing' do
40
73
  let(:rnc_input) do
41
- <<~RNC
42
- element addressBook {
43
- element card {
44
- element name { text },
45
- element email { text },
46
- element note { text }?
47
- }*
48
- }
49
- RNC
50
- end
51
-
52
- it "correctly parses and rebuilds RNC" do
53
- parsed = rnc_parser.parse(rnc_input)
54
- rebuilt = builder.build(parsed, format: :rnc)
55
- expect(rebuilt.gsub(/\s+/, "")).to eq(rnc_input.gsub(/\s+/, ""))
74
+ File.read('spec/fixtures/rnc/address_book.rnc')
75
+ end
76
+
77
+ it 'correctly parses RNC' do
78
+ parsed = Rng.parse_rnc(rnc_input)
79
+ expect(parsed).to be_a(described_class)
80
+ start_element = parsed.start.first.element
81
+ expect(start_element).to be_a(Rng::Element)
82
+ expect(start_element.attr_name).to eq('addressBook')
56
83
  end
57
84
  end
58
85
 
59
- describe "RNG to RNC conversion" do
86
+ describe 'RNG to RNC conversion' do
60
87
  let(:rng_input) do
61
- <<~RNG
62
- <?xml version="1.0" encoding="UTF-8"?>
63
- <element name="addressBook" xmlns="http://relaxng.org/ns/structure/1.0">
64
- <zeroOrMore>
65
- <element name="card">
66
- <element name="name">
67
- <text/>
68
- </element>
69
- <element name="email">
70
- <text/>
71
- </element>
72
- </element>
73
- </zeroOrMore>
74
- </element>
75
- RNG
76
- end
77
-
78
- let(:expected_rnc) do
79
- <<~RNC
80
- element addressBook {
81
- element card {
82
- element name { text },
83
- element email { text }
84
- }*
85
- }
86
- RNC
87
- end
88
-
89
- it "correctly converts RNG to RNC" do
90
- parsed = rng_parser.parse(rng_input)
91
- rnc = builder.build(parsed, format: :rnc)
92
- expect(rnc.gsub(/\s+/, "")).to eq(expected_rnc.gsub(/\s+/, ""))
88
+ File.read('spec/fixtures/rng/address_book.rng')
89
+ end
90
+
91
+ it 'correctly converts RNG to RNC' do
92
+ parsed = Rng.parse(rng_input)
93
+ rnc = Rng.to_rnc(parsed)
94
+ expect(rnc).to include('element addressBook')
95
+ expect(rnc).to include('element card')
96
+ expect(rnc).to include('element name')
97
+ expect(rnc).to include('element email')
93
98
  end
94
99
  end
95
100
 
96
- describe "RNC to RNG conversion" do
101
+ describe 'RNC to RNG conversion' do
97
102
  let(:rnc_input) do
98
- <<~RNC
99
- element addressBook {
100
- element card {
101
- element name { text },
102
- element email { text }
103
- }*
104
- }
105
- RNC
106
- end
107
-
108
- let(:expected_rng) do
109
- <<~RNG
110
- <element name="addressBook" xmlns="http://relaxng.org/ns/structure/1.0">
111
- <zeroOrMore>
112
- <element name="card">
113
- <element name="name">
114
- <text/>
115
- </element>
116
- <element name="email">
117
- <text/>
118
- </element>
119
- </element>
120
- </zeroOrMore>
121
- </element>
122
- RNG
123
- end
124
-
125
- it "correctly converts RNC to RNG" do
126
- parsed = rnc_parser.parse(rnc_input)
127
- rng = builder.build(parsed, format: :rng)
128
- expect(rng.gsub(/\s+/, "")).to eq(expected_rng.gsub(/\s+/, ""))
103
+ File.read('spec/fixtures/rnc/address_book.rnc')
104
+ end
105
+
106
+ it 'correctly converts RNC to RNG' do
107
+ parsed = Rng.parse_rnc(rnc_input)
108
+ expect(parsed).to be_a(described_class)
109
+ start_element = parsed.start.first.element
110
+ expect(start_element).to be_a(Rng::Element)
111
+ expect(start_element.attr_name).to eq('addressBook')
129
112
  end
130
113
  end
131
114
 
132
- describe "Complex schema parsing and building" do
133
- let(:complex_rng_input) do
134
- <<~RNG
135
- <grammar xmlns="http://relaxng.org/ns/structure/1.0">
136
- <start>
137
- <ref name="addressBook"/>
138
- </start>
139
-
140
- <define name="addressBook">
141
- <element name="addressBook">
142
- <zeroOrMore>
143
- <ref name="card"/>
144
- </zeroOrMore>
145
- </element>
146
- </define>
147
-
148
- <define name="card">
149
- <element name="card">
150
- <ref name="name"/>
151
- <ref name="email"/>
152
- <optional>
153
- <ref name="note"/>
154
- </optional>
155
- </element>
156
- </define>
157
-
158
- <define name="name">
159
- <element name="name">
160
- <text/>
161
- </element>
162
- </define>
163
-
164
- <define name="email">
165
- <element name="email">
166
- <text/>
167
- </element>
168
- </define>
169
-
170
- <define name="note">
171
- <element name="note">
172
- <text/>
173
- </element>
174
- </define>
175
- </grammar>
176
- RNG
177
- end
178
-
179
- it "correctly parses and rebuilds complex RNG" do
180
- parsed = rng_parser.parse(complex_rng_input)
181
- rebuilt = builder.build(parsed, format: :rng)
182
- expect(rebuilt.gsub(/\s+/, "")).to eq(complex_rng_input.gsub(/\s+/, ""))
183
- end
184
-
185
- it "correctly converts complex RNG to RNC" do
186
- parsed = rng_parser.parse(complex_rng_input)
187
- rnc = builder.build(parsed, format: :rnc)
188
- reparsed = rnc_parser.parse(rnc)
189
- rng_again = builder.build(reparsed, format: :rng)
190
- expect(rng_again.gsub(/\s+/, "")).to eq(complex_rng_input.gsub(/\s+/, ""))
115
+ describe 'Round-trip testing RNG/RNC' do
116
+ let(:rng_input) do
117
+ File.read('spec/fixtures/rng/address_book.rng')
118
+ end
119
+
120
+ let(:rnc_input) do
121
+ File.read('spec/fixtures/rnc/address_book.rnc')
122
+ end
123
+
124
+ it 'correctly round-trips RNG to RNC and back' do
125
+ parsed_rng = Rng.parse(rng_input)
126
+ rnc = Rng.to_rnc(parsed_rng)
127
+ parsed_rnc = Rng.parse_rnc(rnc)
128
+
129
+ # Compare key properties
130
+ rng_start_elem = parsed_rng.start.first.element
131
+ rnc_start_elem = parsed_rnc.start.first.element
132
+ expect(rnc_start_elem.attr_name).to eq(rng_start_elem.attr_name)
133
+ end
134
+
135
+ it 'correctly round-trips RNC to RNG and back' do
136
+ parsed_rnc = Rng.parse_rnc(rnc_input)
137
+ rng_xml = parsed_rnc.to_xml
138
+ parsed_rng = Rng.parse(rng_xml)
139
+
140
+ # Compare key properties
141
+ rng_start_elem = parsed_rng.start.first.element
142
+ rnc_start_elem = parsed_rnc.start.first.element
143
+ expect(rng_start_elem.attr_name).to eq(rnc_start_elem.attr_name)
191
144
  end
192
145
  end
193
146
  end