expressir 2.4.44 → 2.4.46

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/conformance.yml +4 -0
  3. data/.github/workflows/gemspec-floor.yml +61 -0
  4. data/.github/workflows/performance-release.yml +3 -1
  5. data/lib/expressir/coverage.rb +19 -3
  6. data/lib/expressir/model/data_types/enumeration.rb +1 -1
  7. data/lib/expressir/model/data_types/select.rb +1 -1
  8. data/lib/expressir/model/declarations/entity.rb +7 -7
  9. data/lib/expressir/model/declarations/function.rb +9 -9
  10. data/lib/expressir/model/declarations/interface.rb +1 -1
  11. data/lib/expressir/model/declarations/interfaced_item.rb +2 -2
  12. data/lib/expressir/model/declarations/procedure.rb +9 -9
  13. data/lib/expressir/model/declarations/remark_item.rb +1 -1
  14. data/lib/expressir/model/declarations/rule.rb +11 -11
  15. data/lib/expressir/model/declarations/schema.rb +8 -8
  16. data/lib/expressir/model/declarations/schema_version.rb +1 -1
  17. data/lib/expressir/model/declarations/subtype_constraint.rb +1 -1
  18. data/lib/expressir/model/declarations/type.rb +2 -2
  19. data/lib/expressir/model/declarations/unique_rule.rb +1 -1
  20. data/lib/expressir/model/exp_file.rb +1 -1
  21. data/lib/expressir/model/expressions/aggregate_initializer.rb +1 -1
  22. data/lib/expressir/model/expressions/entity_constructor.rb +1 -1
  23. data/lib/expressir/model/expressions/function_call.rb +1 -1
  24. data/lib/expressir/model/identifier.rb +3 -3
  25. data/lib/expressir/model/model_element.rb +1 -1
  26. data/lib/expressir/model/statements/alias.rb +1 -1
  27. data/lib/expressir/model/statements/case.rb +1 -1
  28. data/lib/expressir/model/statements/case_action.rb +1 -1
  29. data/lib/expressir/model/statements/compound.rb +1 -1
  30. data/lib/expressir/model/statements/if.rb +2 -2
  31. data/lib/expressir/model/statements/procedure_call.rb +1 -1
  32. data/lib/expressir/model/statements/repeat.rb +1 -1
  33. data/lib/expressir/model/supertype_expressions/oneof_supertype_expression.rb +1 -1
  34. data/lib/expressir/p28/xsd.rb +35 -5
  35. data/lib/expressir/version.rb +1 -1
  36. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1eb19f12779500cf95395e7422a425bbb59e6e4e3f10cc1c62f5946f79cc4644
4
- data.tar.gz: 227a77557ce585cb6313944dbc7cd6e5104f5e5594214c48a9c59955dce4a4b7
3
+ metadata.gz: dfc39b74ae5aed552c9487f7fe8a8e642947c76af8faaeb6a3efb9394b975e68
4
+ data.tar.gz: 38b6d067b885aa364b2027916b27b4ee2ab706a43c6a98da157c654638d309d8
5
5
  SHA512:
6
- metadata.gz: 80e48ee59df97ce558cd507a89eccae20c9063c3031f51dc77803b8ca5f148ac0cb5344dc969c3e0c639fd706f95d3db80788b0de1d56a411ccf10ea75f8e580
7
- data.tar.gz: 043ec4835ace4e4922f166ceef3c216c2a8b6f2b06c591aca93a954172b31ec50d2cddc97c54e517ce84004a305a8f39249c95f3abd7c3a3311c831c9e63898e
6
+ metadata.gz: e6554fc3447df08d8093e4bbde2956485b6fbcbeede516899a2f013f0fe81c7bc1afc7635ad6c5afb46ee69c82cc14c36d415997ff10b742307ae8028c6088e9
7
+ data.tar.gz: 8d069223f80dbaa4c7e282467c054e6488ca02f5a2def0860cd1c6ddb7bdf147a0c515e572e17310a7250124218cf8ac25ff760c69c7989e2b4bdacd349948d0
@@ -15,6 +15,10 @@ jobs:
15
15
  repository: metanorma/iso-10303-stepmod
