xmi 0.6.1 → 0.7.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.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xmi
4
+ module Performance
5
+ # Terminal formatting for benchmark output. Colors live on the
6
+ # parent Xmi::Performance module; box-drawing characters here.
7
+ module Term
8
+ extend self
9
+
10
+ HL = "─"
11
+ VL = "│"
12
+ TL = "┌"
13
+ TR = "┐"
14
+ BL = "└"
15
+ BR = "┘"
16
+
17
+ def header(title, color: CYAN)
18
+ width = 78
19
+ line = HL * width
20
+ puts
21
+ puts "#{color}#{TL}#{line}#{TR}#{CLEAR}"
22
+ puts "#{color}#{VL}#{CLEAR} #{BOLD}#{color}#{title}#{CLEAR}#{' ' * (width - title.length - 4)}#{color}#{VL}#{CLEAR}"
23
+ puts "#{color}#{BL}#{line}#{BR}#{CLEAR}"
24
+ end
25
+
26
+ def sep(char: HL, width: 78)
27
+ puts "#{DIM}#{char * width}#{CLEAR}"
28
+ end
29
+
30
+ def env_info(ruby_version, platform)
31
+ puts
32
+ puts " #{DIM}Environment:#{CLEAR}"
33
+ puts " #{VL} Ruby #{ruby_version} on #{platform}#{' ' * (60 - ruby_version.length - platform.length)}#{VL}"
34
+ puts " #{DIM}#{BL}#{HL * 76}#{BR}#{CLEAR}"
35
+ puts
36
+ end
37
+
38
+ def category(title, icon:, description:, failure_means:,
39
+ compare_against: nil)
40
+ puts
41
+ puts "#{CYAN}#{VL}#{CLEAR} #{BOLD}#{MAGENTA}#{icon} #{title}#{CLEAR}"
42
+ puts
43
+ puts " #{DIM}#{description}#{CLEAR}"
44
+ puts
45
+
46
+ if compare_against
47
+ puts " #{CYAN}Comparing against:#{CLEAR} #{compare_against}"
48
+ puts
49
+ end
50
+
51
+ puts " #{YELLOW}⚠️ Failure means:#{CLEAR} #{failure_means}"
52
+ puts
53
+ sep(width: 76)
54
+ puts
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xmi
4
+ # Benchmarking and performance comparison tooling.
5
+ #
6
+ # Loaded on demand via autoload — never required at gem runtime.
7
+ # The rake tasks in lib/tasks/performance.rake load this module
8
+ # through `require "xmi"` and reference Xmi::Performance::*
9
+ # constants.
10
+ module Performance
11
+ autoload :Helpers, "xmi/performance/helpers"
12
+ autoload :Comparator, "xmi/performance/comparator"
13
+ autoload :Runner, "xmi/performance/runner"
14
+ autoload :Term, "xmi/performance/term"
15
+
16
+ # ANSI color codes shared by Term, Helpers, Comparator, and Runner.
17
+ CLEAR = "\e[0m"
18
+ BOLD = "\e[1m"
19
+ DIM = "\e[2m"
20
+ CYAN = "\e[36m"
21
+ GREEN = "\e[32m"
22
+ YELLOW = "\e[33m"
23
+ RED = "\e[31m"
24
+ GRAY = "\e[90m"
25
+ MAGENTA = "\e[35m"
26
+ end
27
+ end
@@ -190,6 +190,7 @@ module Xmi
190
190
 
191
191
  # Recurse into children
192
192
  walk_packaged_elements(pe.packaged_element, pe)
193
+ walk_packaged_elements(pe.nested_classifier, pe)
193
194
  end
194
195
  end
195
196
 
@@ -5,7 +5,7 @@ module Xmi
5
5
  # UML `<packagedElement xmi:type="uml:AssociationClass">`. An
6
6
  # association that is also a class (UML 2.5 §11.4).
7
7
  #
8
- # Phase A of TODO.next/01: subclass is a type tag on
8
+ # Phase A: subclass is a type tag on
9
9
  # PackagedElement. The union-bag attribute set is inherited
10
10
  # unchanged so existing consumers keep working. Phase B
11
11
  # (narrowing attrs to the subclass) is future work.
@@ -9,7 +9,7 @@ module Xmi
9
9
  # interface_realization — but signals a different deployment role
10
10
  # to consumers.
11
11
  #
12
- # Phase A of TODO.next/01: subclass is a type tag on
12
+ # Phase A: subclass is a type tag on
13
13
  # PackagedElement. The union-bag attribute set is inherited
14
14
  # unchanged so existing consumers keep working.
15
15
  class Component < PackagedElement
