From bde2a1c5b162895df7e1a916d1dc0335de2075a8 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Mon, 10 Nov 2025 23:05:05 +0500 Subject: [PATCH 01/11] added drop identifier methods --- lib/lutaml/xsd/base.rb | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/lutaml/xsd/base.rb b/lib/lutaml/xsd/base.rb index e20c09a..23348da 100644 --- a/lib/lutaml/xsd/base.rb +++ b/lib/lutaml/xsd/base.rb @@ -22,8 +22,33 @@ def resolved_element_order end end + def is_sequence? + is_a?(Sequence) + end + + def is_any? + is_a?(Any) + end + + def is_all? + is_a?(All) + end + + def is_choice? + is_a?(Choice) + end + + def is_annotation? + is_a?(Annotation) + end + liquid do map "to_xml", to: :to_xml + map "is_any?", to: :is_any? + map "is_all?", to: :is_all? + map "is_choice?", to: :is_choice? + map "is_sequence?", to: :is_sequence? + map "is_annotation?", to: :is_annotation? map "to_formatted_xml", to: :to_formatted_xml map "resolved_element_order", to: :resolved_element_order end From 5283e68d64b168e80a34071107e5916f791dd830 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Tue, 11 Nov 2025 21:54:32 +0500 Subject: [PATCH 02/11] Updated liquid methods --- lib/lutaml/xsd/base.rb | 25 +++++++++++++++++++ lib/lutaml/xsd/complex_type.rb | 1 + lib/lutaml/xsd/liquid_methods/complex_type.rb | 15 +++++++++++ lib/lutaml/xsd/liquid_methods/element.rb | 10 -------- lib/lutaml/xsd/liquid_methods/sequence.rb | 4 +-- 5 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/lutaml/xsd/base.rb b/lib/lutaml/xsd/base.rb index 23348da..ab44d0a 100644 --- a/lib/lutaml/xsd/base.rb +++ b/lib/lutaml/xsd/base.rb @@ -42,14 +42,39 @@ def is_annotation? is_a?(Annotation) end + def is_attribute? + is_a?(Attribute) + end + + def is_attribute_group? + is_a?(AttributeGroup) + 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 "is_any?", to: :is_any? map "is_all?", to: :is_all? map "is_choice?", to: :is_choice? map "is_sequence?", to: :is_sequence? + map "is_attribute?", to: :is_attribute? map "is_annotation?", to: :is_annotation? + map "min_occurrences", to: :min_occurrences + map "max_occurrences", to: :max_occurrences map "to_formatted_xml", to: :to_formatted_xml + map "is_attribute_group?", to: :is_attribute_group? map "resolved_element_order", to: :resolved_element_order end diff --git a/lib/lutaml/xsd/complex_type.rb b/lib/lutaml/xsd/complex_type.rb index 9c63442..3658264 100644 --- a/lib/lutaml/xsd/complex_type.rb +++ b/lib/lutaml/xsd/complex_type.rb @@ -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) diff --git a/lib/lutaml/xsd/liquid_methods/complex_type.rb b/lib/lutaml/xsd/liquid_methods/complex_type.rb index e4c82dd..6b26f29 100644 --- a/lib/lutaml/xsd/liquid_methods/complex_type.rb +++ b/lib/lutaml/xsd/liquid_methods/complex_type.rb @@ -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 @@ -19,6 +26,14 @@ def attribute_elements(array = []) array end + def direct_child_elements(array = [], except: DIRECT_CHILD_ELEMENTS_EXCEPTION) + resolved_element_order.each_with_object(array) do |child| + next if except.any? { |klass| child.class.name.include?("::#{klass}") } + + array << child + end + end + def child_elements(array = []) resolved_element_order.each do |child| if child.is_a?(Xsd::Element) diff --git a/lib/lutaml/xsd/liquid_methods/element.rb b/lib/lutaml/xsd/liquid_methods/element.rb index 1b7a9fe..4c6967b 100644 --- a/lib/lutaml/xsd/liquid_methods/element.rb +++ b/lib/lutaml/xsd/liquid_methods/element.rb @@ -12,16 +12,6 @@ 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 - end - def child_elements(array = []) referenced_complex_type&.child_elements(array) end diff --git a/lib/lutaml/xsd/liquid_methods/sequence.rb b/lib/lutaml/xsd/liquid_methods/sequence.rb index dbe8ab9..930e2ab 100644 --- a/lib/lutaml/xsd/liquid_methods/sequence.rb +++ b/lib/lutaml/xsd/liquid_methods/sequence.rb @@ -6,7 +6,7 @@ module LiquidMethods module Sequence def child_elements(array = []) resolved_element_order.each do |child| - if child.is_a?(Xsd::Element) + if child.is_a?(Element) array << child elsif child.respond_to?(:child_elements) child.child_elements(array) @@ -17,7 +17,7 @@ def child_elements(array = []) def find_elements_used(element_name) resolved_element_order.any? do |child| - if child.is_a?(Xsd::Element) + if child.is_a?(Element) child.ref == element_name elsif child.respond_to?(:find_elements_used) child.find_elements_used(element_name) From d4ce2389daa873b5a0fef488c746c559f992b22e Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Thu, 13 Nov 2025 12:47:22 +0500 Subject: [PATCH 03/11] Updated and added new liquid methods --- lib/lutaml/xsd/base.rb | 10 ++++++++++ lib/lutaml/xsd/liquid_methods/attribute_group.rb | 2 +- lib/lutaml/xsd/liquid_methods/complex_type.rb | 2 +- lib/lutaml/xsd/liquid_methods/simple_content.rb | 6 ++++++ lib/lutaml/xsd/simple_content.rb | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/lutaml/xsd/base.rb b/lib/lutaml/xsd/base.rb index ab44d0a..056c15d 100644 --- a/lib/lutaml/xsd/base.rb +++ b/lib/lutaml/xsd/base.rb @@ -50,6 +50,14 @@ def is_attribute_group? is_a?(AttributeGroup) end + def is_simple_content? + is_a?(SimpleContent) + end + + def is_element? + is_a?(Element) + end + def min_occurrences return unless respond_to?(:min_occurs) @@ -68,12 +76,14 @@ def max_occurrences map "is_any?", to: :is_any? map "is_all?", to: :is_all? map "is_choice?", to: :is_choice? + map "is_element?", to: :is_element? map "is_sequence?", to: :is_sequence? map "is_attribute?", to: :is_attribute? map "is_annotation?", to: :is_annotation? map "min_occurrences", to: :min_occurrences map "max_occurrences", to: :max_occurrences map "to_formatted_xml", to: :to_formatted_xml + map "is_simple_content?", to: :is_simple_content? map "is_attribute_group?", to: :is_attribute_group? map "resolved_element_order", to: :resolved_element_order end diff --git a/lib/lutaml/xsd/liquid_methods/attribute_group.rb b/lib/lutaml/xsd/liquid_methods/attribute_group.rb index 9eec988..bad88b8 100644 --- a/lib/lutaml/xsd/liquid_methods/attribute_group.rb +++ b/lib/lutaml/xsd/liquid_methods/attribute_group.rb @@ -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 diff --git a/lib/lutaml/xsd/liquid_methods/complex_type.rb b/lib/lutaml/xsd/liquid_methods/complex_type.rb index 6b26f29..1a27a21 100644 --- a/lib/lutaml/xsd/liquid_methods/complex_type.rb +++ b/lib/lutaml/xsd/liquid_methods/complex_type.rb @@ -21,7 +21,7 @@ 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 diff --git a/lib/lutaml/xsd/liquid_methods/simple_content.rb b/lib/lutaml/xsd/liquid_methods/simple_content.rb index 449481e..d1dc860 100644 --- a/lib/lutaml/xsd/liquid_methods/simple_content.rb +++ b/lib/lutaml/xsd/liquid_methods/simple_content.rb @@ -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 diff --git a/lib/lutaml/xsd/simple_content.rb b/lib/lutaml/xsd/simple_content.rb index b10d720..67eb2e2 100644 --- a/lib/lutaml/xsd/simple_content.rb +++ b/lib/lutaml/xsd/simple_content.rb @@ -25,6 +25,7 @@ class SimpleContent < Base end liquid do + map "base_type", to: :base_type map "attribute_elements", to: :attribute_elements end From c7e07744468349295fdcbd1811501dd764452e7a Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Thu, 13 Nov 2025 22:22:07 +0500 Subject: [PATCH 04/11] Updated readme --- README.adoc | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/README.adoc b/README.adoc index 4607d5c..529b128 100644 --- a/README.adoc +++ b/README.adoc @@ -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`. @@ -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] ---- @@ -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] ---- @@ -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] @@ -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] ---- @@ -241,9 +244,41 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid` {% endfor %} ---- -=== ResolvedElementOrder (`Lutaml::Xsd::LiquidMethods::ResolvedElementOrder`) +=== Base Class Methods (`Lutaml::Xsd::Base`) -- `resolved_element_order` — A helper method that returns all elements in the order they originally appeared in the XSD. +The following methods are available on all XSD objects via the base class: + +Type checking methods: +- `is_any?` — Returns `true` if the object is an `Any` instance. +- `is_all?` — Returns `true` if the object is an `All` instance. +- `is_choice?` — Returns `true` if the object is a `Choice` instance. +- `is_element?` — Returns `true` if the object is an `Element` instance. +- `is_sequence?` — Returns `true` if the object is a `Sequence` instance. +- `is_attribute?` — Returns `true` if the object is an `Attribute` instance. +- `is_annotation?` — Returns `true` if the object is an `Annotation` instance. +- `is_simple_content?` — Returns `true` if the object is a `SimpleContent` instance. +- `is_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.is_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 From 1dbe1076125267202b851b49aa9a28520b65630e Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Thu, 13 Nov 2025 22:27:27 +0500 Subject: [PATCH 05/11] remove xml-c14n and added canon as dependency --- Gemfile | 2 -- lutaml-xsd.gemspec | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 3b9e3f5..d9b2006 100644 --- a/Gemfile +++ b/Gemfile @@ -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" @@ -15,4 +14,3 @@ gem "oga" gem "rake" gem "rspec" gem "rubocop" -gem "xml-c14n" diff --git a/lutaml-xsd.gemspec b/lutaml-xsd.gemspec index 768f319..685192e 100644 --- a/lutaml-xsd.gemspec +++ b/lutaml-xsd.gemspec @@ -34,5 +34,6 @@ Gem::Specification.new do |spec| # TODO: Remove "activesupport" once lutaml-model > 0.7.7 is available. spec.add_dependency "activesupport" + spec.add_dependency "canon" spec.add_dependency "lutaml-model", "~> 0.7" end From c0a454faeb04d76c0861c4d9fdebf525585b9f57 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Thu, 13 Nov 2025 22:31:57 +0500 Subject: [PATCH 06/11] updated spec_helper --- spec/spec_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1f34aae..76cf9dd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ require "liquid" require "lutaml/xsd" -require "xml/c14n" +require "canon" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure @@ -17,7 +17,7 @@ end def schema_to_xml(xml, escape_content_tags: false) - xml = Xml::C14n.format(xml) + xml = Canon.format_xml(xml) xml.gsub!(/<([^&]+)>/, '<\1>') if escape_content_tags xml end From 527938276cbc2ac644767068612337317bf25383 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Fri, 14 Nov 2025 17:42:35 +0500 Subject: [PATCH 07/11] freezed canon version 0.1.3 to fix failing specs --- lutaml-xsd.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lutaml-xsd.gemspec b/lutaml-xsd.gemspec index 685192e..33c2dc6 100644 --- a/lutaml-xsd.gemspec +++ b/lutaml-xsd.gemspec @@ -34,6 +34,6 @@ Gem::Specification.new do |spec| # TODO: Remove "activesupport" once lutaml-model > 0.7.7 is available. spec.add_dependency "activesupport" - spec.add_dependency "canon" + spec.add_dependency "canon", "0.1.3" spec.add_dependency "lutaml-model", "~> 0.7" end From cb84062274b223d7994a077f5630dcec59e731be Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Fri, 14 Nov 2025 20:33:48 +0500 Subject: [PATCH 08/11] Added specs, updated methods and readme --- .rubocop_todo.yml | 14 +- README.adoc | 20 +-- lib/lutaml/xsd/base.rb | 38 +++--- .../xsd/liquid_methods/attribute_group.rb | 2 +- lib/lutaml/xsd/liquid_methods/complex_type.rb | 3 +- lib/lutaml/xsd/liquid_methods/element.rb | 2 +- lib/lutaml/xsd/liquid_methods/group.rb | 8 +- .../liquid_methods/attribute_group_spec.rb | 74 ++++++++++ .../xsd/liquid_methods/attribute_spec.rb | 91 ++++++++++++ spec/lutaml/xsd/liquid_methods/choice_spec.rb | 84 ++++++++++++ .../xsd/liquid_methods/complex_type_spec.rb | 129 ++++++++++++++++++ .../lutaml/xsd/liquid_methods/element_spec.rb | 124 +++++++++++++++++ .../xsd/liquid_methods/extension_spec.rb | 53 +++++++ spec/lutaml/xsd/liquid_methods/group_spec.rb | 82 +++++++++++ .../xsd/liquid_methods/sequence_spec.rb | 72 ++++++++++ .../xsd/liquid_methods/simple_content_spec.rb | 77 +++++++++++ spec/lutaml/xsd_spec.rb | 19 +-- 17 files changed, 839 insertions(+), 53 deletions(-) create mode 100644 spec/lutaml/xsd/liquid_methods/attribute_group_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/attribute_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/choice_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/complex_type_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/element_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/extension_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/group_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/sequence_spec.rb create mode 100644 spec/lutaml/xsd/liquid_methods/simple_content_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 43c7998..0864c49 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -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 diff --git a/README.adoc b/README.adoc index 529b128..7322ca8 100644 --- a/README.adoc +++ b/README.adoc @@ -249,15 +249,15 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid` The following methods are available on all XSD objects via the base class: Type checking methods: -- `is_any?` — Returns `true` if the object is an `Any` instance. -- `is_all?` — Returns `true` if the object is an `All` instance. -- `is_choice?` — Returns `true` if the object is a `Choice` instance. -- `is_element?` — Returns `true` if the object is an `Element` instance. -- `is_sequence?` — Returns `true` if the object is a `Sequence` instance. -- `is_attribute?` — Returns `true` if the object is an `Attribute` instance. -- `is_annotation?` — Returns `true` if the object is an `Annotation` instance. -- `is_simple_content?` — Returns `true` if the object is a `SimpleContent` instance. -- `is_attribute_group?` — Returns `true` if the object is an `AttributeGroup` instance. +- `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`. @@ -269,7 +269,7 @@ Other methods: [source,liquid] ---- {% for item in schema.element %} - {% if item.is_element? %} + {% if item.element? %} Element: {{ item.name }} {% if item.min_occurrences %} Min: {{ item.min_occurrences }}, Max: {{ item.max_occurrences }} diff --git a/lib/lutaml/xsd/base.rb b/lib/lutaml/xsd/base.rb index 056c15d..e8c5c13 100644 --- a/lib/lutaml/xsd/base.rb +++ b/lib/lutaml/xsd/base.rb @@ -15,46 +15,46 @@ 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 is_sequence? + def sequence? is_a?(Sequence) end - def is_any? + def any? is_a?(Any) end - def is_all? + def all? is_a?(All) end - def is_choice? + def choice? is_a?(Choice) end - def is_annotation? + def annotation? is_a?(Annotation) end - def is_attribute? + def attribute? is_a?(Attribute) end - def is_attribute_group? + def attribute_group? is_a?(AttributeGroup) end - def is_simple_content? + def simple_content? is_a?(SimpleContent) end - def is_element? + def element? is_a?(Element) end @@ -73,18 +73,18 @@ def max_occurrences liquid do map "to_xml", to: :to_xml - map "is_any?", to: :is_any? - map "is_all?", to: :is_all? - map "is_choice?", to: :is_choice? - map "is_element?", to: :is_element? - map "is_sequence?", to: :is_sequence? - map "is_attribute?", to: :is_attribute? - map "is_annotation?", to: :is_annotation? + 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 "is_simple_content?", to: :is_simple_content? - map "is_attribute_group?", to: :is_attribute_group? + map "simple_content?", to: :simple_content? + map "attribute_group?", to: :attribute_group? map "resolved_element_order", to: :resolved_element_order end diff --git a/lib/lutaml/xsd/liquid_methods/attribute_group.rb b/lib/lutaml/xsd/liquid_methods/attribute_group.rb index bad88b8..a18b473 100644 --- a/lib/lutaml/xsd/liquid_methods/attribute_group.rb +++ b/lib/lutaml/xsd/liquid_methods/attribute_group.rb @@ -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 diff --git a/lib/lutaml/xsd/liquid_methods/complex_type.rb b/lib/lutaml/xsd/liquid_methods/complex_type.rb index 1a27a21..3d4f3c5 100644 --- a/lib/lutaml/xsd/liquid_methods/complex_type.rb +++ b/lib/lutaml/xsd/liquid_methods/complex_type.rb @@ -27,11 +27,12 @@ def attribute_elements(array = []) end def direct_child_elements(array = [], except: DIRECT_CHILD_ELEMENTS_EXCEPTION) - resolved_element_order.each_with_object(array) do |child| + 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 = []) diff --git a/lib/lutaml/xsd/liquid_methods/element.rb b/lib/lutaml/xsd/liquid_methods/element.rb index 4c6967b..9f009b8 100644 --- a/lib/lutaml/xsd/liquid_methods/element.rb +++ b/lib/lutaml/xsd/liquid_methods/element.rb @@ -9,7 +9,7 @@ def used_by end def attributes - referenced_complex_type.attribute_elements + referenced_complex_type&.attribute_elements end def child_elements(array = []) diff --git a/lib/lutaml/xsd/liquid_methods/group.rb b/lib/lutaml/xsd/liquid_methods/group.rb index c796782..185262f 100644 --- a/lib/lutaml/xsd/liquid_methods/group.rb +++ b/lib/lutaml/xsd/liquid_methods/group.rb @@ -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) @@ -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 diff --git a/spec/lutaml/xsd/liquid_methods/attribute_group_spec.rb b/spec/lutaml/xsd/liquid_methods/attribute_group_spec.rb new file mode 100644 index 0000000..a8ae967 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/attribute_group_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::AttributeGroup do + let(:schema_xml) do + <<~XML + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:attr_group) { schema.attribute_group.find { |ag| ag.name == "TestAttributeGroup" } } + let(:root_type) { schema.complex_type.find { |ct| ct.name == "RootType" } } + + before do + attr_group.instance_variable_set(:@__root, schema) + root_type.instance_variable_set(:@__root, schema) + end + + describe "#used_by" do + it "returns complex types that use this attribute group" do + used_by = attr_group.used_by + expect(used_by).to include(root_type) + end + end + + describe "#attribute_elements" do + it "returns all attributes from the attribute group" do + attrs = attr_group.attribute_elements + expect(attrs.length).to eq(2) + expect(attrs.map(&:name)).to contain_exactly("GroupAttr1", "GroupAttr2") + end + + it "accumulates attributes in the provided array" do + array = [] + attr_group.attribute_elements(array) + expect(array.length).to eq(2) + end + end + + describe "#find_used_by" do + it "returns true when attribute group is used by the object" do + expect(attr_group.find_used_by(root_type)).to be true + end + + it "returns false when attribute group is not used" do + other_type = Lutaml::Xsd::ComplexType.new(__register: Lutaml::Xsd.register) + expect(attr_group.find_used_by(other_type)).to be nil + end + end + + describe "#referenced_object" do + it "returns self when attribute group has a name" do + expect(attr_group.referenced_object).to eq(attr_group) + end + + it "returns the referenced attribute group when using ref" do + ref_group = Lutaml::Xsd::AttributeGroup.new(__register: Lutaml::Xsd.register) + ref_group.ref = "TestAttributeGroup" + ref_group.instance_variable_set(:@__root, schema) + expect(ref_group.referenced_object).to eq(attr_group) + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/attribute_spec.rb b/spec/lutaml/xsd/liquid_methods/attribute_spec.rb new file mode 100644 index 0000000..65ae254 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/attribute_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Attribute do + let(:schema_xml) do + <<~XML + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:required_attr) { schema.attribute.find { |a| a.name == "RequiredAttribute" } } + let(:optional_attr) { schema.attribute.find { |a| a.name == "OptionalAttribute" } } + let(:test_attr) { schema.attribute.find { |a| a.name == "TestAttribute" } } + + before do + [required_attr, optional_attr, test_attr].each do |attr| + attr&.instance_variable_set(:@__root, schema) + end + end + + describe "#cardinality" do + it "returns '1' for required attributes" do + required_attr.use = "required" + expect(required_attr.cardinality).to eq("1") + end + + it "returns '0..1' for optional attributes" do + optional_attr.use = "optional" + expect(optional_attr.cardinality).to eq("0..1") + end + + it "returns nil for attributes without use specified" do + test_attr.use = nil + expect(test_attr.cardinality).to be_nil + end + end + + describe "#referenced_type" do + it "returns the type of the attribute when it has a name" do + test_attr.type = "xs:string" + expect(test_attr.referenced_type).to eq("xs:string") + end + + it "returns the type of the referenced attribute when using ref" do + ref_attr = Lutaml::Xsd::Attribute.new(__register: Lutaml::Xsd.register) + ref_attr.ref = "TestAttribute" + ref_attr.instance_variable_set(:@__root, schema) + test_attr.type = "xs:int" + expect(ref_attr.referenced_type).to eq("xs:int") + end + end + + describe "#referenced_name" do + it "returns the name when attribute has a name" do + expect(test_attr.referenced_name).to eq("TestAttribute") + end + + it "returns the name of referenced attribute when using ref" do + ref_attr = Lutaml::Xsd::Attribute.new(__register: Lutaml::Xsd.register) + ref_attr.ref = "TestAttribute" + ref_attr.instance_variable_set(:@__root, schema) + expect(ref_attr.referenced_name).to eq("TestAttribute") + end + + it "returns ref when referenced object is not found" do + ref_attr = Lutaml::Xsd::Attribute.new(__register: Lutaml::Xsd.register) + ref_attr.ref = "NonExistent" + ref_attr.instance_variable_set(:@__root, schema) + expect(ref_attr.referenced_name).to eq("NonExistent") + end + end + + describe "#referenced_object" do + it "returns self when attribute has a name" do + expect(test_attr.referenced_object).to eq(test_attr) + end + + it "returns the referenced attribute when using ref" do + ref_attr = Lutaml::Xsd::Attribute.new(__register: Lutaml::Xsd.register) + ref_attr.ref = "TestAttribute" + ref_attr.instance_variable_set(:@__root, schema) + expect(ref_attr.referenced_object).to eq(test_attr) + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/choice_spec.rb b/spec/lutaml/xsd/liquid_methods/choice_spec.rb new file mode 100644 index 0000000..36b5b70 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/choice_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Choice do + let(:schema_xml) do + <<~XML + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:root_type) { schema.complex_type.find { |ct| ct.name == "RootType" } } + let(:choice) { root_type.sequence.choice.first } + + describe "#child_elements" do + it "returns all element children" do + elements = choice.child_elements + expect(elements.length).to eq(2) + expect(elements.map(&:name)).to contain_exactly("ChoiceElement1", "ChoiceElement2") + end + + it "accumulates elements in the provided array" do + expect(choice.child_elements.length).to eq(2) + end + + it "handles nested structures" do + nested_schema_xml = <<~XML + + + + + + + + + + XML + nested_schema = Lutaml::Xsd.parse(nested_schema_xml) + nested_choice = nested_schema.complex_type&.first&.choice&.sequence&.first + elements = nested_choice.child_elements + expect(elements.map(&:name)).to include("NestedElement") + end + end + + describe "#find_elements_used" do + let(:schema_xml_ref_element) do + <<~XML + + + + + + + + + + + + + XML + end + + it "returns true when element is used in choice" do + choice = Lutaml::Xsd.parse(schema_xml_ref_element).complex_type.first.sequence.choice.first + expect(choice.find_elements_used("Element1")).to be true + end + + it "returns false when element is not used" do + expect(choice.find_elements_used("NonExistent")).to be false + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/complex_type_spec.rb b/spec/lutaml/xsd/liquid_methods/complex_type_spec.rb new file mode 100644 index 0000000..2feabf9 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/complex_type_spec.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::ComplexType do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:root_type) { schema.complex_type.find { |ct| ct.name == "RootType" } } + let(:child_type) { schema.complex_type.find { |ct| ct.name == "ChildType" } } + + describe "#used_by" do + it "returns elements that use this complex type" do + used_by = root_type.used_by + expect(used_by).to be_an(Array) + expect(used_by.any? { |el| el.type == "RootType" }).to be true + end + end + + describe "#attribute_elements" do + it "returns all attributes including from attribute groups" do + attrs = root_type.attribute_elements + expect(attrs.length).to be >= 1 + expect(attrs.map(&:name)).to include("RootAttr") + end + + it "includes attributes from attribute groups" do + attrs = root_type.attribute_elements + attr_names = attrs.map(&:name) + expect(attr_names).to include("GroupAttr1") + end + + it "includes attributes from simple content" do + simple_content_schema = <<~XML + + + + + + + + + + XML + sc_schema = Lutaml::Xsd.parse(simple_content_schema) + sc_type = sc_schema.complex_type.first + sc_type.__root = sc_schema + attrs = sc_type.attribute_elements + expect(attrs.map(&:name)).to include("ExtendedAttr") + end + end + + describe "#direct_child_elements" do + it "returns direct child elements excluding attributes and annotations" do + elements = root_type.direct_child_elements + expect(elements.any? { |el| el.is_a?(Lutaml::Xsd::Sequence) }).to be true + expect(elements.none? { |el| el.is_a?(Lutaml::Xsd::Attribute) }).to be true + expect(elements.first.element.any? { |el| el.is_a?(Lutaml::Xsd::Element) }).to be true + end + + it "allows custom exception list" do + elements = root_type.direct_child_elements(except: ["Element"]) + expect(elements.none? { |el| el.is_a?(Lutaml::Xsd::Element) }).to be true + end + end + + describe "#child_elements" do + it "returns all nested child elements" do + elements = root_type.child_elements + expect(elements.length).to be >= 3 + expect(elements.map(&:referenced_name)).to include("DirectChild", "ChoiceElement1", "GroupElement1") + end + end + + describe "#find_elements_used" do + it "returns true when element is used" do + expect(root_type.find_elements_used("DirectChild")).to be true + end + + it "returns false when element is not used" do + expect(root_type.find_elements_used("NonExistent")).to be false + end + end + + describe "#find_used_by" do + it "returns true when complex type is used by the object" do + # This would require setting up a more complex scenario + # For now, we test the method exists and returns boolean + result = root_type.find_used_by(child_type) + expect([true, false]).to include(result) + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/element_spec.rb b/spec/lutaml/xsd/liquid_methods/element_spec.rb new file mode 100644 index 0000000..e285011 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/element_spec.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Element do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:root_element) { schema.element.find { |e| e.name == "RootElement" } } + let(:referenced_element) { schema.element.find { |e| e.name == "ReferencedElement" } } + let(:simple_element) { schema.element.find { |e| e.name == "SimpleElement" } } + + before do + [root_element, referenced_element, simple_element].each do |el| + el&.instance_variable_set(:@__root, schema) + end + schema.complex_type.each { |ct| ct.instance_variable_set(:@__root, schema) } + end + + describe "#used_by" do + it "returns complex types that use this element" do + used_by = root_element.used_by + expect(used_by).to be_an(Array) + end + end + + describe "#attributes" do + it "returns attributes from the referenced complex type" do + attrs = root_element.attributes + expect(attrs).to be_an(Array) + expect(attrs.map(&:name)).to include("RootAttr") + end + + it "returns empty array when element does not reference a complex type" do + attrs = simple_element.attributes + expect(attrs).to be_nil + end + end + + describe "#child_elements" do + it "returns child elements from the referenced complex type" do + elements = root_element.child_elements + expect(elements).to be_an(Array) + expect(elements.map(&:name)).to include("DirectChild") + end + + it "returns empty array when element does not reference a complex type" do + elements = simple_element.child_elements + expect(elements).to be_nil + end + end + + describe "#referenced_name" do + it "returns the name when element has a name" do + expect(root_element.referenced_name).to eq("RootElement") + end + + it "returns the name of referenced element when using ref" do + ref_element = Lutaml::Xsd::Element.new(__register: Lutaml::Xsd.register) + ref_element.ref = "ReferencedElement" + ref_element.instance_variable_set(:@__root, schema) + expect(ref_element.referenced_name).to eq("ReferencedElement") + end + end + + describe "#referenced_type" do + it "returns the type when element has a type" do + expect(root_element.referenced_type).to eq("RootType") + end + + it "returns the type of referenced element when using ref" do + ref_element = Lutaml::Xsd::Element.new(__register: Lutaml::Xsd.register) + ref_element.ref = "ReferencedElement" + ref_element.instance_variable_set(:@__root, schema) + expect(ref_element.referenced_type).to eq("xs:string") + end + end + + describe "#referenced_object" do + it "returns self when element has a name" do + expect(root_element.referenced_object).to eq(root_element) + end + + it "returns the referenced element when using ref" do + ref_element = Lutaml::Xsd::Element.new(__register: Lutaml::Xsd.register) + ref_element.ref = "ReferencedElement" + ref_element.instance_variable_set(:@__root, schema) + expect(ref_element.referenced_object).to eq(referenced_element) + end + end + + describe "#referenced_complex_type" do + it "returns the complex type when element references one" do + ct = root_element.referenced_complex_type + expect(ct).to be_a(Lutaml::Xsd::ComplexType) + expect(ct.name).to eq("RootType") + end + + it "returns nil when element does not reference a complex type" do + expect(simple_element.referenced_complex_type).to be_nil + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/extension_spec.rb b/spec/lutaml/xsd/liquid_methods/extension_spec.rb new file mode 100644 index 0000000..414b080 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/extension_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Extension do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:simple_content_type) { schema.complex_type.find { |ct| ct.name == "SimpleContentType" } } + let(:extension) { simple_content_type.simple_content.extension } + + before do + extension.attribute_group.each { |ag| ag.instance_variable_set(:@__root, schema) } + end + + describe "#attribute_elements" do + it "returns attributes from the extension" do + attrs = extension.attribute_elements + expect(attrs.length).to be >= 1 + expect(attrs.map(&:name)).to include("ExtendedAttr") + end + + it "includes attributes from attribute groups" do + attrs = extension.attribute_elements + attr_names = attrs.map(&:name) + expect(attr_names).to include("GroupAttr1", "GroupAttr2") + end + + it "accumulates attributes in the provided array" do + array = [] + extension.attribute_elements(array) + expect(array.length).to be >= 3 + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/group_spec.rb b/spec/lutaml/xsd/liquid_methods/group_spec.rb new file mode 100644 index 0000000..55143c9 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/group_spec.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Group do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:test_group) { schema.group.find { |g| g.name == "TestGroup" } } + let(:nested_group) { schema.group.find { |g| g.name == "NestedGroup" } } + + describe "#child_elements" do + it "returns all element children" do + elements = test_group.child_elements + expect(elements.length).to eq(2) + expect(elements.map(&:referenced_name)).to contain_exactly("GroupElement1", "GroupElement2") + end + + it "handles nested structures" do + elements = nested_group.child_elements + expect(elements.map(&:referenced_name)).to include("NestedElement", "NestedSeqElement") + end + + it "accumulates elements in the provided array" do + array = [] + test_group.child_elements(array) + expect(array.length).to eq(2) + end + end + + describe "#find_elements_used" do + it "returns true when element is used in group" do + expect(test_group.find_elements_used("GroupElement1")).to be true + end + + it "returns false when element is not used" do + expect(test_group.find_elements_used("NonExistent")).to be false + end + + it "finds elements in nested structures" do + expect(nested_group.find_elements_used("NestedElement")).to be true + expect(nested_group.find_elements_used("NestedSeqElement")).to be true + end + end + + describe "#referenced_object" do + it "returns self when group has a name" do + expect(test_group.referenced_object).to eq(test_group) + end + + it "returns the referenced group when using ref" do + ref_group = Lutaml::Xsd::Group.new(__register: Lutaml::Xsd.register) + ref_group.ref = "TestGroup" + ref_group.instance_variable_set(:@__root, schema) + expect(ref_group.referenced_object).to eq(test_group) + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/sequence_spec.rb b/spec/lutaml/xsd/liquid_methods/sequence_spec.rb new file mode 100644 index 0000000..b9628b7 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/sequence_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Sequence do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:root_type) { schema.complex_type.find { |ct| ct.name == "RootType" } } + let(:sequence) { root_type.sequence } + + describe "#child_elements" do + it "returns all element children" do + elements = sequence.child_elements + expected_elements = %w[ + DirectChild + ChoiceElement1 + ChoiceElement2 + NestedSeqElement + ] + expect(elements.length).to be >= 3 + expect(elements.map(&:referenced_name)).to include(*expected_elements) + end + + it "handles nested sequences" do + elements = sequence.child_elements + expect(elements.map(&:referenced_name)).to include("NestedSeqElement") + end + + it "accumulates elements in the provided array" do + array = [] + sequence.child_elements(array) + expect(array.length).to be >= 3 + end + end + + describe "#find_elements_used" do + it "returns true when element is used in sequence" do + expect(sequence.find_elements_used("DirectChild")).to be true + end + + it "returns false when element is not used" do + expect(sequence.find_elements_used("NonExistent")).to be false + end + + it "finds elements in nested structures" do + expect(sequence.find_elements_used("ChoiceElement1")).to be true + expect(sequence.find_elements_used("NestedSeqElement")).to be true + end + end +end diff --git a/spec/lutaml/xsd/liquid_methods/simple_content_spec.rb b/spec/lutaml/xsd/liquid_methods/simple_content_spec.rb new file mode 100644 index 0000000..a33dd18 --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/simple_content_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::SimpleContent do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + let(:complex_types) { schema.complex_type } + let(:simple_content) { complex_types.find { |ct| ct.name == "SimpleContentType" }.simple_content } + + describe "#attribute_elements" do + it "returns attributes from the extension" do + attrs = simple_content.attribute_elements + expect(attrs.length).to be >= 1 + expect(attrs.map(&:name)).to include("ExtendedAttr") + end + + it "includes attributes from attribute groups in extension" do + attrs = simple_content.attribute_elements + attr_names = attrs.map(&:name) + expect(attr_names).to include("GroupAttr1") + end + end + + describe "#base_type" do + let(:simple_content_with_base) { complex_types.find { |ct| ct.name == "SimpleContentWithBase" } } + let(:simple_content_with_restriction) { complex_types.find { |ct| ct.name == "SimpleContentWithRestriction" } } + + it "returns base from extension when present" do + expect(simple_content.base_type).to eq("xs:string") + end + + it "returns base from restriction when present" do + expect(simple_content_with_restriction.simple_content.base_type).to eq("xs:string") + end + + it "returns base attribute when extension and restriction are nil" do + expect(simple_content_with_base.simple_content.base_type).to eq("xs:int") + end + + it "returns nil when no base is specified" do + sc = Lutaml::Xsd::SimpleContent.new(__register: Lutaml::Xsd.register) + expect(sc.base_type).to be_nil + end + end +end diff --git a/spec/lutaml/xsd_spec.rb b/spec/lutaml/xsd_spec.rb index 3fd021e..d7d18db 100644 --- a/spec/lutaml/xsd_spec.rb +++ b/spec/lutaml/xsd_spec.rb @@ -10,6 +10,15 @@ "unitsml-v1.0-csd03": nil }.freeze +SCHEMA_ELEMENTS_REGEXES = { + imports: /<\w+:import /, + includes: /<\w+:include /, + group: /<\w+:group name=/, + simple_type: /<\w+:simpleType /, + element: /^\s{0,2}<\w+:element /, + complex_type: /<\w+:complexType / +}.freeze + RSpec.describe Lutaml::Xsd do subject(:parsed_schema) { described_class.parse(schema, location: location) } @@ -25,15 +34,7 @@ end it "matches count of direct child elements of the root" do - expected_counts = { - imports: /<\w+:import /, - includes: /<\w+:include /, - group: /<\w+:group name=/, - simple_type: /<\w+:simpleType /, - element: /^\s{0,2}<\w+:element /, - complex_type: /<\w+:complexType / - } - expected_counts.each_pair do |key, regex| + SCHEMA_ELEMENTS_REGEXES.each_pair do |key, regex| value = parsed_schema.send(key) count = value.nil? ? 0 : value.count expect(count).to eql(schema.scan(regex).count) From 7517c2728a01c4762dfe1f5747a731c56aeb2ba1 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Wed, 10 Dec 2025 19:50:39 +0500 Subject: [PATCH 09/11] added sorting liquid methods in schema file and updated documentation --- README.adoc | 25 +++++++++++++++++++++++++ lib/lutaml/xsd/liquid_methods/schema.rb | 21 +++++++++++++++++++++ lib/lutaml/xsd/schema.rb | 10 ++++++++++ 3 files changed, 56 insertions(+) create mode 100644 lib/lutaml/xsd/liquid_methods/schema.rb diff --git a/README.adoc b/README.adoc index 7322ca8..b36fab1 100644 --- a/README.adoc +++ b/README.adoc @@ -244,6 +244,31 @@ This gem exposes convenient methods for use in Liquid templates via `to_liquid` {% endfor %} ---- +=== Schema Class Methods (`Lutaml::Xsd::Schema`) + +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: diff --git a/lib/lutaml/xsd/liquid_methods/schema.rb b/lib/lutaml/xsd/liquid_methods/schema.rb new file mode 100644 index 0000000..cd1ed1e --- /dev/null +++ b/lib/lutaml/xsd/liquid_methods/schema.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Lutaml + module Xsd + module LiquidMethods + module AttributeGroup + 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 diff --git a/lib/lutaml/xsd/schema.rb b/lib/lutaml/xsd/schema.rb index 6182ed9..980be44 100644 --- a/lib/lutaml/xsd/schema.rb +++ b/lib/lutaml/xsd/schema.rb @@ -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 @@ -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( From 705962a65aa19062f728245d7e0670de64db1075 Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Wed, 10 Dec 2025 20:01:02 +0500 Subject: [PATCH 10/11] added new specs and fixed failing CI --- lib/lutaml/xsd/liquid_methods/schema.rb | 2 +- spec/lutaml/xsd/liquid_methods/schema_spec.rb | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 spec/lutaml/xsd/liquid_methods/schema_spec.rb diff --git a/lib/lutaml/xsd/liquid_methods/schema.rb b/lib/lutaml/xsd/liquid_methods/schema.rb index cd1ed1e..abd25df 100644 --- a/lib/lutaml/xsd/liquid_methods/schema.rb +++ b/lib/lutaml/xsd/liquid_methods/schema.rb @@ -3,7 +3,7 @@ module Lutaml module Xsd module LiquidMethods - module AttributeGroup + module Schema def elements_sorted_by_name element.sort_by(&:name) end diff --git a/spec/lutaml/xsd/liquid_methods/schema_spec.rb b/spec/lutaml/xsd/liquid_methods/schema_spec.rb new file mode 100644 index 0000000..a8bd99b --- /dev/null +++ b/spec/lutaml/xsd/liquid_methods/schema_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +RSpec.describe Lutaml::Xsd::LiquidMethods::Schema do + let(:schema_xml) do + <<~XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XML + end + + let(:schema) { Lutaml::Xsd.parse(schema_xml) } + + describe "#elements_sorted_by_name" do + it "returns elements sorted alphabetically by name" do + element_names = schema.elements_sorted_by_name.map(&:name) + expect(element_names).to eq(%w[AlphaElement BetaElement GammaElement]) + end + end + + describe "#complex_types_sorted_by_name" do + it "returns complex types sorted alphabetically by name" do + complex_type_names = schema.complex_types_sorted_by_name.map(&:name) + expect(complex_type_names).to eq(%w[AlphaType BetaType WidgetType]) + end + end + + describe "#attribute_groups_sorted_by_name" do + it "returns attribute groups sorted alphabetically by name" do + group_names = schema.attribute_groups_sorted_by_name.map(&:name) + expect(group_names).to eq(%w[BaseAttributes CoreAttributes ZetaAttributes]) + end + end +end From 3a928664bedd121ad174df16d40f7903d7acfa5b Mon Sep 17 00:00:00 2001 From: suleman-uzair Date: Thu, 11 Dec 2025 17:34:27 +0500 Subject: [PATCH 11/11] minor Copilot suggestions --- lib/lutaml/xsd/liquid_methods/sequence.rb | 4 ++-- spec/lutaml/xsd/liquid_methods/choice_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/lutaml/xsd/liquid_methods/sequence.rb b/lib/lutaml/xsd/liquid_methods/sequence.rb index 930e2ab..dbe8ab9 100644 --- a/lib/lutaml/xsd/liquid_methods/sequence.rb +++ b/lib/lutaml/xsd/liquid_methods/sequence.rb @@ -6,7 +6,7 @@ module LiquidMethods module Sequence def child_elements(array = []) resolved_element_order.each do |child| - if child.is_a?(Element) + if child.is_a?(Xsd::Element) array << child elsif child.respond_to?(:child_elements) child.child_elements(array) @@ -17,7 +17,7 @@ def child_elements(array = []) def find_elements_used(element_name) resolved_element_order.any? do |child| - if child.is_a?(Element) + if child.is_a?(Xsd::Element) child.ref == element_name elsif child.respond_to?(:find_elements_used) child.find_elements_used(element_name) diff --git a/spec/lutaml/xsd/liquid_methods/choice_spec.rb b/spec/lutaml/xsd/liquid_methods/choice_spec.rb index 36b5b70..a02c1a8 100644 --- a/spec/lutaml/xsd/liquid_methods/choice_spec.rb +++ b/spec/lutaml/xsd/liquid_methods/choice_spec.rb @@ -58,7 +58,7 @@ - +