Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2025-03-07 11:40:20 UTC using RuboCop version 1.73.2.
# on 2025-11-14 15:24:37 UTC using RuboCop version 1.81.7.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemfile, **/Gemfile, **/gems.rb
Bundler/OrderedGems:
Exclude:
- 'Gemfile'

# Offense count: 2
# Offense count: 10
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
# AllowedMethods: refine
Metrics/BlockLength:
Max: 30
Max: 108
2 changes: 0 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ source "https://rubygems.org"
gemspec

gem "base64"
gem "canon"
gem "equivalent-xml"
gem "liquid"
gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
Expand All @@ -15,4 +14,3 @@ gem "oga"
gem "rake"
gem "rspec"
gem "rubocop"
gem "xml-c14n"
72 changes: 66 additions & 6 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,8 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`

- `used_by` — Returns complex_types that use/reference this element.
- `attributes` — Returns attribute elements.
- `min_occurrences` — Returns the element's minimum occurrences as Integer (default 1).
- `max_occurrences` — Returns `*` for unbounded, otherwise Integer (default 1).
- `child_elements` — Returns all nested child elements (resolved across groups/choices/sequences).
- `referenced_name` — Returns the name of the element or its referenced element by `ref`.
- `referenced_type` — Returns the name of the element's `type` if present, or the type name indicated by `ref` if defined.
- `referenced_object` — Resolves and returns the element or its referenced element by `ref`.
- `referenced_complex_type` — Resolves the complex type matching `referenced_type`.
Expand Down Expand Up @@ -165,6 +164,7 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`

- `used_by` — Returns complex types that use this attribute group.
- `attribute_elements` — Returns a flattened list of attributes, resolving nested groups.
- `referenced_object` — Resolves and returns the attribute group or its referenced group by `ref`.

[source,liquid]
----
Expand All @@ -184,8 +184,10 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`

- `used_by` — Returns elements and group elements that reference this type.
- `attribute_elements` — Returns all attributes (including those from groups and simple content extensions).
- `direct_child_elements` — Returns direct child elements, excluding attributes, attribute groups, annotations, and any attributes.
- `child_elements` — Returns all child elements (across sequences/choices/groups).
- `find_elements_used(name)` — Boolean indicating if an element name is used within this type.
- `find_elements_used(element_name)` — Boolean indicating if an element name is used within this type.
- `find_used_by(object)` — Boolean indicating if this complex type is used by the given object.

[source,liquid]
----
Expand All @@ -205,7 +207,7 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`
=== Group (`Lutaml::Xsd::LiquidMethods::Group`) and Sequence (`...::Sequence`) and Choice (`...::Choice`)

- `child_elements` — Returns all nested `Element` instances contained within.
- `find_elements_used(name)` — Boolean indicating if an element name is used within this container.
- `find_elements_used(element_name)` — Boolean indicating if an element name is used within this container.
- All three include the same helper surface and resolve nested structures consistently.

[source,liquid]
Expand All @@ -221,6 +223,7 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`
=== SimpleContent (`Lutaml::Xsd::LiquidMethods::SimpleContent`)

- `attribute_elements` — Returns attributes coming from the `extension` and nested attribute groups.
- `base_type` — Returns the base type from `base`, `extension.base`, or `restriction.base`.

[source,liquid]
----
Expand All @@ -241,9 +244,66 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid`
{% endfor %}
----

=== ResolvedElementOrder (`Lutaml::Xsd::LiquidMethods::ResolvedElementOrder`)
=== Schema Class Methods (`Lutaml::Xsd::Schema`)

- `resolved_element_order` — A helper method that returns all elements in the order they originally appeared in the XSD.
The `Schema` class exposes several helper methods in Liquid templates for easier access and navigation:

- `elements_sorted_by_name` — Returns all `element` objects in the schema sorted by their `name` attribute.
- `complex_types_sorted_by_name` — Returns all `complex_type` objects in the schema sorted by their `name` attribute.
- `attribute_groups_sorted_by_name` — Returns all `attribute_group` objects in the schema sorted by their `name` attribute.

These methods provide a predictable, alphabetical traversal of major parts of the XSD. Example usage:

