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
data/lib/rng/start.rb CHANGED
@@ -1,14 +1,65 @@
1
- require "lutaml/model"
2
- require_relative "element"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rng
5
4
  class Start < Lutaml::Model::Serializable
6
- attribute :ref, :string
7
- attribute :elements, Element, collection: true
5
+ attribute :id, :string
6
+ attribute :ns, :string
7
+ attribute :datatypeLibrary, :string
8
+ attribute :combine, :string
9
+ attribute :ref, Ref
10
+ attribute :element, Element
11
+ attribute :choice, Choice
12
+ attribute :group, Group
13
+ attribute :interleave, Interleave
14
+ attribute :mixed, Mixed
15
+ attribute :optional, Optional
16
+ attribute :zeroOrMore, ZeroOrMore
17
+ attribute :oneOrMore, OneOrMore
18
+ attribute :text, Text
19
+ attribute :empty, Empty
20
+ attribute :value, Value
21
+ attribute :data, Data
22
+ attribute :list, List
23
+ attribute :parentRef, ParentRef
24
+ attribute :notAllowed, NotAllowed
25
+ attribute :grammar, Grammar
26
+ attribute :documentation, Documentation
8
27
 
9
28
  xml do
10
- map_attribute "ref", to: :ref
11
- map_element "element", to: :elements
29
+ element 'start'
30
+ ordered
31
+
32
+ namespace ::Rng::Namespaces::RngNamespace
33
+
34
+ map_attribute 'id', to: :id
35
+ map_attribute 'ns', to: :ns, value_map: {
36
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
37
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
38
+ }
39
+ map_attribute 'datatypeLibrary', to: :datatypeLibrary, value_map: {
40
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
41
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
42
+ }
43
+ map_attribute 'combine', to: :combine
44
+
45
+ map_element 'ref', to: :ref
46
+ map_element 'element', to: :element
47
+ map_element 'choice', to: :choice
48
+ map_element 'group', to: :group
49
+ map_element 'interleave', to: :interleave
50
+ map_element 'mixed', to: :mixed
51
+ map_element 'optional', to: :optional
52
+ map_element 'zeroOrMore', to: :zeroOrMore
53
+ map_element 'oneOrMore', to: :oneOrMore
54
+ map_element 'text', to: :text
55
+ map_element 'empty', to: :empty
56
+ map_element 'value', to: :value
57
+ map_element 'data', to: :data
58
+ map_element 'list', to: :list
59
+ map_element 'parentRef', to: :parentRef
60
+ map_element 'notAllowed', to: :notAllowed
61
+ map_element 'grammar', to: :grammar
62
+ map_element 'documentation', to: :documentation
12
63
  end
13
64
  end
14
65
  end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ module Rng