16
16
  path: stepmod
17
17
  sparse-checkout: data/resources
18
+ # the stepmod corpus is a PRIVATE metanorma repo; the default
19
+ # GITHUB_TOKEN is scoped to lutaml/expressir and got a 404 on
20
+ # every scheduled run since the workflow was added (2026-10-21)
21
+ token: ${{ secrets.LUTAML_CI_PAT_TOKEN }}
18
22
  - uses: ruby/setup-ruby@v1
19
23
  with:
20
24
  ruby-version: "3.4"
@@ -0,0 +1,61 @@
1
+ # Floor guard: every released expressir must LOAD under the OLDEST
2
+ # lutaml-model its own gemspec admits. v2.4.4/v2.4.5 shipped `map ...,
3
+ # serialize: false` while declaring `lutaml-model ~> 0.8.0` — the
4
+ # keyword first exists in 0.8.43 — so any bundle resolving that pair
5
+ # died at class-load time (issue #566). This job pins lutaml-model to
6
+ # the exact gemspec floor and loads the model classes against it.
7
+ name: gemspec-floor
8
+
9
+ on:
10
+ push:
11
+ branches: [ master, main ]
12
+ pull_request:
13
+
14
+ jobs:
15
+ floor:
16
+ runs-on: ubuntu-latest
17
+ name: lutaml-model at the gemspec floor
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: '3.3'
24
+
25
+ - name: Extract the lutaml-model floor from the gemspec
26
+ id: floor
27
+ run: |
28
+ floor=$(ruby -e '
29
+ spec = Gem::Specification.load(Dir["*.gemspec"].first)
30
+ dep = spec.dependencies.find { |d| d.name == "lutaml-model" }
31
+ candidates = dep.requirement.requirements
32
+ .select { |op, _| [">=", "~>"].include?(op) }
33
+ .map { |_, v| v.version }
34
+ abort "no lutaml-model floor in the gemspec" if candidates.empty?
35
+ puts candidates.min
36
+ ')
37
+ echo "floor=$floor" >> "$GITHUB_OUTPUT"
38
+ echo "lutaml-model floor: $floor"
39
+
40
+ - name: Pin lutaml-model exactly at the floor
41
+ run: |
42
+ cat >> Gemfile <<'GEMFILE'
43
+
44
+ gem "lutaml-model", "= ${{ steps.floor.outputs.floor }}"
45
+ GEMFILE
46
+ bundle install
47
+
48
+ - name: Load the model classes under the floor
49
+ run: |
50
+ bundle exec ruby -e '
51
+ require "lutaml/model"
52
+ resolved = Gem.loaded_specs["lutaml-model"].version.to_s
53
+ abort "resolved #{resolved}, expected ${{ steps.floor.outputs.floor }}" unless resolved == "${{ steps.floor.outputs.floor }}"
54
+ require "expressir"
55
+ require "expressir/model/model_element"
56
+ require "expressir/model"
57
+ puts "model classes load under lutaml-model #{resolved}"
58
+ '
59
+
60
+ - name: Model specs under the floor
61
+ run: bundle exec rspec spec/expressir/model
@@ -105,7 +105,9 @@ jobs:
105
105
  TAG=${TAG:-$(gh release view --json tagName --jq .tagName)}
106
106
  echo "Reporting against $TAG"
107
107
  gh release view "$TAG" --json body --jq .body > /tmp/body.md
108
- ruby -e '
108
+ # bundle exec: the expressir gem is linked through the Gemfile's
109
+ # gemspec — bare ruby cannot load it (LoadError, 2026-10-26+ runs)
110
+ bundle exec ruby -e '
109
111
  require "json"
110
112
  require "expressir"
111
113
  require "expressir/scoreboard"
@@ -465,9 +465,11 @@ module Expressir
465
465
  elsif entity_class == "Expressir::Model::Declarations::Type" && type_subtype_skips.any?
466
466
  entity_subtype = get_type_subtype(entity)
467
467
  type_subtype_skips.include?(entity_subtype)
468
- # Check FUNCTION:INNER exclusions
469
- elsif entity_class == "Expressir::Model::Declarations::Function" && function_subtype_skips.include?("INNER")
470
- inner_function?(entity)
468
+ # Check FUNCTION:INNER exclusions — the whole inner-function
469
+ # subtree is excluded, its parameters and nested declarations
470
+ # included, not only the function entity itself
471
+ elsif function_subtype_skips.include?("INNER") && within_inner_function?(entity)
472
+ true
471
473
  else
472
474
  false
473
475
  end
@@ -506,5 +508,19 @@ module Expressir
506
508
  parent.is_a?(Expressir::Model::Declarations::Rule) ||
507
509
  parent.is_a?(Expressir::Model::Declarations::Procedure)
508
510
  end
511
+
512
+ # Check if an entity is an inner function or a declaration belonging to one
513
+ # (its parameters, nested types, nested functions, ...)
514
+ # @param entity [Expressir::Model::ModelElement] The entity to check
515
+ # @return [Boolean] True if the entity or any of its ancestors is an inner function
516
+ def self.within_inner_function?(entity)
517
+ current = entity
518
+ while current
519
+ return true if current.is_a?(Model::Declarations::Function) && inner_function?(current)
520
+
521
+ current = current.respond_to?(:parent) ? current.parent : nil
522
+ end
523
+ false
524
+ end
509
525
  end
510
526
  end
@@ -6,7 +6,7 @@ module Expressir
6
6
  class Enumeration < ModelElement
7
7
  attribute :extensible, :boolean
8
8
  attribute :based_on, ModelElement
9
- attribute :items, EnumerationItem, collection: true
9
+ attribute :items, EnumerationItem, collection: true, initialize_empty: true
10
10
  attribute :_class, :string, default: -> { self.class.name }
11
11
 
12
12
  key_value do
@@ -7,7 +7,7 @@ module Expressir
7
7
  attribute :extensible, :boolean
8
8
  attribute :generic_entity, :boolean
9
9
  attribute :based_on, ModelElement
10
- attribute :items, ModelElement, collection: true
10
+ attribute :items, ModelElement, collection: true, initialize_empty: true
11
11
  attribute :_class, :string, default: -> { self.class.name }
12
12
 
13
13
  key_value do
@@ -16,19 +16,19 @@ module Expressir
16
16
 
17
17
  attribute :abstract, :boolean
18
18
  attribute :supertype_expression, ModelElement
19
- attribute :subtype_of, ModelElement, collection: true
19
+ attribute :subtype_of, ModelElement, collection: true, initialize_empty: true
20
20
  # Heterogeneous by design: Attribute, DerivedAttribute, and
21
21
  # InverseAttribute all live here (the builders concatenate the
22
22
  # three clauses in declaration order). The ModelElement base
23
23
  # keeps from_hash polymorphic via the _class marker, exactly
24
24
  # like the Ruby-built model.
25
- attribute :attributes, ModelElement, collection: true
26
- attribute :derived_attributes, DerivedAttribute, collection: true
27
- attribute :inverse_attributes, InverseAttribute, collection: true
28
- attribute :unique_rules, UniqueRule, collection: true
29
- attribute :where_rules, WhereRule, collection: true
25
+ attribute :attributes, ModelElement, collection: true, initialize_empty: true
26
+ attribute :derived_attributes, DerivedAttribute, collection: true, initialize_empty: true
27
+ attribute :inverse_attributes, InverseAttribute, collection: true, initialize_empty: true
28
+ attribute :unique_rules, UniqueRule, collection: true, initialize_empty: true
29
+ attribute :where_rules, WhereRule, collection: true, initialize_empty: true
30
30
  attribute :informal_propositions, InformalPropositionRule,
31
- collection: true
31
+ collection: true, initialize_empty: true
32
32
  attribute :_class, :string, default: -> { self.class.name }
33
33
 
34
34
  key_value do
@@ -12,16 +12,16 @@ module Expressir
12
12
  :constants, :variables, :statements,
13
13
  :remark_items
14
14
 
15
- attribute :parameters, Parameter, collection: true
15
+ attribute :parameters, Parameter, collection: true, initialize_empty: true
16
16
  attribute :return_type, ModelElement
17
- attribute :types, ModelElement, collection: true
18
- attribute :entities, Entity, collection: true
19
- attribute :subtype_constraints, SubtypeConstraint, collection: true
20
- attribute :functions, Function, collection: true
21
- attribute :procedures, Procedure, collection: true
22
- attribute :constants, Constant, collection: true
23
- attribute :variables, Variable, collection: true
24
- attribute :statements, ModelElement, collection: true
17
+ attribute :types, ModelElement, collection: true, initialize_empty: true
18
+ attribute :entities, Entity, collection: true, initialize_empty: true
19
+ attribute :subtype_constraints, SubtypeConstraint, collection: true, initialize_empty: true
20
+ attribute :functions, Function, collection: true, initialize_empty: true
21
+ attribute :procedures, Procedure, collection: true, initialize_empty: true
22
+ attribute :constants, Constant, collection: true, initialize_empty: true
23
+ attribute :variables, Variable, collection: true, initialize_empty: true
24
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
25
25
  attribute :_class, :string, default: -> { self.class.name }
26
26
 
27
27
  key_value do
@@ -12,7 +12,7 @@ module Expressir
12
12
 
13
13
  attribute :kind, :string, values: %w[USE REFERENCE]
14
14
  attribute :schema, ModelElement
15
- attribute :items, InterfaceItem, collection: true
15
+ attribute :items, InterfaceItem, collection: true, initialize_empty: true
16
16
  attribute :_class, :string, default: -> { self.class.name }
17
17
 
18
18
  # Registers :items with the tree-walker registry: the position index
@@ -8,8 +8,8 @@ module Expressir
8
8
  include HasRemarks
9
9
 
10
10
  attribute :id, :string
11
- attribute :remarks, :string, collection: true
12
- attribute :remark_items, RemarkItem, collection: true
11
+ attribute :remarks, :string, collection: true, initialize_empty: true
12
+ attribute :remark_items, RemarkItem, collection: true, initialize_empty: true
13
13
  attribute :base_item, ModelElement
14
14
  attribute :_class, :string, default: -> { self.class.name }
15
15
 
@@ -12,15 +12,15 @@ module Expressir
12
12
  :constants, :variables, :statements,
13
13
  :remark_items
14
14
 
15
- attribute :parameters, Parameter, collection: true
16
- attribute :types, ModelElement, collection: true
17
- attribute :entities, Entity, collection: true
18
- attribute :subtype_constraints, SubtypeConstraint, collection: true
19
- attribute :functions, Function, collection: true
20
- attribute :procedures, Procedure, collection: true
21
- attribute :constants, Constant, collection: true
22
- attribute :variables, Variable, collection: true
23
- attribute :statements, ModelElement, collection: true
15
+ attribute :parameters, Parameter, collection: true, initialize_empty: true
16
+ attribute :types, ModelElement, collection: true, initialize_empty: true
17
+ attribute :entities, Entity, collection: true, initialize_empty: true
18
+ attribute :subtype_constraints, SubtypeConstraint, collection: true, initialize_empty: true
19
+ attribute :functions, Function, collection: true, initialize_empty: true
20
+ attribute :procedures, Procedure, collection: true, initialize_empty: true
21
+ attribute :constants, Constant, collection: true, initialize_empty: true
22
+ attribute :variables, Variable, collection: true, initialize_empty: true
23
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
24
24
  attribute :_class, :string, default: -> { self.class.name }
25
25
 
26
26
  key_value do
@@ -7,7 +7,7 @@ module Expressir
7
7
  include HasRemarks
8
8
 
9
9
  attribute :id, :string
10
- attribute :remarks, :string, collection: true
10
+ attribute :remarks, :string, collection: true, initialize_empty: true
11
11
  attribute :_class, :string, default: -> { self.class.name }
12
12
 
13
13
  key_value do
@@ -15,18 +15,18 @@ module Expressir
15
15
  :where_rules, :informal_propositions,
16
16
  :remark_items
17
17
 
18
- attribute :applies_to, ModelElement, collection: true
19
- attribute :types, ModelElement, collection: true
20
- attribute :entities, Entity, collection: true
21
- attribute :subtype_constraints, SubtypeConstraint, collection: true
22
- attribute :functions, Function, collection: true
23
- attribute :procedures, Procedure, collection: true
24
- attribute :constants, Constant, collection: true
25
- attribute :variables, Variable, collection: true
26
- attribute :statements, ModelElement, collection: true
27
- attribute :where_rules, WhereRule, collection: true
18
+ attribute :applies_to, ModelElement, collection: true, initialize_empty: true
19
+ attribute :types, ModelElement, collection: true, initialize_empty: true
20
+ attribute :entities, Entity, collection: true, initialize_empty: true
21
+ attribute :subtype_constraints, SubtypeConstraint, collection: true, initialize_empty: true
22
+ attribute :functions, Function, collection: true, initialize_empty: true
23
+ attribute :procedures, Procedure, collection: true, initialize_empty: true
24
+ attribute :constants, Constant, collection: true, initialize_empty: true
25
+ attribute :variables, Variable, collection: true, initialize_empty: true
26
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
27
+ attribute :where_rules, WhereRule, collection: true, initialize_empty: true
28
28
  attribute :informal_propositions, InformalPropositionRule,
29
- collection: true
29
+ collection: true, initialize_empty: true
30
30
  attribute :_class, :string, default: -> { self.class.name }
31
31
 
32
32
  key_value do
@@ -25,14 +25,14 @@ module Expressir
25
25
 
26
26
  attribute :file, :string
27
27
  attribute :version, SchemaVersion
28
- attribute :interfaces, Interface, collection: true
29
- attribute :constants, Constant, collection: true
30
- attribute :types, ModelElement, collection: true
31
- attribute :entities, Entity, collection: true
32
- attribute :subtype_constraints, SubtypeConstraint, collection: true
33
- attribute :functions, Function, collection: true
34
- attribute :rules, Rule, collection: true
35
- attribute :procedures, Procedure, collection: true
28
+ attribute :interfaces, Interface, collection: true, initialize_empty: true
29
+ attribute :constants, Constant, collection: true, initialize_empty: true
30
+ attribute :types, ModelElement, collection: true, initialize_empty: true
31
+ attribute :entities, Entity, collection: true, initialize_empty: true
32
+ attribute :subtype_constraints, SubtypeConstraint, collection: true, initialize_empty: true
33
+ attribute :functions, Function, collection: true, initialize_empty: true
34
+ attribute :rules, Rule, collection: true, initialize_empty: true
35
+ attribute :procedures, Procedure, collection: true, initialize_empty: true
36
36
  attribute :header, :string
37
37
  attribute :_class, :string, default: -> { self.class.name }
38
38
  attribute :selected, :boolean, default: false
@@ -5,7 +5,7 @@ module Expressir
5
5
  # - section 9.3 Schema
6
6
  class SchemaVersion < ModelElement
7
7
  attribute :value, :string
8
- attribute :items, SchemaVersionItem, collection: true
8
+ attribute :items, SchemaVersionItem, collection: true, initialize_empty: true
9
9
  attribute :_class, :string, default: -> { self.class.name }
10
10
 
11
11
  key_value do
@@ -8,7 +8,7 @@ module Expressir
8
8
 
9
9
  attribute :applies_to, ModelElement
10
10
  attribute :abstract, :boolean
11
- attribute :total_over, ModelElement, collection: true
11
+ attribute :total_over, ModelElement, collection: true, initialize_empty: true
12
12
  attribute :supertype_expression, ModelElement
13
13
  attribute :_class, :string, default: -> { self.class.name }
14
14
 
@@ -13,9 +13,9 @@ module Expressir
13
13
  :remark_items
14
14
 
15
15
  attribute :underlying_type, ModelElement
16
- attribute :where_rules, WhereRule, collection: true
16
+ attribute :where_rules, WhereRule, collection: true, initialize_empty: true
17
17
  attribute :informal_propositions, InformalPropositionRule,
18
- collection: true
18
+ collection: true, initialize_empty: true
19
19
  attribute :_class, :string, default: -> { self.class.name }
20
20
 
21
21
  key_value do
@@ -6,7 +6,7 @@ module Expressir
6
6
  class UniqueRule < ModelElement
7
7
  include Identifier
8
8
 
9
- attribute :attributes, ModelElement, collection: true
9
+ attribute :attributes, ModelElement, collection: true, initialize_empty: true
10
10
  attribute :_class, :string, default: -> { self.class.name }
11
11
 
12
12
  key_value do
@@ -17,7 +17,7 @@ module Expressir
17
17
  collection_attributes :schemas
18
18
 
19
19
  attribute :path, :string
20
- attribute :schemas, Declarations::Schema, collection: true,
20
+ attribute :schemas, Declarations::Schema, collection: true, initialize_empty: true,
21
21
  initialize_empty: true
22
22
  attribute :_class, :string, default: -> { self.class.name }
23
23
 
@@ -5,7 +5,7 @@ module Expressir
5
5
  # - section 12.9 Aggregate initializer
6
6
  class AggregateInitializer < ModelElement
7
7
  child_attributes :items
8
- attribute :items, ModelElement, collection: true
8
+ attribute :items, ModelElement, collection: true, initialize_empty: true
9
9
  attribute :_class, :string, default: -> { self.class.name }
10
10
 
11
11
  key_value do
@@ -6,7 +6,7 @@ module Expressir
6
6
  class EntityConstructor < ModelElement
7
7
  child_attributes :parameters
8
8
  attribute :entity, ModelElement
9
- attribute :parameters, ModelElement, collection: true
9
+ attribute :parameters, ModelElement, collection: true, initialize_empty: true
10
10
  attribute :_class, :string, default: -> { self.class.name }
11
11
 
12
12
  key_value do
@@ -6,7 +6,7 @@ module Expressir
6
6
  class FunctionCall < ModelElement
7
7
  child_attributes :parameters
8
8
  attribute :function, ModelElement
9
- attribute :parameters, ModelElement, collection: true
9
+ attribute :parameters, ModelElement, collection: true, initialize_empty: true
10
10
  attribute :_class, :string, default: -> { self.class.name }
11
11
 
12
12
  key_value do
@@ -8,11 +8,11 @@ module Expressir
8
8
  mod.include(HasRemarks)
9
9
 
10
10
  mod.attribute :id, :string
11
- mod.attribute :remarks, :string, collection: true
11
+ mod.attribute :remarks, :string, collection: true, initialize_empty: true
12
12
  mod.attribute :remark_items,
13
- ::Expressir::Model::Declarations::RemarkItem, collection: true
13
+ ::Expressir::Model::Declarations::RemarkItem, collection: true, initialize_empty: true
14
14
  mod.attribute :untagged_remarks, ::Expressir::Model::RemarkInfo,
15
- collection: true
15
+ collection: true, initialize_empty: true
16
16
 
17
17
  mod.key_value do
18
18
  map "id", to: :id
@@ -75,7 +75,7 @@ module Expressir
75
75
  polymorphic_class: true
76
76
  attribute :source, :string
77
77
  attribute :source_offset, :integer
78
- attribute :untagged_remarks, RemarkInfo, collection: true
78
+ attribute :untagged_remarks, RemarkInfo, collection: true, initialize_empty: true
79
79
 
80
80
  POLYMORPHIC_CLASS_MAP = {
81
81
  "Expressir::Model::Cache" => "Expressir::Model::Cache",
@@ -9,7 +9,7 @@ module Expressir
9
9
 
10
10
  collection_attributes :statements
11
11
  attribute :expression, ModelElement
12
- attribute :statements, ModelElement, collection: true
12
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
13
13
  attribute :_class, :string, default: -> { self.class.name }
14
14
 
15
15
  key_value do
@@ -15,7 +15,7 @@ module Expressir
15
15
 
16
16
  child_attributes :expression
17
17
  attribute :expression, ModelElement
18
- attribute :actions, CaseAction, collection: true
18
+ attribute :actions, CaseAction, collection: true, initialize_empty: true
19
19
  attribute :otherwise_statement, ModelElement
20
20
  attribute :_class, :string, default: -> { self.class.name }
21
21
 
@@ -5,7 +5,7 @@ module Expressir
5
5
  # - section 13.4 Case statement
6
6
  class CaseAction < ModelElement
7
7
  child_attributes :expression
8
- attribute :labels, ModelElement, collection: true
8
+ attribute :labels, ModelElement, collection: true, initialize_empty: true
9
9
  attribute :statement, ModelElement
10
10
  attribute :_class, :string, default: -> { self.class.name }
11
11
 
@@ -7,7 +7,7 @@ module Expressir
7
7
  include Statement
8
8
 
9
9
  collection_attributes :statements
10
- attribute :statements, ModelElement, collection: true
10
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
11
11
  attribute :_class, :string, default: -> { self.class.name }
12
12
 
13
13
  key_value do
@@ -9,8 +9,8 @@ module Expressir
9
9
  collection_attributes :statements, :else_statements
10
10
  child_attributes :expression
11
11
  attribute :expression, ModelElement
12
- attribute :statements, ModelElement, collection: true
13
- attribute :else_statements, ModelElement, collection: true
12
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
13
+ attribute :else_statements, ModelElement, collection: true, initialize_empty: true
14
14
  attribute :_class, :string, default: -> { self.class.name }
15
15
 
16
16
  key_value do
@@ -7,7 +7,7 @@ module Expressir
7
7
  include Statement
8
8
 
9
9
  attribute :procedure, ModelElement
10
- attribute :parameters, ModelElement, collection: true
10
+ attribute :parameters, ModelElement, collection: true, initialize_empty: true
11
11
  attribute :_class, :string, default: -> { self.class.name }
12
12
 
13
13
  key_value do
@@ -14,7 +14,7 @@ module Expressir
14
14
  attribute :increment, ModelElement
15
15
  attribute :while_expression, ModelElement
16
16
  attribute :until_expression, ModelElement
17
- attribute :statements, ModelElement, collection: true
17
+ attribute :statements, ModelElement, collection: true, initialize_empty: true
18
18
  attribute :_class, :string, default: -> { self.class.name }
19
19
 
20
20
  key_value do
@@ -4,7 +4,7 @@ module Expressir
4
4
  # Specified in ISO 10303-11:2004
5
5
  # - section 9.2.5.2 ONEOF
6
6
  class OneofSupertypeExpression < ModelElement
7
- attribute :operands, ModelElement, collection: true
7
+ attribute :operands, ModelElement, collection: true, initialize_empty: true
8
8
  attribute :_class, :string, default: -> { self.class.name }
9
9
 
10
10
  key_value do
@@ -372,6 +372,16 @@ module Expressir
372
372
  # 7.6.3: one accessor element per mapped attribute; OPTIONAL
373
373
  # attributes are minOccurs="0"; aggregates carry their
374
374
  # occurrence bounds.
375
+ #
376
+ # Entity-typed accessors carry the REF form (exp:Entity —
377
+ # content-free, the ref/href IDREF attributes): a P21
378
+ # entity-valued position holds a reference (#N), never inline
379
+ # content, and the full validated form stays on the GLOBAL
380
+ # entity element (substitutionGroup exp:Entity). Typing the
381
+ # accessor with the entity's own complexType demanded that
382
+ # content inside by-reference occurrences — `<Widget ref="id2"/>`
383
+ # could not validate (#565, exposed by leptris 1.9.383 fixing
384
+ # the empty-instance bypass #1860).
375
385
  def accessor_node(doc, schema, prefix, attribute, inline, config = nil,
376
386
  scope = nil)
377
387
  type = attribute.type
@@ -383,11 +393,14 @@ module Expressir
383
393
  node.set_attributes("minOccurs" => "0") if optional
384
394
  case kind
385
395
  when :entity
386
- target = entity_id_of(schema, type)
387
- node.set_attributes("type" => "#{prefix}:#{mapped(target, config, :entity)}")
396
+ # exp:Entity is abstract — an element cannot use it as its
397
+ # type directly (libxml2 rejects the schema); the concrete
398
+ # ref form is an anonymous empty extension carrying the
399
+ # base's ref/href attributes.
400
+ node.add_child(ref_form_node(doc, inline, prefix))
388
401
  when :entity_aggregate
389
402
  node.add_child(aggregate_content_node(doc, schema, prefix, type,
390
- config))
403
+ config, inline: inline))
391
404
  when :aggregate
