Module: Idml::Schema::Rnc
- Defined in:
- lib/idml/schema/rnc.rb
Overview
Parses RelaxNG Compact element definitions into data:
element name → wire attribute names. Handles the Adobe
package schemas' shape (NAME_Object = element Name { ... }
with balanced braces; attributes as attribute X { type }).
Two consumers over this seam: the class generator script and
the schema-conformance spec.
Constant Summary collapse
- ELEMENT_DEF =
/(\w+)\s*+=\s*+element\s*+([\w:]+)\s*+\{/- ATTRIBUTE_DEF =
/attribute\s++(\w+)\s*+\{([^}]*)\}/
Class Method Summary collapse
-
.balanced_body(src, from) ⇒ Object
The balanced-brace body starting just after
from(which points past the opening brace); nil when unterminated. - .body_attributes(body) ⇒ Object
- .each_element(paths, &block) ⇒ Object
-
.element_attribute_map(paths) ⇒ Object
{ "Story" => [["Self", ...]], ... } for every element definition across the given RNC files.
-
.element_definition(path, definition_name) ⇒ Object
[xml_element_name, attrs_with_types] for one named definition (
Story_Object) in one file; nil when absent. - .scan_file(path) ⇒ Object
Class Method Details
.balanced_body(src, from) ⇒ Object
The balanced-brace body starting just after from (which
points past the opening brace); nil when unterminated.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/idml/schema/rnc.rb', line 72 def balanced_body(src, from) depth = 1 i = from while i < src.length && depth.positive? case src[i] when "{" then depth += 1 when "}" then depth -= 1 end i += 1 end return nil if depth.positive? src[from...(i - 1)] end |
.body_attributes(body) ⇒ Object
48 49 50 |
# File 'lib/idml/schema/rnc.rb', line 48 def body_attributes(body) body.scan(ATTRIBUTE_DEF).map(&:first).uniq end |
.each_element(paths, &block) ⇒ Object
52 53 54 55 56 |
# File 'lib/idml/schema/rnc.rb', line 52 def each_element(paths, &block) paths.each do |path| scan_file(path, &block) end end |
.element_attribute_map(paths) ⇒ Object
{ "Story" => [["Self", ...]], ... } for every element definition across the given RNC files. An element defined in several files keeps each definition as an alternative (array of attribute-name arrays) — a class conforms when its wire attributes equal ANY one alternative.
22 23 24 25 26 27 28 |
# File 'lib/idml/schema/rnc.rb', line 22 def element_attribute_map(paths) definitions = Hash.new { |h, k| h[k] = [] } each_element(paths) do |element_name, body| definitions[element_name] << body_attributes(body) end definitions.to_h end |
.element_definition(path, definition_name) ⇒ Object
[xml_element_name, attrs_with_types] for one named
definition (Story_Object) in one file; nil when absent.
attrs_with_types: [["Self", "xsd:string"], ...].
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/idml/schema/rnc.rb', line 33 def element_definition(path, definition_name) src = File.read(path) prefix = /#{Regexp.escape(definition_name)}\s*=\s*element\s+/ pattern = /#{prefix}([\w:]+)\s*\{/ match = src.match(pattern) return nil unless match body = balanced_body(src, match.end(0)) return nil unless body attrs_with_types = body.scan(ATTRIBUTE_DEF) .map { |name, type| [name, type.strip] } [match[1], attrs_with_types] end |
.scan_file(path) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/idml/schema/rnc.rb', line 58 def scan_file(path) src = File.read(path) src.scan(ELEMENT_DEF) do match = Regexp.last_match body = balanced_body(src, match.end(0)) next unless body element_name = match[2].sub(/^idPkg:/, "") yield(element_name, body) end end |