6
+ # Parses RELAX NG test suite XML files (like compacttest.xml)
7
+ class TestSuiteParser
8
+ # Represents a single test case with RNC and RNG versions
9
+ class TestCase
10
+ attr_reader :id, :compact_correct, :compact_incorrect,
11
+ :xml_correct, :xml_incorrect,
12
+ :compact_resources, :xml_resources
13
+
14
+ def initialize(id:)
15
+ @id = id
16
+ @compact_correct = nil
17
+ @compact_incorrect = nil
18
+ @xml_correct = nil
19
+ @xml_incorrect = nil
20
+ @compact_resources = {}
21
+ @xml_resources = {}
22
+ end
23
+
24
+ def valid_rnc?
25
+ !compact_correct.nil?
26
+ end
27
+
28
+ def invalid_rnc?
29
+ !compact_incorrect.nil?
30
+ end
31
+
32
+ def valid_rng?
33
+ !xml_correct.nil?
34
+ end
35
+
36
+ def has_resources?
37
+ !compact_resources.empty? || !xml_resources.empty?
38
+ end
39
+
40
+ def description
41
+ if valid_rnc? && valid_rng?
42
+ 'RNC ↔ RNG conversion'
43
+ elsif valid_rnc?
44
+ 'Valid RNC parsing'
45
+ elsif invalid_rnc?
46
+ 'Invalid RNC rejection'
47
+ else
48
+ 'Test case'
49
+ end
50
+ end
51
+ end
52
+
53
+ attr_reader :test_cases
54
+
55
+ def initialize(xml_content)
56
+ @doc = Nokogiri::XML(xml_content)
57
+ @test_cases = []
58
+ parse_test_cases
59
+ end
60
+
61
+ # Load test suite from file path
62
+ def self.load(file_path)
63
+ content = File.read(file_path)
64
+ new(content)
65
+ end
66
+
67
+ # Get test cases with valid RNC that should parse
68
+ def valid_rnc_cases
69
+ test_cases.select(&:valid_rnc?)
70
+ end
71
+
72
+ # Get test cases with invalid RNC that should fail
73
+ def invalid_rnc_cases
74
+ test_cases.select(&:invalid_rnc?)
75
+ end
76
+
77
+ # Get test cases with both RNC and RNG for round-trip testing
78
+ def roundtrip_cases
79
+ test_cases.select { |tc| tc.valid_rnc? && tc.valid_rng? }
80
+ end
81
+
82
+ # Get test cases with resources (external files)
83
+ def resource_cases
84
+ test_cases.select(&:has_resources?)
85
+ end
86
+
87
+ private
88
+
89
+ def parse_test_cases
90
+ @doc.xpath('//testCase').each_with_index do |test_node, index|
91
+ test_case = TestCase.new(id: index + 1)
92
+
93
+ # Parse compact syntax (RNC)
94
+ parse_compact_section(test_node, test_case)
95
+
96
+ # Parse XML syntax (RNG)
97
+ parse_xml_section(test_node, test_case)
98
+
99
+ @test_cases << test_case
100
+ end
101
+ end
102
+
103
+ def parse_compact_section(test_node, test_case)
104
+ compact_node = test_node.at_xpath('compact')
105
+ return unless compact_node
106
+
107
+ # Parse resources
108
+ compact_node.xpath('resource').each do |resource_node|
109
+ name = resource_node['name']
110
+ content = resource_node.text.strip
111
+ test_case.compact_resources[name] = content
112
+ end
113
+
114
+ # Parse correct RNC
115
+ correct_node = compact_node.at_xpath('correct')
116
+ if correct_node
117
+ test_case.instance_variable_set(
118
+ :@compact_correct,
119
+ correct_node.text.strip
120
+ )
121
+ end
122
+
123
+ # Parse incorrect RNC
124
+ incorrect_node = compact_node.at_xpath('incorrect')
125
+ return unless incorrect_node
126
+
127
+ test_case.instance_variable_set(
128
+ :@compact_incorrect,
129
+ incorrect_node.text.strip
130
+ )
131
+ end
132
+
133
+ def parse_xml_section(test_node, test_case)
134
+ xml_node = test_node.at_xpath('xml')
135
+ return unless xml_node
136
+
137
+ # Parse resources
138
+ xml_node.xpath('resource').each do |resource_node|
139
+ name = resource_node['name']
140
+ # Get the first element child as RNG content
141
+ content_node = resource_node.elements.first
142
+ content = content_node ? content_node.to_xml : ''
143
+ test_case.xml_resources[name] = content
144
+ end
145
+
146
+ # Parse correct RNG
147
+ correct_node = xml_node.at_xpath('correct')
148
+ if correct_node
149
+ # Get the first element child as RNG content
150
+ rng_node = correct_node.elements.first
151
+ test_case.instance_variable_set(
152
+ :@xml_correct,
153
+ rng_node ? rng_node.to_xml : ''
154
+ )
155
+ end
156
+
157
+ # Parse incorrect RNG (if any)
158
+ incorrect_node = xml_node.at_xpath('incorrect')
159
+ return unless incorrect_node
160
+
161
+ rng_node = incorrect_node.elements.first
162
+ test_case.instance_variable_set(
163
+ :@xml_incorrect,
164
+ rng_node ? rng_node.to_xml : ''
165
+ )
166
+ end
167
+ end
168
+ end
data/lib/rng/text.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rng
4
+ class Text < Lutaml::Model::Serializable
5
+ attribute :id, :string
6
+ attribute :ns, :string
7
+ attribute :datatypeLibrary, :string
8
+ attribute :value, :string
9
+
10
+ xml do
11
+ element 'text'
12
+ namespace ::Rng::Namespaces::RngNamespace
13
+
14
+ map_attribute 'id', to: :id
15
+ map_attribute 'ns', to: :ns, value_map: {
16
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
17
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
18
+ }
19
+ map_attribute 'datatypeLibrary', to: :datatypeLibrary, value_map: {
20
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
21
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
22
+ }
23
+ map_content to: :value, value_map: {
24
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
25
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
26
+ }
27
+ end
28
+ end
29
+ end
data/lib/rng/to_rnc.rb ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rnc_parser'
4
+
5
+ module Rng
6
+ # RNG to RNC converter module
7
+ # Provides functionality to convert RELAX NG XML Schema (RNG) to RELAX NG Compact Syntax (RNC)
8
+ module ToRnc
9
+ class << self
10
+ # Convert an RNG schema object to RNC syntax
11
+ # @param schema [Rng::Grammar] The schema to convert
12
+ # @return [String] The RNC representation of the schema
13
+ def convert(schema)
14
+ # Delegate to RncParser which has the actual implementation via RncBuilder
15
+ RncParser.to_rnc(schema)
16
+ end
17
+ end
18
+ end
19
+
20
+ # Add class-level conversion method
21
+ def self.to_rnc(schema)
22
+ ToRnc.convert(schema)
23
+ end
24
+ end
data/lib/rng/value.rb ADDED
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rng
4
+ class Value < Lutaml::Model::Serializable
5
+ attribute :id, :string
6
+ attribute :ns, :string
7
+ attribute :datatypeLibrary, :string
8
+ attribute :type, :string
9
+ attribute :value, :string
10
+
11
+ xml do
12
+ element 'value'
13
+ namespace ::Rng::Namespaces::RngNamespace
14
+
15
+ map_attribute 'type', to: :type
16
+ map_attribute 'id', to: :id
17
+ map_attribute 'ns', to: :ns, value_map: {
18
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
19
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
20
+ }
21
+ map_attribute 'datatypeLibrary', to: :datatypeLibrary, value_map: {
22
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
23
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
24
+ }
25
+ map_content to: :value
26
+ end
27
+ end
28
+ end
data/lib/rng/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rng
4
- VERSION = "0.1.1"
4
+ VERSION = '0.3.3'
5
5
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rng
4
+ class ZeroOrMore < Lutaml::Model::Serializable
5
+ attribute :id, :string
6
+ attribute :ns, :string
7
+ attribute :datatypeLibrary, :string
8
+ attribute :element, Element, collection: true, initialize_empty: true
9
+ attribute :attribute, Attribute, collection: true, initialize_empty: true
10
+ attribute :ref, Ref, collection: true, initialize_empty: true
11
+ attribute :choice, Choice, collection: true, initialize_empty: true
12
+ attribute :group, Group, collection: true, initialize_empty: true
13
+ attribute :interleave, Interleave, collection: true, initialize_empty: true
14
+ attribute :mixed, Mixed, collection: true, initialize_empty: true
15
+ attribute :optional, Optional, collection: true, initialize_empty: true
16
+ attribute :zeroOrMore, ZeroOrMore, collection: true, initialize_empty: true
17
+ attribute :oneOrMore, OneOrMore, collection: true, initialize_empty: true
18
+ attribute :text, Text, collection: true, initialize_empty: true
19
+ attribute :empty, Empty, collection: true, initialize_empty: true
20
+ attribute :value, Value, collection: true, initialize_empty: true
21
+ attribute :data, Data, collection: true, initialize_empty: true
22
+ attribute :list, List, collection: true, initialize_empty: true
23
+ attribute :notAllowed, NotAllowed, collection: true, initialize_empty: true
24
+
25
+ xml do
26
+ element 'zeroOrMore'
27
+ ordered
28
+
29
+ namespace ::Rng::Namespaces::RngNamespace
30
+
31
+ map_attribute 'id', to: :id
32
+ map_attribute 'ns', to: :ns, value_map: {
33
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
34
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
35
+ }
36
+ map_attribute 'datatypeLibrary', to: :datatypeLibrary, value_map: {
37
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
38
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
39
+ }
40
+ map_element 'element', to: :element
41
+ map_element 'attribute', to: :attribute
42
+ map_element 'ref', to: :ref
43
+ map_element 'choice', to: :choice
44
+ map_element 'group', to: :group
45
+ map_element 'interleave', to: :interleave
46
+ map_element 'mixed', to: :mixed
47
+ map_element 'optional', to: :optional
48
+ map_element 'zeroOrMore', to: :zeroOrMore
49
+ map_element 'oneOrMore', to: :oneOrMore
50
+ map_element 'text', to: :text
51
+ map_element 'empty', to: :empty
52
+ map_element 'value', to: :value
53
+ map_element 'data', to: :data
54
+ map_element 'list', to: :list
55
+ map_element 'notAllowed', to: :notAllowed
56
+ end
57
+ end
58
+ end
data/lib/rng.rb CHANGED
@@ -1,12 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "rng/version"
4
- require_relative "rng/rnc_parser"
5
- require_relative "rng/rng_parser"
6
- require_relative "rng/builder"
3
+ require 'lutaml/model'
7
4
 