@@ -5,7 +5,7 @@ module Xmi
5
5
  # UML `<packagedElement xmi:type="uml:Dependency">`. A relationship
6
6
  # between a client and supplier packageable element.
7
7
  #
8
- # Phase A of TODO.next/01: subclass is a type tag on
8
+ # Phase A: subclass is a type tag on
9
9
  # PackagedElement. The union-bag attribute set is inherited
10
10
  # unchanged so existing consumers keep working. Phase B
11
11
  # (narrowing attrs to the subclass) is future work.
@@ -6,7 +6,7 @@ module Xmi
6
6
  # associates a stereotype with a metaclass to make the stereotype
7
7
  # applicable to instances of that metaclass (UML 2.5 §22.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -35,10 +35,11 @@ module Xmi
35
35
  map_attribute "aggregation", to: :aggregation
36
36
 
37
37
  map_element "type", to: :uml_type
38
- map_element "upperValue", to: :upper_value,
39
- polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
38
+ # Sparx EA emits lowerValue before upperValue.
40
39
  map_element "lowerValue", to: :lower_value,
41
40
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
41
+ map_element "upperValue", to: :upper_value,
42
+ polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
42
43
  map_element "defaultValue", to: :default_value,
43
44
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
44
45
  end
@@ -7,7 +7,7 @@ module Xmi
7
7
  attribute :body_element, :string
8
8
  attribute :body_attribute, :string
9
9
  attribute :annotated_attribute, :string
10
- attribute :annotated_element, AnnotatedElement
10
+ attribute :annotated_element, AnnotatedElement, collection: true
11
11
 
12
12
  xml do
13
13
  root "ownedComment"
@@ -15,7 +15,8 @@ module Xmi
15
15
  map_attribute "body", to: :body_attribute
16
16
  map_attribute "annotatedElement", to: :annotated_attribute
17
17
 
18
- map_element "annotatedElement", to: :annotated_element
18
+ map_element "annotatedElement", to: :annotated_element,
19
+ value_map: VALUE_MAP
19
20
  map_element "body", to: :body_element
20
21
  end
21
22
  end
@@ -12,7 +12,7 @@ module Xmi
12
12
  attribute :bounds, Bounds, collection: true
13
13
  attribute :source, :string
14
14
  attribute :target, :string
15
- attribute :waypoint, Waypoint
15
+ attribute :waypoint, Waypoint, collection: true
16
16
 
17
17
  xml do
18
18
  root "ownedElement"
@@ -23,7 +23,7 @@ module Xmi
23
23
  map_element "bounds", to: :bounds, value_map: VALUE_MAP
24
24
  map_element "source", to: :source
25
25
  map_element "target", to: :target
26
- map_element "waypoint", to: :waypoint
26
+ map_element "waypoint", to: :waypoint, value_map: VALUE_MAP
27
27
  end
28
28
  end
29
29
  end
@@ -24,10 +24,11 @@ module Xmi
24
24
  map_attribute "isComposite", to: :is_composite
25
25
 
26
26
  map_element "type", to: :uml_type
27
- map_element "upperValue", to: :upper_value,
28
- polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
27
+ # Sparx EA emits lowerValue before upperValue.
29
28
  map_element "lowerValue", to: :lower_value,
30
29
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
30
+ map_element "upperValue", to: :upper_value,
31
+ polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
31
32
  map_element "defaultValue", to: :default_value,
32
33
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
33
34
  end
@@ -4,6 +4,23 @@ module Xmi
4
4
  module Uml
5
5
  class OwnedParameter < Base
6
6
  attribute :name, :string
7
+ # These are two DIFFERENT attributes that collide here:
8
+ # xmi:type="uml:Parameter" — the XMI metaclass discriminator
9
+ # type="EAnone_void" — Sparx's classifier reference
10
+ #
11
+ # Known limit: lutaml-model matches attributes by local name only,
12
+ # so both land in this one slot and whichever appears LAST in the
13
+ # input wins. They are not two spellings of one concept, and only
14
+ # one of them can survive a round trip.
15
+ #
16
+ # The slot stays xmi-namespaced, which keeps the discriminator —
17
+ # the general UML XMI shape this gem round-trips. Sparx's
18
+ # classifier reference still parses into it, but re-serializes as
19
+ # `xmi:type`. Restoring the Sparx spelling belongs to the Sparx
20
+ # exporter, not to this shared model: flipping it here would break
21
+ # the general case for every other consumer. Modelling both at
22
+ # once needs namespace-disjoint attribute deserialization
23
+ # upstream (lutaml-model#744).
7
24
  attribute :type, ::Xmi::Type::XmiType