392
405
  node.add_child(inline_list_node(doc, schema, type, inline))
393
406
  when :select
@@ -415,24 +428,41 @@ module Expressir
415
428
  # Aggregates of entity base: the accessor wraps unqualified
416
429
  # component elements typed by the component entity (9.2.6.1
417
430
  # worked example shape).
418
- def aggregate_content_node(doc, schema, prefix, aggregate, config = nil)
431
+ def aggregate_content_node(doc, schema, prefix, aggregate, config = nil,
432
+ inline: false)
419
433
  base = aggregate.base_type
420
434
  target = entity_id_of(schema, base)
421
435
  name = mapped(target, config, :entity)
422
436
  content = doc.create_element("xs:complexType")
423
437
  sequence = doc.create_element("xs:sequence")
424
438
  component = doc.create_element("xs:element")
439
+ # Aggregate members hold REFERENCES to entity instances
440
+ # (P21 #N ids), so the component element carries the
441
+ # content-free ref form (exp:Entity) — the full validated
442
+ # form stays on the global entity element (#565).
425
443
  component.set_attributes(
426
444
  "name" => name,
427
- "type" => "#{prefix}:#{name}",
428
445
  "minOccurs" => "0",
429
446
  "maxOccurs" => "unbounded",
430
447
  )
448
+ component.add_child(ref_form_node(doc, inline, prefix))
431
449
  sequence.add_child(component)