8
5
  module Rng
9
6
  class Error < StandardError; end
10
7
 
11
- # Your code goes here...
8
+ autoload :Namespaces, File.expand_path('rng/namespaces', __dir__)
9
+
10
+ # Autoload all model classes - avoids circular dependency issues
11
+ autoload :Pattern, File.expand_path('rng/pattern', __dir__)
12
+ autoload :Text, File.expand_path('rng/text', __dir__)
13
+ autoload :Empty, File.expand_path('rng/empty', __dir__)
14
+ autoload :Value, File.expand_path('rng/value', __dir__)
15
+ autoload :Param, File.expand_path('rng/param', __dir__)
16
+ autoload :NotAllowed, File.expand_path('rng/not_allowed', __dir__)
17
+ autoload :Name, File.expand_path('rng/name', __dir__)
18
+ autoload :AnyName, File.expand_path('rng/any_name', __dir__)
19
+ autoload :NsName, File.expand_path('rng/ns_name', __dir__)
20
+ autoload :Except, File.expand_path('rng/except', __dir__)
21
+ autoload :Data, File.expand_path('rng/data', __dir__)
22
+ autoload :List, File.expand_path('rng/list', __dir__)
23
+ autoload :Ref, File.expand_path('rng/ref', __dir__)
24
+ autoload :ParentRef, File.expand_path('rng/parent_ref', __dir__)
25
+ autoload :ExternalRef, File.expand_path('rng/external_ref', __dir__)
26
+ autoload :Optional, File.expand_path('rng/optional', __dir__)
27
+ autoload :ZeroOrMore, File.expand_path('rng/zero_or_more', __dir__)
28
+ autoload :OneOrMore, File.expand_path('rng/one_or_more', __dir__)
29
+ autoload :Choice, File.expand_path('rng/choice', __dir__)
30
+ autoload :Group, File.expand_path('rng/group', __dir__)
31
+ autoload :Interleave, File.expand_path('rng/interleave', __dir__)
32
+ autoload :Mixed, File.expand_path('rng/mixed', __dir__)
33
+ autoload :Attribute, File.expand_path('rng/attribute', __dir__)
34
+ autoload :Element, File.expand_path('rng/element', __dir__)
35
+ autoload :Define, File.expand_path('rng/define', __dir__)
36
+ autoload :Start, File.expand_path('rng/start', __dir__)
37
+ autoload :Div, File.expand_path('rng/div', __dir__)
38
+ autoload :Include, File.expand_path('rng/include', __dir__)
39
+ autoload :Grammar, File.expand_path('rng/grammar', __dir__)
40
+ autoload :Documentation, File.expand_path('rng/documentation', __dir__)
41
+ autoload :TestSuiteParser, File.expand_path('rng/test_suite_parser', __dir__)
42
+ autoload :Value, File.expand_path('rng/value', __dir__)
43
+
44
+ # Autoload parser support classes
45
+ autoload :ParseTreeProcessor, File.expand_path('rng/parse_tree_processor', __dir__)
46
+ autoload :RncToRngConverter, File.expand_path('rng/rnc_to_rng_converter', __dir__)
47
+ autoload :SchemaPreamble, File.expand_path('rng/schema_preamble', __dir__)
48
+ autoload :NamespaceDeclaration, File.expand_path('rng/namespace_declaration', __dir__)
49
+ autoload :DatatypeDeclaration, File.expand_path('rng/datatype_declaration', __dir__)
50
+
51
+ # Schema validation
52
+ autoload :SchemaValidator, File.expand_path('rng/schema_validator', __dir__)
53
+ autoload :SchemaValidationError, File.expand_path('rng/schema_validator', __dir__)
54
+
55
+ # External reference resolution
56
+ autoload :ExternalRefResolver, File.expand_path('rng/external_ref_resolver', __dir__)
57
+
58
+ # Autoload parsers and builders
59
+ autoload :RncParser, File.expand_path('rng/rnc_parser', __dir__)
60
+ autoload :ParseRnc, File.expand_path('rng/parse_rnc', __dir__)
61
+ autoload :ToRnc, File.expand_path('rng/to_rnc', __dir__)
62
+ autoload :RncBuilder, File.expand_path('rng/rnc_builder', __dir__)
63
+ autoload :IncludeProcessor, File.expand_path('rng/include_processor', __dir__)
64
+
65
+ module_function
66
+
67
+ def parse(rng, location: nil, nested_schema: false, validate: false, resolve_external: false)
68
+ SchemaValidator.validate(rng) if validate && !nested_schema
69
+ grammar = Grammar.from_xml(rng)
70
+ return grammar unless resolve_external
71
+
72
+ ExternalRefResolver.new(grammar, location: location).resolve
73
+ end
74
+
75
+ def self.parse_rnc(rnc)
76
+ ParseRnc.parse(rnc)
77
+ end
78
+
79
+ def self.parse_file(file_path)
80
+ RncParser.parse_file(file_path)
81
+ end
82
+
83
+ def self.to_rnc(schema)
84
+ # Convert RNG schema to RNC
85
+ ToRnc.convert(schema)
86
+ end
12
87
  end