8
25
  attribute :direction, :string
9
26
  attribute :visibility, :string
@@ -24,10 +41,11 @@ module Xmi
24
41
  map_attribute "isUnique", to: :is_unique
25
42
  map_attribute "effect", to: :effect
26
43
 
27
- map_element "upperValue", to: :upper_value,
28
- polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
44
+ # Sparx EA emits lowerValue before upperValue.
29
45
  map_element "lowerValue", to: :lower_value,
30
46
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
47
+ map_element "upperValue", to: :upper_value,
48
+ polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
31
49
  map_element "defaultValue", to: :default_value,
32
50
  polymorphic: VALUE_SPECIFICATION_POLYMORPHIC_MAP
33
51
  end
@@ -7,17 +7,23 @@ module Xmi
7
7
  # packaged_element recursion on PackagedElement itself, and by
8
8
  # UmlModel.packaged_element.
9
9
  #
10
- # Phase A of TODO.next/01: subclasses are type tags on
11
- # PackagedElement (no attrs narrowed). Phase B will narrow each
12
- # subclass's attrs to its UML-2.5-conformant subset.
13
- #
14
- # Fallback contract: unknown or missing `xmi:type` resolves to
10
+ # Fallback contract: an unknown `xmi:type` resolves to
15
11
  # `Xmi::Uml::PackagedElement` (the union-bag base) via the
16
- # class_map's default_proc. This avoids the lutaml-model
12
+ # class_map's Hash default value. This avoids the lutaml-model
17
13
  # `Object.const_get(nil)` TypeError when a Sparx XMI emits a
18
14
  # type we haven't modelled yet, at the cost of treating the
19
- # element as generic. polymorphic_robustness_spec locks in the
20
- # graceful-fallback behavior.
15
+ # element as generic. polymorphic_map_contract_spec and
16
+ # polymorphic_robustness_spec lock in the graceful-fallback
17
+ # behavior.
18
+ #
19
+ # Two paths land at the base:
20
+ # 1. Missing discriminator — lutaml-model's
21
+ # `polymorphic_map_defined?` (lib/lutaml/model/attribute.rb)
22
+ # short-circuits and returns the declared attribute type
23
+ # (`PackagedElement`). The class_map default is not consulted.
24
+ # 2. Unknown discriminator — `polymorphic_map_defined?` is true,
25
+ # the class_map lookup hits a missing key, and the Hash default
26
+ # value supplies the fallback class name.
21
27
  PACKAGED_ELEMENT_POLYMORPHIC_MAP = {
22
28
  attribute: "xmi:type",
23
29
  class_map: Hash.new("Xmi::Uml::PackagedElement").merge!(
@@ -37,7 +43,7 @@ module Xmi
37
43
  "uml:Stereotype" => "Xmi::Uml::Stereotype",
38
44
  "uml:Usage" => "Xmi::Uml::Usage",
39
45
  "uml:Component" => "Xmi::Uml::Component",
40
- ),
46
+ ).freeze,
41
47
  }.freeze
42
48
 
43
49
  class PackagedElement < Base
@@ -58,6 +64,10 @@ module Xmi
58
64
 
59
65
  attribute :packaged_element, PackagedElement, collection: true,
60
66
  polymorphic: true
67
+ # Sparx EA nests child classifiers (a Class owned by a Class)
68
+ # as <nestedClassifier> rather than <packagedElement>.
69
+ attribute :nested_classifier, PackagedElement, collection: true,
70
+ polymorphic: true
61
71
  attribute :owned_end, OwnedEnd, collection: true
62
72
  attribute :owned_attribute, OwnedAttribute, collection: true
63
73
  attribute :owned_comment, OwnedComment, collection: true
@@ -90,6 +100,8 @@ module Xmi
90
100
  map_element "ownedOperation", to: :owned_operation, value_map: VALUE_MAP
91
101
  map_element "packagedElement", to: :packaged_element,
92
102
  polymorphic: PACKAGED_ELEMENT_POLYMORPHIC_MAP
103
+ map_element "nestedClassifier", to: :nested_classifier,
104
+ polymorphic: PACKAGED_ELEMENT_POLYMORPHIC_MAP
93
105
  map_element "memberEnd", to: :member_ends, value_map: VALUE_MAP
94
106
  map_element "slot", to: :slot, value_map: VALUE_MAP
95
107
  map_element "specification", to: :specification
@@ -6,7 +6,7 @@ module Xmi
6
6
  # classifier that communicates the sending of a message instance
7
7
  # (UML 2.5 §17.4).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -6,7 +6,7 @@ module Xmi