[source,liquid]
----
{% for el in schema.elements_sorted_by_name %}
- {{ el.name }}
{% endfor %}

{% for ct in schema.complex_types_sorted_by_name %}
- {{ ct.name }}
{% endfor %}

{% for ag in schema.attribute_groups_sorted_by_name %}
- {{ ag.name }}
{% endfor %}
----

=== Base Class Methods (`Lutaml::Xsd::Base`)

The following methods are available on all XSD objects via the base class:

Type checking methods:
- `any?` — Returns `true` if the object is an `Any` instance.
- `all?` — Returns `true` if the object is an `All` instance.
- `choice?` — Returns `true` if the object is a `Choice` instance.
- `element?` — Returns `true` if the object is an `Element` instance.
- `sequence?` — Returns `true` if the object is a `Sequence` instance.
- `attribute?` — Returns `true` if the object is an `Attribute` instance.
- `annotation?` — Returns `true` if the object is an `Annotation` instance.
- `simple_content?` — Returns `true` if the object is a `SimpleContent` instance.
- `attribute_group?` — Returns `true` if the object is an `AttributeGroup` instance.

Other methods:
- `min_occurrences` — Returns the minimum occurrences as Integer (default 1). Only available on objects that respond to `min_occurs`.
- `max_occurrences` — Returns `*` for unbounded, otherwise Integer (default 1). Only available on objects that respond to `max_occurs`.
- `to_xml` — Returns the XML representation of the object.
- `to_formatted_xml` — Returns a formatted XML representation of the object.
- `resolved_element_order` — Returns all elements in the order they originally appeared in the XSD.

[source,liquid]
----
{% for item in schema.element %}
{% if item.element? %}
Element: {{ item.name }}
{% if item.min_occurrences %}
Min: {{ item.min_occurrences }}, Max: {{ item.max_occurrences }}
{% endif %}
{% endif %}
{% endfor %}
----

NOTE: The `resolved_element_order` method is available on all objects via the base class (`Lutaml::Xsd::Base`) and returns all elements in the order they originally appeared in the XSD. This method is used internally by many of the liquid methods above.

== Development

Expand Down
62 changes: 61 additions & 1 deletion lib/lutaml/xsd/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,76 @@ def to_formatted_xml(except: [])
end

def resolved_element_order
element_order.each_with_object(element_order.dup) do |element, array|
element_order&.each_with_object(element_order.dup) do |element, array|
next delete_deletables(array, element) if deletable?(element)

update_element_array(array, element)
end
end

def sequence?
is_a?(Sequence)
end

def any?
is_a?(Any)
end

def all?
is_a?(All)
end

def choice?
is_a?(Choice)
end

def annotation?
is_a?(Annotation)
end

def attribute?
is_a?(Attribute)
end

def attribute_group?
is_a?(AttributeGroup)
end

def simple_content?
is_a?(SimpleContent)
end

def element?
is_a?(Element)
end

def min_occurrences
return unless respond_to?(:min_occurs)

@min_occurs&.to_i || 1
end

def max_occurrences
return unless respond_to?(:max_occurs)
return "*" if @max_occurs == "unbounded"

@max_occurs&.to_i || 1
end

liquid do
map "to_xml", to: :to_xml
map "any?", to: :any?
map "all?", to: :all?
map "choice?", to: :choice?
map "element?", to: :element?
map "sequence?", to: :sequence?
map "attribute?", to: :attribute?
map "annotation?", to: :annotation?
map "min_occurrences", to: :min_occurrences
map "max_occurrences", to: :max_occurrences
map "to_formatted_xml", to: :to_formatted_xml
map "simple_content?", to: :simple_content?
map "attribute_group?", to: :attribute_group?
map "resolved_element_order", to: :resolved_element_order
end

Expand Down
1 change: 1 addition & 0 deletions lib/lutaml/xsd/complex_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ComplexType < Base
map "used_by", to: :used_by
map "child_elements", to: :child_elements
map "attribute_elements", to: :attribute_elements
map "direct_child_elements", to: :direct_child_elements
end

