xseed 1.0.0
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 +7 -0
- data/.github/workflows/rake.yml +16 -0
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +72 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +432 -0
- data/CHANGELOG.adoc +446 -0
- data/Gemfile +21 -0
- data/LICENSE.adoc +29 -0
- data/README.adoc +386 -0
- data/Rakefile +11 -0
- data/examples/README.adoc +334 -0
- data/examples/advanced_usage.rb +286 -0
- data/examples/html_generation.rb +167 -0
- data/examples/parser_usage.rb +102 -0
- data/examples/schemas/person.xsd +171 -0
- data/examples/simple_generation.rb +149 -0
- data/exe/xseed +6 -0
- data/lib/xseed/cli.rb +376 -0
- data/lib/xseed/documentation/config.rb +101 -0
- data/lib/xseed/documentation/constants.rb +76 -0
- data/lib/xseed/documentation/generators/hierarchy_table_generator.rb +554 -0
- data/lib/xseed/documentation/generators/instance_sample_generator.rb +723 -0
- data/lib/xseed/documentation/generators/properties_table_generator.rb +983 -0
- data/lib/xseed/documentation/html_generator.rb +836 -0
- data/lib/xseed/documentation/html_generator.rb.bak +723 -0
- data/lib/xseed/documentation/presentation/css_generator.rb +510 -0
- data/lib/xseed/documentation/presentation/javascript_generator.rb +151 -0
- data/lib/xseed/documentation/presentation/navigation_builder.rb +169 -0
- data/lib/xseed/documentation/schema_loader.rb +121 -0
- data/lib/xseed/documentation/utils/helpers.rb +205 -0
- data/lib/xseed/documentation/utils/namespaces.rb +149 -0
- data/lib/xseed/documentation/utils/references.rb +135 -0
- data/lib/xseed/documentation/utils/strings.rb +75 -0
- data/lib/xseed/models/element_declaration.rb +144 -0
- data/lib/xseed/parser/xsd_parser.rb +192 -0
- data/lib/xseed/version.rb +5 -0
- data/lib/xseed.rb +76 -0
- data/xseed.gemspec +39 -0
- metadata +158 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xseed
|
|
4
|
+
module Documentation
|
|
5
|
+
module Utils
|
|
6
|
+
# References utility module for handling XSD component references
|
|
7
|
+
#
|
|
8
|
+
# This module provides methods for extracting component names, prefixes,
|
|
9
|
+
# and namespaces from qualified and unqualified references in XML Schema.
|
|
10
|
+
#
|
|
11
|
+
# Based on XS3P references.xsl utilities.
|
|
12
|
+
module References
|
|
13
|
+
XSD_NAMESPACE = "http://www.w3.org/2001/XMLSchema"
|
|
14
|
+
|
|
15
|
+
# Extracts the local name from a reference
|
|
16
|
+
#
|
|
17
|
+
# @param ref [String, nil] The reference string (e.g., "xs:string" or "SimpleType")
|
|
18
|
+
# @return [String] The local name part of the reference
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# get_ref_name("xs:string") #=> "string"
|
|
22
|
+
# get_ref_name("SimpleType") #=> "SimpleType"
|
|
23
|
+
# get_ref_name(nil) #=> ""
|
|
24
|
+
def get_ref_name(ref)
|
|
25
|
+
return "" if ref.nil? || ref.empty?
|
|
26
|
+
|
|
27
|
+
if ref.include?(":")
|
|
28
|
+
# Extract local name after first colon
|
|
29
|
+
ref.split(":", 2).last
|
|
30
|
+
else
|
|
31
|
+
# No prefix, return as-is
|
|
32
|
+
ref
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Extracts the namespace prefix from a reference
|
|
37
|
+
#
|
|
38
|
+
# @param ref [String, nil] The reference string (e.g., "xs:string")
|
|
39
|
+
# @return [String] The namespace prefix or empty string if none
|
|
40
|
+
#
|
|
41
|
+
# @example
|
|
42
|
+
# get_ref_prefix("xs:string") #=> "xs"
|
|
43
|
+
# get_ref_prefix("SimpleType") #=> ""
|
|
44
|
+
# get_ref_prefix(nil) #=> ""
|
|
45
|
+
def get_ref_prefix(ref)
|
|
46
|
+
return "" if ref.nil? || ref.empty?
|
|
47
|
+
|
|
48
|
+
if ref.include?(":")
|
|
49
|
+
# Extract prefix before first colon
|
|
50
|
+
ref.split(":", 2).first
|
|
51
|
+
else
|
|
52
|
+
# No prefix
|
|
53
|
+
""
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Resolves the namespace URI from a reference prefix
|
|
58
|
+
#
|
|
59
|
+
# @param ref [String, nil] The reference string (e.g., "xs:string")
|
|
60
|
+
# @param schema [Object] The schema object with namespaces hash
|
|
61
|
+
# @return [String, nil] The resolved namespace URI or nil
|
|
62
|
+
#
|
|
63
|
+
# @example
|
|
64
|
+
# get_ref_namespace("xs:string", schema) #=> "http://www.w3.org/2001/XMLSchema"
|
|
65
|
+
# get_ref_namespace("SimpleType", schema) #=> nil
|
|
66
|
+
def get_ref_namespace(ref, schema)
|
|
67
|
+
return nil if ref.nil? || ref.empty? || schema.nil?
|
|
68
|
+
|
|
69
|
+
prefix = get_ref_prefix(ref)
|
|
70
|
+
return nil if prefix.empty?
|
|
71
|
+
|
|
72
|
+
schema.namespaces[prefix]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the declared prefix of the schema's target namespace
|
|
76
|
+
#
|
|
77
|
+
# @param schema [Object] The schema object with target_namespace and namespaces
|
|
78
|
+
# @return [String] The prefix for the target namespace or empty string
|
|
79
|
+
#
|
|
80
|
+
# @example
|
|
81
|
+
# get_this_prefix(schema) #=> "tns"
|
|
82
|
+
def get_this_prefix(schema)
|
|
83
|
+
return "" if schema.nil? || schema.target_namespace.nil?
|
|
84
|
+
|
|
85
|
+
target_ns = schema.target_namespace
|
|
86
|
+
namespaces = schema.namespaces || {}
|
|
87
|
+
|
|
88
|
+
# Find the prefix that maps to the target namespace
|
|
89
|
+
prefix = namespaces.find { |_k, v| v == target_ns }&.first
|
|
90
|
+
prefix || ""
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns the declared prefix of the XML Schema namespace
|
|
94
|
+
#
|
|
95
|
+
# @param schema [Object] The schema object with namespaces hash
|
|
96
|
+
# @return [String] The prefix for XSD namespace or empty string
|
|
97
|
+
#
|
|
98
|
+
# @example
|
|
99
|
+
# get_xsd_prefix(schema) #=> "xs" or "xsd"
|
|
100
|
+
def get_xsd_prefix(schema)
|
|
101
|
+
return "" if schema.nil?
|
|
102
|
+
|
|
103
|
+
namespaces = schema.namespaces || {}
|
|
104
|
+
|
|
105
|
+
# Find the prefix that maps to the XSD namespace
|
|
106
|
+
prefix = namespaces.find { |_k, v| v == XSD_NAMESPACE }&.first
|
|
107
|
+
prefix || ""
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Resolves a type reference to its components
|
|
111
|
+
#
|
|
112
|
+
# @param ref [String, nil] The type reference (e.g., "xs:string" or "MyType")
|
|
113
|
+
# @param schema [Object] The schema object with namespaces
|
|
114
|
+
# @return [Hash] Hash with :namespace, :prefix, and :local_name keys
|
|
115
|
+
#
|
|
116
|
+
# @example
|
|
117
|
+
# resolve_type_ref("xs:string", schema)
|
|
118
|
+
# #=> { namespace: "http://www.w3.org/2001/XMLSchema",
|
|
119
|
+
# # prefix: "xs", local_name: "string" }
|
|
120
|
+
def resolve_type_ref(ref, schema)
|
|
121
|
+
if ref.nil? || ref.empty?
|
|
122
|
+
return { namespace: nil, prefix: "",
|
|
123
|
+
local_name: "" }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
namespace: get_ref_namespace(ref, schema),
|
|
128
|
+
prefix: get_ref_prefix(ref),
|
|
129
|
+
local_name: get_ref_name(ref),
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Xseed
|
|
4
|
+
module Documentation
|
|
5
|
+
module Utils
|
|
6
|
+
# String manipulation utilities
|
|
7
|
+
# Ported from xs3p utils/strings.xsl
|
|
8
|
+
module Strings
|
|
9
|
+
# Translates occurrences of single and double quotes with escape
|
|
10
|
+
# characters
|
|
11
|
+
#
|
|
12
|
+
# @param text [String] Text to translate
|
|
13
|
+
# @return [String] Text with escaped quotes
|
|
14
|
+
def self.escape_quotes(text)
|
|
15
|
+
return text if text.nil? || text.empty?
|
|
16
|
+
|
|
17
|
+
# Escape single quotes
|
|
18
|
+
result = text.gsub("'", "\\\\'")
|
|
19
|
+
# Escape double quotes
|
|
20
|
+
result.gsub('"', '\\\\"')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Repeatedly output content
|
|
24
|
+
#
|
|
25
|
+
# @param content [String] The content to be output
|
|
26
|
+
# @param count [Integer] Number of times to output the content
|
|
27
|
+
# @return [String] Repeated content
|
|
28
|
+
def self.repeat(content, count)
|
|
29
|
+
return "" if count <= 0
|
|
30
|
+
|
|
31
|
+
content * count
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Translates occurrences of a string in a piece of text with another
|
|
35
|
+
# string
|
|
36
|
+
#
|
|
37
|
+
# @param value [String] Text to translate
|
|
38
|
+
# @param str_to_replace [String] String to be replaced
|
|
39
|
+
# @param replacement_str [String] Replacement text
|
|
40
|
+
# @return [String] Translated text
|
|
41
|
+
def self.translate(value, str_to_replace, replacement_str)
|
|
42
|
+
return value if value.nil? || value.empty?
|
|
43
|
+
return value unless value.include?(str_to_replace)
|
|
44
|
+
|
|
45
|
+
value.gsub(str_to_replace, replacement_str)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Normalizes whitespace in a string by:
|
|
49
|
+
# - Converting tabs and newlines to spaces
|
|
50
|
+
# - Collapsing multiple spaces to single space
|
|
51
|
+
# - Trimming leading and trailing whitespace
|
|
52
|
+
#
|
|
53
|
+
# @param text [String] Text to normalize
|
|
54
|
+
# @return [String] Normalized text
|
|
55
|
+
def self.normalize_whitespace(text)
|
|
56
|
+
return "" if text.nil? || text.empty?
|
|
57
|
+
|
|
58
|
+
text.gsub(/[\t\n\r]+/, " ")
|
|
59
|
+
.gsub(/\s+/, " ")
|
|
60
|
+
.strip
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Splits a string by whitespace into an array
|
|
64
|
+
#
|
|
65
|
+
# @param text [String] Text to split
|
|
66
|
+
# @return [Array<String>] Array of tokens
|
|
67
|
+
def self.split_whitespace(text)
|
|
68
|
+
return [] if text.nil? || text.empty?
|
|
69
|
+
|
|
70
|
+
normalize_whitespace(text).split
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Xseed
|
|
6
|
+
module Models
|
|
7
|
+
# Represents an XSD element declaration
|
|
8
|
+
#
|
|
9
|
+
# This model captures all properties of an xs:element declaration including
|
|
10
|
+
# name, type, occurrence constraints, and documentation.
|
|
11
|
+
#
|
|
12
|
+
# @example Creating an element
|
|
13
|
+
# element = ElementDeclaration.new(
|
|
14
|
+
# name: "Person",
|
|
15
|
+
# type: "PersonType",
|
|
16
|
+
# min_occurs: 0,
|
|
17
|
+
# max_occurs: "unbounded"
|
|
18
|
+
# )
|
|
19
|
+
#
|
|
20
|
+
class ElementDeclaration < Lutaml::Model::Serializable
|
|
21
|
+
attribute :name, :string
|
|
22
|
+
attribute :type, :string
|
|
23
|
+
attribute :min_occurs, :integer, default: -> { 1 }
|
|
24
|
+
attribute :max_occurs, :string, default: -> { "1" }
|
|
25
|
+
attribute :nillable, Lutaml::Model::Type::Boolean, default: -> { false }
|
|
26
|
+
attribute :abstract, Lutaml::Model::Type::Boolean, default: -> { false }
|
|
27
|
+
attribute :substitution_group, :string
|
|
28
|
+
attribute :documentation, :string
|
|
29
|
+
attribute :default_value, :string
|
|
30
|
+
attribute :fixed_value, :string
|
|
31
|
+
|
|
32
|
+
xml do
|
|
33
|
+
root "element"
|
|
34
|
+
namespace "http://www.w3.org/2001/XMLSchema", "xs"
|
|
35
|
+
|
|
36
|
+
map_attribute "name", to: :name
|
|
37
|
+
map_attribute "type", to: :type
|
|
38
|
+
map_attribute "minOccurs", to: :min_occurs
|
|
39
|
+
map_attribute "maxOccurs", to: :max_occurs
|
|
40
|
+
map_attribute "nillable", to: :nillable
|
|
41
|
+
map_attribute "abstract", to: :abstract
|
|
42
|
+
map_attribute "substitutionGroup", to: :substitution_group
|
|
43
|
+
map_attribute "default", to: :default_value
|
|
44
|
+
map_attribute "fixed", to: :fixed_value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
yaml do
|
|
48
|
+
map "name", to: :name
|
|
49
|
+
map "type", to: :type
|
|
50
|
+
map "min_occurs", to: :min_occurs
|
|
51
|
+
map "max_occurs", to: :max_occurs
|
|
52
|
+
map "nillable", to: :nillable
|
|
53
|
+
map "abstract", to: :abstract
|
|
54
|
+
map "substitution_group", to: :substitution_group
|
|
55
|
+
map "documentation", to: :documentation
|
|
56
|
+
map "default_value", to: :default_value
|
|
57
|
+
map "fixed_value", to: :fixed_value
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
json do
|
|
61
|
+
map "name", to: :name
|
|
62
|
+
map "type", to: :type
|
|
63
|
+
map "minOccurs", to: :min_occurs
|
|
64
|
+
map "maxOccurs", to: :max_occurs
|
|
65
|
+
map "nillable", to: :nillable
|
|
66
|
+
map "abstract", to: :abstract
|
|
67
|
+
map "substitutionGroup", to: :substitution_group
|
|
68
|
+
map "documentation", to: :documentation
|
|
69
|
+
map "defaultValue", to: :default_value
|
|
70
|
+
map "fixedValue", to: :fixed_value
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Parse an element declaration from an XSD Nokogiri node
|
|
74
|
+
#
|
|
75
|
+
# @param node [Nokogiri::XML::Element] The xs:element node
|
|
76
|
+
# @return [ElementDeclaration] The parsed element declaration
|
|
77
|
+
def self.from_xsd_node(node)
|
|
78
|
+
new(
|
|
79
|
+
name: node["name"],
|
|
80
|
+
type: node["type"],
|
|
81
|
+
min_occurs: parse_occurs(node["minOccurs"], 1),
|
|
82
|
+
max_occurs: node["maxOccurs"] || "1",
|
|
83
|
+
nillable: parse_boolean(node["nillable"]),
|
|
84
|
+
abstract: parse_boolean(node["abstract"]),
|
|
85
|
+
substitution_group: node["substitutionGroup"],
|
|
86
|
+
default_value: node["default"],
|
|
87
|
+
fixed_value: node["fixed"],
|
|
88
|
+
documentation: extract_documentation(node),
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Check if the element is optional (minOccurs = 0)
|
|
93
|
+
#
|
|
94
|
+
# @return [Boolean] true if the element is optional
|
|
95
|
+
def optional?
|
|
96
|
+
min_occurs.zero?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Check if the element is unbounded (maxOccurs = "unbounded")
|
|
100
|
+
#
|
|
101
|
+
# @return [Boolean] true if the element is unbounded
|
|
102
|
+
def unbounded?
|
|
103
|
+
max_occurs == "unbounded"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Check if the element has documentation
|
|
107
|
+
#
|
|
108
|
+
# @return [Boolean] true if documentation is present
|
|
109
|
+
def documented?
|
|
110
|
+
!documentation.nil? && !documentation.empty?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Get occurrence string representation
|
|
114
|
+
#
|
|
115
|
+
# @return [String] Occurrence representation like "[0..1]" or "[1..∞]"
|
|
116
|
+
def occurrence_string
|
|
117
|
+
return "" if min_occurs == 1 && max_occurs == "1"
|
|
118
|
+
|
|
119
|
+
max_display = max_occurs == "unbounded" ? "∞" : max_occurs
|
|
120
|
+
"[#{min_occurs}..#{max_display}]"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private_class_method def self.parse_occurs(value, default)
|
|
124
|
+
return default if value.nil? || value.empty?
|
|
125
|
+
|
|
126
|
+
value.to_i
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private_class_method def self.parse_boolean(value)
|
|
130
|
+
value == "true"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private_class_method def self.extract_documentation(node)
|
|
134
|
+
doc_node = node.at_xpath(
|
|
135
|
+
"xs:annotation/xs:documentation",
|
|
136
|
+
"xs" => "http://www.w3.org/2001/XMLSchema",
|
|
137
|
+
)
|
|
138
|
+
return nil unless doc_node
|
|
139
|
+
|
|
140
|
+
doc_node.text.strip
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "nokogiri"
|
|
4
|
+
|
|
5
|
+
module Xseed
|
|
6
|
+
module Parser
|
|
7
|
+
# XSD Parser for extracting schema components and metadata
|
|
8
|
+
#
|
|
9
|
+
# This class parses XML Schema Definition (XSD) files using Nokogiri
|
|
10
|
+
# and provides methods to access schema components such as elements,
|
|
11
|
+
# types, groups, and documentation.
|
|
12
|
+
#
|
|
13
|
+
# @example Parse an XSD file
|
|
14
|
+
# parser = Xseed::Parser::XsdParser.new("schema.xsd")
|
|
15
|
+
# elements = parser.elements
|
|
16
|
+
# types = parser.types
|
|
17
|
+
#
|
|
18
|
+
class XsdParser
|
|
19
|
+
attr_reader :document, :schema, :target_namespace
|
|
20
|
+
|
|
21
|
+
XSD_NS = "http://www.w3.org/2001/XMLSchema"
|
|
22
|
+
|
|
23
|
+
# Initialize the parser with an XSD file path
|
|
24
|
+
#
|
|
25
|
+
# @param xsd_file_path [String] Path to the XSD file
|
|
26
|
+
# @raise [Errno::ENOENT] if file does not exist
|
|
27
|
+
def initialize(xsd_file_path)
|
|
28
|
+
content = File.read(xsd_file_path)
|
|
29
|
+
@document = Nokogiri::XML(content, &:strict)
|
|
30
|
+
@schema = @document.root
|
|
31
|
+
|
|
32
|
+
raise ParserError, "Invalid XSD: No root element found" unless @schema
|
|
33
|
+
|
|
34
|
+
parse_schema
|
|
35
|
+
rescue Nokogiri::XML::SyntaxError => e
|
|
36
|
+
raise ParserError, "Invalid XML syntax: #{e.message}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Get the elementFormDefault attribute value
|
|
40
|
+
#
|
|
41
|
+
# @return [String, nil] The elementFormDefault value
|
|
42
|
+
def element_form_default
|
|
43
|
+
schema["elementFormDefault"]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get the schema version attribute
|
|
47
|
+
#
|
|
48
|
+
# @return [String, nil] The schema version if present
|
|
49
|
+
def schema_version
|
|
50
|
+
schema["version"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get all global element definitions
|
|
54
|
+
#
|
|
55
|
+
# @return [Array<Nokogiri::XML::Element>] Array of element nodes
|
|
56
|
+
def elements
|
|
57
|
+
xpath("//xs:schema/xs:element")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get all complex type definitions
|
|
61
|
+
#
|
|
62
|
+
# @return [Array<Nokogiri::XML::Element>] Array of complexType nodes
|
|
63
|
+
def complex_types
|
|
64
|
+
xpath("//xs:schema/xs:complexType")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Get all simple type definitions
|
|
68
|
+
#
|
|
69
|
+
# @return [Array<Nokogiri::XML::Element>] Array of simpleType nodes
|
|
70
|
+
def simple_types
|
|
71
|
+
xpath("//xs:schema/xs:simpleType")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Get all type definitions (both complex and simple)
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<Nokogiri::XML::Element>] Array of type nodes
|
|
77
|
+
def types
|
|
78
|
+
complex_types + simple_types
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get all group definitions
|
|
82
|
+
#
|
|
83
|
+
# @return [Array<Nokogiri::XML::Element>] Array of group nodes
|
|
84
|
+
def groups
|
|
85
|
+
xpath("//xs:schema/xs:group")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Get all attribute group definitions
|
|
89
|
+
#
|
|
90
|
+
# @return [Array<Nokogiri::XML::Element>] Array of attributeGroup nodes
|
|
91
|
+
def attribute_groups
|
|
92
|
+
xpath("//xs:schema/xs:attributeGroup")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Get namespace mappings from the schema
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash<String, String>] Hash mapping prefixes to namespace URIs
|
|
98
|
+
def namespaces
|
|
99
|
+
schema.namespaces.transform_keys do |key|
|
|
100
|
+
key.sub(/^xmlns:/, "")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Get schema-level documentation
|
|
105
|
+
#
|
|
106
|
+
# @return [String, nil] The documentation text or nil
|
|
107
|
+
def documentation
|
|
108
|
+
doc_node = schema.at_xpath(
|
|
109
|
+
"xs:annotation/xs:documentation",
|
|
110
|
+
"xs" => XSD_NS,
|
|
111
|
+
)
|
|
112
|
+
return nil unless doc_node
|
|
113
|
+
|
|
114
|
+
doc_node.text.strip
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Get documentation for a specific element by name
|
|
118
|
+
#
|
|
119
|
+
# @param element_name [String] The name of the element
|
|
120
|
+
# @return [String, nil] The documentation text or nil
|
|
121
|
+
def element_documentation(element_name)
|
|
122
|
+
element = schema.at_xpath(
|
|
123
|
+
"xs:element[@name='#{element_name}']",
|
|
124
|
+
"xs" => XSD_NS,
|
|
125
|
+
)
|
|
126
|
+
return nil unless element
|
|
127
|
+
|
|
128
|
+
extract_documentation(element)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get documentation for a specific type by name
|
|
132
|
+
#
|
|
133
|
+
# @param type_name [String] The name of the type
|
|
134
|
+
# @return [String, nil] The documentation text or nil
|
|
135
|
+
def type_documentation(type_name)
|
|
136
|
+
type_node = schema.at_xpath(
|
|
137
|
+
"xs:complexType[@name='#{type_name}'] | " \
|
|
138
|
+
"xs:simpleType[@name='#{type_name}']",
|
|
139
|
+
"xs" => XSD_NS,
|
|
140
|
+
)
|
|
141
|
+
return nil unless type_node
|
|
142
|
+
|
|
143
|
+
extract_documentation(type_node)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Get all import declarations
|
|
147
|
+
#
|
|
148
|
+
# @return [Array<Nokogiri::XML::Element>] Array of import nodes
|
|
149
|
+
def imports
|
|
150
|
+
xpath("//xs:schema/xs:import")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Get all include declarations
|
|
154
|
+
#
|
|
155
|
+
# @return [Array<Nokogiri::XML::Element>] Array of include nodes
|
|
156
|
+
def includes
|
|
157
|
+
xpath("//xs:schema/xs:include")
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
# Parse the schema and extract metadata
|
|
163
|
+
def parse_schema
|
|
164
|
+
return unless schema
|
|
165
|
+
|
|
166
|
+
@target_namespace = schema["targetNamespace"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Execute XPath query with XSD namespace
|
|
170
|
+
#
|
|
171
|
+
# @param xpath_expr [String] XPath expression
|
|
172
|
+
# @return [Array<Nokogiri::XML::Element>] Matching nodes
|
|
173
|
+
def xpath(xpath_expr)
|
|
174
|
+
document.xpath(xpath_expr, "xs" => XSD_NS)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Extract documentation from an XML node
|
|
178
|
+
#
|
|
179
|
+
# @param node [Nokogiri::XML::Element] The node to extract from
|
|
180
|
+
# @return [String, nil] The documentation text or nil
|
|
181
|
+
def extract_documentation(node)
|
|
182
|
+
doc_node = node.at_xpath(
|
|
183
|
+
"xs:annotation/xs:documentation",
|
|
184
|
+
"xs" => XSD_NS,
|
|
185
|
+
)
|
|
186
|
+
return nil unless doc_node
|
|
187
|
+
|
|
188
|
+
doc_node.text.strip
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
data/lib/xseed.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "xseed/version"
|
|
4
|
+
|
|
5
|
+
module Xseed
|
|
6
|
+
class Error < StandardError; end
|
|
7
|
+
class ParserError < Error; end
|
|
8
|
+
class GenerationError < Error; end
|
|
9
|
+
|
|
10
|
+
autoload :CLI, "xseed/cli"
|
|
11
|
+
|
|
12
|
+
module Parser
|
|
13
|
+
autoload :XsdParser, "xseed/parser/xsd_parser"
|
|
14
|
+
autoload :SchemaParser, "xseed/parser/schema_parser"
|
|
15
|
+
autoload :NamespaceResolver, "xseed/parser/namespace_resolver"
|
|
16
|
+
autoload :AnnotationExtractor, "xseed/parser/annotation_extractor"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module Models
|
|
20
|
+
autoload :Schema, "xseed/models/schema"
|
|
21
|
+
autoload :Element, "xseed/models/element"
|
|
22
|
+
autoload :ComplexType, "xseed/models/complex_type"
|
|
23
|
+
autoload :SimpleType, "xseed/models/simple_type"
|
|
24
|
+
autoload :Attribute, "xseed/models/attribute"
|
|
25
|
+
autoload :Group, "xseed/models/group"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module Documentation
|
|
29
|
+
autoload :Generator, "xseed/documentation/generator"
|
|
30
|
+
autoload :HtmlGenerator, "xseed/documentation/html_generator"
|
|
31
|
+
autoload :Config, "xseed/documentation/config"
|
|
32
|
+
|
|
33
|
+
module Core
|
|
34
|
+
autoload :Parameters, "xseed/documentation/core/parameters"
|
|
35
|
+
autoload :Constants, "xseed/documentation/core/constants"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module Utilities
|
|
39
|
+
autoload :StringHelpers, "xseed/documentation/utilities/string_helpers"
|
|
40
|
+
autoload :NamespaceHelpers,
|
|
41
|
+
"xseed/documentation/utilities/namespace_helpers"
|
|
42
|
+
autoload :ReferenceResolver,
|
|
43
|
+
"xseed/documentation/utilities/reference_resolver"
|
|
44
|
+
autoload :SchemaLocationHelper,
|
|
45
|
+
"xseed/documentation/utilities/schema_location_helper"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module Renderers
|
|
49
|
+
autoload :XMLPrettyPrinter,
|
|
50
|
+
"xseed/documentation/renderers/xml_pretty_printer"
|
|
51
|
+
autoload :GlossaryRenderer,
|
|
52
|
+
"xseed/documentation/renderers/glossary_renderer"
|
|
53
|
+
autoload :UIComponentRenderer,
|
|
54
|
+
"xseed/documentation/renderers/ui_component_renderer"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
module Sections
|
|
58
|
+
autoload :ComponentLinks,
|
|
59
|
+
"xseed/documentation/sections/component_links"
|
|
60
|
+
autoload :HierarchyTables,
|
|
61
|
+
"xseed/documentation/sections/hierarchy_tables"
|
|
62
|
+
autoload :PropertiesTables,
|
|
63
|
+
"xseed/documentation/sections/properties_tables"
|
|
64
|
+
autoload :SchemaComponents,
|
|
65
|
+
"xseed/documentation/sections/schema_components"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
module Presentation
|
|
69
|
+
autoload :HTMLDocument,
|
|
70
|
+
"xseed/documentation/presentation/html_document"
|
|
71
|
+
autoload :Navigation, "xseed/documentation/presentation/navigation"
|
|
72
|
+
autoload :CSSStyles, "xseed/documentation/presentation/css_styles"
|
|
73
|
+
autoload :JavaScript, "xseed/documentation/presentation/javascript"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/xseed.gemspec
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/xseed/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "xseed"
|
|
7
|
+
spec.version = Xseed::VERSION
|
|
8
|
+
spec.authors = ["Ribose"]
|
|
9
|
+
spec.email = ["open.source@ribose.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby XSD documentation generator with unified SVG and HTML output"
|
|
12
|
+
spec.description = "Generate comprehensive XSD documentation with SVG diagrams (via xsdvi gem) and HTML reference (XS3P port)"
|
|
13
|
+
spec.homepage = "https://github.com/metanorma/xseed"
|
|
14
|
+
spec.license = "BSD-2-Clause"
|
|
15
|
+
|
|
16
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
17
|
+
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
20
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.adoc"
|
|
21
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
22
|
+
|
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
25
|
+
f.match(%r{\A(?:test|spec|features)/})
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
# Runtime dependencies
|
|
34
|
+
spec.add_dependency "lutaml-model", "~> 0.7"
|
|
35
|
+
spec.add_dependency "lutaml-xsd", "~> 1.0"
|
|
36
|
+
spec.add_dependency "nokogiri", "~> 1.16"
|
|
37
|
+
spec.add_dependency "thor", "~> 1.3"
|
|
38
|
+
spec.add_dependency "xsdvi", "~> 1.0"
|
|
39
|
+
end
|