432
450
  content.add_child(sequence)
433
451
  content
434
452
  end
435
453
 
454
+ # The concrete ref form: an anonymous empty extension of
455
+ # exp:Entity — content-free, carrying the base's ref/href
456
+ # attributes. Entity-valued positions in a P21 instance hold
457
+ # references (#N ids), never inline content (#565).
458
+ def ref_form_node(doc, inline, prefix)
459
+ content = doc.create_element("xs:complexContent")
460
+ extension = doc.create_element("xs:extension")
461
+ extension.set_attributes("base" => exp_name("Entity", inline, prefix))
462
+ content.add_child(extension)
463
+ content
464
+ end
465
+
436
466
  # 7.8: the context-schema specific uos element — in the
437
467
  # exp:uos substitution group, extending the exp:uos carrier,
438
468
  # with a repeating choice over the exp:Entity substitution
@@ -3,6 +3,6 @@ module Expressir
3
3
  # autoload it (Ruby autoload only works for module/class constants, not
4
4
  # for string constants like `Expressir::VERSION`).
5
5
  module Version
6
- VERSION = "2.4.44".freeze
6
+ VERSION = "2.4.46".freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expressir
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.44
4
+ version: 2.4.46
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-10-26 00:00:00.000000000 Z
11
+ date: 2026-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys
@@ -242,6 +242,7 @@ files:
242
242
  - ".github/workflows/codeql.yml"
243
243
  - ".github/workflows/conformance.yml"
244
244
  - ".github/workflows/docs.yml"
245
+ - ".github/workflows/gemspec-floor.yml"
245
246
  - ".github/workflows/links.yml"
246
247
  - ".github/workflows/native-gems.yml"
247
248
  - ".github/workflows/performance-release.yml"