Lutaml::Xsd.register_model(self, :complex_type)
Expand Down
4 changes: 2 additions & 2 deletions lib/lutaml/xsd/liquid_methods/attribute_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def attribute_elements(array = [])
end

def find_used_by(object)
object.resolved_element_order.any? do |child|
object&.resolved_element_order&.any? do |child|
if child.is_a?(Xsd::AttributeGroup)
child.ref == name
else
Expand All @@ -30,7 +30,7 @@ def find_used_by(object)
end

def referenced_object
return self unless name
return self if name

@__root.attribute_group.find { |group| group.name == ref }
end
Expand Down
18 changes: 17 additions & 1 deletion lib/lutaml/xsd/liquid_methods/complex_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ module Lutaml
module Xsd
module LiquidMethods
module ComplexType
DIRECT_CHILD_ELEMENTS_EXCEPTION = %w[
AttributeGroup
AnyAttribute
Annotation
Attribute
].freeze

def used_by
root_complex_types = @__root.complex_type.reject { |ct| ct == self }
raw_elements = @__root.group.map(&:child_elements).flatten
Expand All @@ -14,11 +21,20 @@ def used_by

def attribute_elements(array = [])
array.concat(attribute)
attribute_group.flat_map { |group| group.attribute_elements(array) }
attribute_group.each { |group| group.attribute_elements(array) }
simple_content&.attribute_elements(array)
array
end

def direct_child_elements(array = [], except: DIRECT_CHILD_ELEMENTS_EXCEPTION)
resolved_element_order.each do |child|
next if except.any? { |klass| child.class.name.include?("::#{klass}") }

array << child
end
array
end

def child_elements(array = [])
resolved_element_order.each do |child|
if child.is_a?(Xsd::Element)
Expand Down
12 changes: 1 addition & 11 deletions lib/lutaml/xsd/liquid_methods/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@ def used_by
end

def attributes
referenced_complex_type.attribute_elements
end

def min_occurrences
@min_occurs&.to_i || 1
end

def max_occurrences
return "*" if @max_occurs == "unbounded"

@max_occurs&.to_i || 1
referenced_complex_type&.attribute_elements
end

def child_elements(array = [])
Expand Down
8 changes: 7 additions & 1 deletion lib/lutaml/xsd/liquid_methods/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Xsd
module LiquidMethods
module Group
def child_elements(array = [])
resolved_element_order.each do |child|
referenced_object.resolved_element_order.each do |child|
if child.is_a?(Xsd::Element)
array << child
elsif child.respond_to?(:child_elements)
Expand All @@ -24,6 +24,12 @@ def find_elements_used(element_name)
end
end
end

def referenced_object
return self if name

@__root.group.find { |group| group.name == ref }
end
end
end
end
Expand Down
21 changes: 21 additions & 0 deletions lib/lutaml/xsd/liquid_methods/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Lutaml
module Xsd
module LiquidMethods
module Schema
def elements_sorted_by_name
element.sort_by(&:name)
end

def complex_types_sorted_by_name
complex_type.sort_by(&:name)
end

def attribute_groups_sorted_by_name
attribute_group.sort_by(&:name)
end
end
end
end
end
6 changes: 6 additions & 0 deletions lib/lutaml/xsd/liquid_methods/simple_content.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ module SimpleContent
def attribute_elements(array = [])
extension.attribute_elements(array)
end

def base_type
base ||
extension&.base ||
restriction&.base
end
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions lib/lutaml/xsd/schema.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# frozen_string_literal: true

require_relative "liquid_methods/schema"

module Lutaml
module Xsd
# rubocop:disable Metrics/ClassLength
class Schema < Base
include LiquidMethods::Schema

attribute :id, :string
attribute :lang, :string
attribute :xmlns, :string
Expand Down Expand Up @@ -56,6 +60,12 @@ class Schema < Base
map_attribute :lang, to: :lang
end

liquid do
map "elements_sorted_by_name", to: :elements_sorted_by_name
map "complex_types_sorted_by_name", to: :complex_types_sorted_by_name
map "attribute_groups_sorted_by_name", to: :attribute_groups_sorted_by_name
end

def import_from_schema(model, value)
value.each do |schema|
setup_import_and_include(
Expand Down
Loading
Loading