data/rng.gemspec CHANGED
@@ -1,38 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/rng/version"
3
+ require_relative 'lib/rng/version'
4
4
 
5
5
  all_files_in_git = Dir.chdir(File.expand_path(__dir__)) do
6
6
  `git ls-files -z`.split("\x0")
7
7
  end
8
8
 
9
9
  Gem::Specification.new do |spec|
10
- spec.name = "rng"
10
+ spec.name = 'rng'
11
11
  spec.version = Rng::VERSION
12
- spec.authors = ["Ribose"]
13
- spec.email = ["open.source@ribose.com"]
12
+ spec.authors = ['Ribose']
13
+ spec.email = ['open.source@ribose.com']
14
14
 
15
- spec.summary = "Library to parse and build RELAX NG (RNG) and RELAX NG Compact Syntax (RNC) schemas."
16
- spec.homepage = "https://github.com/lutaml/rng"
17
- spec.license = "BSD-2-Clause"
18
- spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
15
+ spec.summary = 'Library to parse and build RELAX NG (RNG) and RELAX NG Compact Syntax (RNC) schemas.'
16
+ spec.homepage = 'https://github.com/lutaml/rng'
17
+ spec.license = 'BSD-2-Clause'
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
19
19
 
20
- spec.metadata["homepage_uri"] = spec.homepage
21
- spec.metadata["source_code_uri"] = spec.homepage
22
- spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = spec.homepage
22
+ spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
23
23
 