6
6
  # is a kind of class defined to extend the UML metamodel
7
7
  # (UML 2.5 §22.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -5,7 +5,7 @@ module Xmi
5
5
  # UML `<packagedElement xmi:type="uml:Class">`. Classifier with
6
6
  # attributes, operations, and structural relationships.
7
7
  #
8
- # Phase A of TODO.next/01: subclass is a type tag on
8
+ # Phase A: subclass is a type tag on
9
9
  # PackagedElement. The union-bag attribute set is inherited
10
10
  # unchanged so existing consumers keep working. Phase B
11
11
  # (narrowing attrs to the subclass) is future work.
data/lib/xmi/uml/usage.rb CHANGED
@@ -6,7 +6,7 @@ module Xmi
6
6
  # subtype indicating that a client requires a supplier for its
7
7
  # proper functioning (UML 2.5 §19.3).
8
8
  #
9
- # Phase A of TODO.next/01: subclass is a type tag on
9
+ # Phase A: subclass is a type tag on
10
10
  # PackagedElement. The union-bag attribute set is inherited
11
11
  # unchanged so existing consumers keep working. Phase B
12
12
  # (narrowing attrs to the subclass) is future work.
@@ -32,10 +32,16 @@ module Xmi
32
32
  # The literal class names are string form because lutaml-model's
33
33
  # resolve_polymorphic_class calls Object.const_get on the value.
34
34
  #
35
- # Fallback contract: unknown or missing `xmi:type` resolves to
35
+ # Fallback contract: an unknown `xmi:type` resolves to
36
36
  # `Xmi::Uml::ValueSpecification` (the abstract base) via the
37
- # class_map's default_proc. polymorphic_robustness_spec locks in
38
- # the graceful-fallback behavior.
37
+ # class_map's Hash default value. polymorphic_map_contract_spec
38
+ # and polymorphic_robustness_spec lock in the graceful-fallback
39
+ # behavior.
40
+ #
41
+ # Two paths land at the base (see PACKAGED_ELEMENT_POLYMORPHIC_MAP
42
+ # for the full rationale): missing discriminator short-circuits via
43
+ # lutaml-model's `polymorphic_map_defined?`; unknown discriminator
44
+ # hits the Hash default value.
39
45
  VALUE_SPECIFICATION_POLYMORPHIC_MAP = {
40
46
  attribute: "xmi:type",
41
47
  class_map: Hash.new("Xmi::Uml::ValueSpecification").merge!(
@@ -45,7 +51,7 @@ module Xmi
45
51
  "uml:LiteralBoolean" => "Xmi::Uml::LiteralBoolean",
46
52
  "uml:LiteralUnlimitedNatural" => "Xmi::Uml::LiteralUnlimitedNatural",
47
53
  "uml:LiteralNull" => "Xmi::Uml::LiteralNull",
48
- ),
54
+ ).freeze,
49
55
  }.freeze
50
56
  end
51
57
  end
data/lib/xmi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Xmi
4
- VERSION = "0.6.1"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/xmi.rb CHANGED
@@ -71,6 +71,7 @@ module Xmi
71
71
  autoload :CustomProfile, "xmi/custom_profile"
72
72
  autoload :Root, "xmi/root"
73
73
  autoload :Sparx, "xmi/sparx"
74
+ autoload :Performance, "xmi/performance"
74
75
 
75
76
  # Unified parsing API
76
77
  autoload :Parsing, "xmi/parsing"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xmi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-09 00:00:00.000000000 Z
11
+ date: 2026-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -61,10 +61,7 @@ files:
61
61
  - bin/setup
62
62
  - docs/migration.md
63
63
  - docs/versioning.md
64
- - lib/tasks/benchmark_runner.rb
65
64
  - lib/tasks/performance.rake
66
- - lib/tasks/performance_comparator.rb
67
- - lib/tasks/performance_helpers.rb
68
65
  - lib/xmi.rb
69
66
  - lib/xmi/add.rb
70
67
  - lib/xmi/custom_profile.rb
@@ -97,6 +94,11 @@ files:
97
94
  - lib/xmi/namespace_registry.rb
98
95
  - lib/xmi/parser_pipeline.rb
99
96
  - lib/xmi/parsing.rb
97
+ - lib/xmi/performance.rb
98
+ - lib/xmi/performance/comparator.rb
99
+ - lib/xmi/performance/helpers.rb
100
+ - lib/xmi/performance/runner.rb
101
+ - lib/xmi/performance/term.rb
100
102
  - lib/xmi/replace.rb
101
103
  - lib/xmi/root.rb
102
104
  - lib/xmi/sparx.rb