24
24
  # Specify which files should be added to the gem when it is released.
25
25
  spec.files = all_files_in_git
26
- .reject { |f| f.match(%r{\A(?:test|features|bin|\.)/}) }
26
+ .reject { |f| f.match(%r{\A(?:test|features|bin|\.)/}) }
27
27
 
28
- spec.bindir = "exe"
28
+ spec.bindir = 'exe'
29
29
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
30
+ spec.require_paths = ['lib']
31
31
 
32
- spec.add_dependency "lutaml-model"
33
- spec.add_dependency "parslet"
34
- spec.add_dependency "nokogiri"
32
+ spec.add_dependency 'lutaml-model', '~> 0.8.0'
33
+ spec.add_dependency 'nokogiri'
34
+ spec.add_dependency 'parslet'
35
35
 
36
- # spec.add_dependency "thor"
37
- spec.metadata["rubygems_mfa_required"] = "true"
36
+ spec.add_dependency 'thor'
37
+ spec.metadata['rubygems_mfa_required'] = 'true'
38
38
  end
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Extracts external test resources from Jing-Trang's spectest.xml
5
+ # and creates fixture files for testing external href resolution.
6
+ #
7
+ # Creates:
8
+ # spec/fixtures/spectest_external/ - extracted resources organized by test case
9
+ # spec/fixtures/spectest_external/resources.json - mapping of test indices to files
10
+
11
+ require 'fileutils'
12
+ require 'json'
13
+ require 'nokogiri'
14
+
15
+ SPECTEST_XML = File.expand_path('~/src/external/jing-trang/mod/rng-validate/test/spectest.xml')
16
+ OUTPUT_DIR = 'spec/fixtures/spectest_external'
17
+ MAPPING_FILE = File.join(OUTPUT_DIR, 'resources.json')
18
+
19
+ def extract_resources
20
+ unless File.exist?(SPECTEST_XML)
21
+ puts "Error: spectest.xml not found at #{SPECTEST_XML}"
22
+ puts 'Please ensure Jing-Trang is checked out at ~/src/external/jing-trang'
23
+ exit 1
24
+ end
25
+
26
+ doc = Nokogiri::XML(File.read(SPECTEST_XML))
27
+
28
+ # Clean up existing fixtures
29
+ FileUtils.rm_rf(OUTPUT_DIR)
30
+ FileUtils.mkdir_p(OUTPUT_DIR)
31
+
32
+ # Find all test cases with resources
33
+ test_cases = doc.xpath('//testCase[resource]')
34
+
35
+ puts "Found #{test_cases.count} test cases with resources"
36
+ puts "Extracting to #{OUTPUT_DIR}..."
37
+
38
+ mapping = {}
39
+
40
+ test_cases.each_with_index do |tc, idx|
41
+ case_num = idx + 1
42
+ section = tc.at_xpath('section')&.text || 'unknown'
43
+ documentation = tc.at_xpath('documentation')&.text || ''
44
+
45
+ case_dir = File.join(OUTPUT_DIR, "case_#{case_num}_#{section}")
46
+ mapping[case_num] = {
47
+ section: section,
48
+ documentation: documentation,
49
+ resources: []
50
+ }
51
+
52
+ # Handle resources in directories
53
+ FileUtils.mkdir_p(case_dir)
54
+ tc.xpath('.//dir').each do |dir|
55
+ dir_name = dir['name']
56
+ dir_path = File.join(case_dir, dir_name)
57
+ FileUtils.mkdir_p(dir_path)
58
+
59
+ dir.xpath('.//resource').each do |res|
60
+ resource_name = res['name']
61
+ content = res.inner_html.strip
62
+ next if content.empty?
63
+
64
+ resource_path = File.join(dir_path, resource_name)
65
+ File.write(resource_path, content)
66
+ mapping[case_num][:resources] << "#{dir_name}/#{resource_name}"
67
+ end
68
+ end
69
+
70
+ # Handle resources at root level
71
+ tc.xpath('./resource').each do |res|
72
+ resource_name = res['name']
73
+ content = res.inner_html.strip
74
+ next if content.empty?
75
+
76
+ resource_path = File.join(case_dir, resource_name)
77
+ File.write(resource_path, content)
78
+ mapping[case_num][:resources] << resource_name
79
+ end
80
+ end
81
+
82
+ # Write mapping file
83
+ File.write(MAPPING_FILE, JSON.pretty_generate(mapping))
84
+
85
+ puts "\nExtracted #{test_cases.count} test cases to #{OUTPUT_DIR}"
86
+ puts "Created mapping file: #{MAPPING_FILE}"
87
+
88
+ puts "\nDirectory structure:"
89
+ Dir.glob("#{OUTPUT_DIR}/**/*").reject { |f| File.directory?(f) }.sort.each { |f| puts " #{f}" }
90
+
91
+ puts "\nTo use these fixtures, update spectest_spec.rb to use resolve_external: true"
92
+ puts 'and reference the case_N directories when resolving external refs.'
93
+ end
94
+
95
+ # Run if called directly
96
+ extract_resources if __FILE__ == $PROGRAM_NAME