expressir 2.4.43-arm-linux → 2.4.45-arm-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a74e2934aa91f697a2afab5936c93e0076c99943c2db2e6a96815c04320b43a
4
- data.tar.gz: f7bcc4bd73d8cba5691e777666757d36ac925d9ec7c877ab929921ed9f249047
3
+ metadata.gz: e09c5c927c878e232059b301e116b126589dd35e4ab72bbce563577a9d82b576
4
+ data.tar.gz: f16d568d6767b6ecb2648717108278b7012bd8f367b6bcc9e081d26b8d484457
5
5
  SHA512:
6
- metadata.gz: e3a82ccfc4da4faddf86f670d5da291575c38946b8b321651de46d331dd759428ed88691205f0ff64e20c74ef12458d206b55e13b62db5b676172f15aefd3e47
7
- data.tar.gz: '0628dd39af53af1cce52dfb58ad4cd31cce7947ddadd3cf62d8b04ace16f904084dc8947cd631c9fe3451cc746effcad7e9cd99a42e0a4b5b458493895e3d0ff'
6
+ metadata.gz: 82bc5464f43c819eede4751832c8947d5a22a36f8466aec0a2db0170d7115ea7af7c17214e01b961b0bc63d318cdea82626b687ae3a3f40e497f69488e231f4e
7
+ data.tar.gz: f6361a1d666f0fbff3964f35917fa2a44c1b07e5b26ac717778f69f12c94f552d5c72d411280ccbab148176ea08d1ee29ed9dcf3863a2ac9f8a7a465a9c8e79c
@@ -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
Binary file
Binary file
Binary file
Binary file
@@ -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
@@ -245,7 +245,9 @@ module Expressir
245
245
  "FUNCTION",
246
246
  " ",
247
247
  node.id,
248
- *if node.parameters&.length&.positive?
248
+ # nil parameters = the source had no parentheses;
249
+ # [] = an explicit `f()` — the parens are written back
250
+ *if node.parameters
249
251
  parameter_indent = indent_char * "FUNCTION #{node.id}(".length
250
252
  [
251
253
  "(",
@@ -371,7 +373,9 @@ module Expressir
371
373
  "PROCEDURE",
372
374
  " ",
373
375
  node.id,
374
- *if node.parameters&.length&.positive?
376
+ # see format_declarations_function: nil = no parens,
377
+ # [] = explicit `p()`
378
+ *if node.parameters
375
379
  parameter_indent = indent_char * "PROCEDURE #{node.id}(".length
376
380
  [
377
381
  "(",
@@ -889,6 +889,20 @@ module Expressir
889
889
  end
890
890
  end
891
891
 
892
+ # A prefixed intermediate segment (wr:WR1.ip:IP2.__note) splits
893
+ # wrong under the generic prefix-normalized walk below, so the
894
+ # segment-aware resolution handles that class: resolve the
895
+ # longest owned prefix (honoring the wr:/ip:/ur: prefixes
896
+ # find() strips) and attach the first unresolved segment as the
897
+ # remark item id on that node — `<rule>.wr:WR1.__note` lands a
898
+ # `__note` item on WR1 (#562), not a `wr` item on the rule.
899
+ segments = path.split(".")
900
+ if segments.length > 2 &&
901
+ segments[0..-2].any? { |segment| segment.include?(":") } && (owned = resolve_longest_owned_prefix(model, path, schema_ids))
902
+ parent, item_id = owned
903
+ return create_item_at_parent(parent, item_id)
904
+ end
905
+
892
906
  clean_path = normalize_path(path)
893
907
  parts = clean_path.split(".")
894
908
  return nil if parts.length < 2
@@ -921,6 +935,41 @@ module Expressir
921
935
  "#{path_part}.#{item_suffix}"
922
936
  end
923
937
 
938
+ # Walks the tag's segments from a schema root, honoring the
939
+ # prefix forms find() strips (wr:WR1, ip:IP1, ur:UR1), and
940
+ # returns [node, first_unresolved_segment] for the deepest node
941
+ # the model owns. Nil when nothing resolves.
942
+ def resolve_longest_owned_prefix(model, path, schema_ids)
943
+ candidate_paths = [path]
944
+ schema_ids.each do |schema_id|
945
+ candidate_paths << "#{schema_id}.#{path}"
946
+ end
947
+
948
+ candidate_paths.each do |candidate|
949
+ segments = candidate.split(".")
950
+ next if segments.empty?
951
+
952
+ owned_path = segments.first
953
+ node = safe_find(model, owned_path)
954
+ next unless node
955
+
956
+ segments[1..].each_with_index do |segment, index|
957
+ stepped = safe_find(model, "#{owned_path}.#{segment}")
958
+ # a lazily created remark item is never a path owner
959
+ stepped = nil if stepped.is_a?(Model::Declarations::RemarkItem)
960
+ if stepped.nil?
961
+ return [node, segment] if index.positive?
962
+
963
+ break
964
+ end
965
+ owned_path = "#{owned_path}.#{segment}"
966
+ node = stepped
967
+ end
968
+ end
969
+
970
+ nil
971
+ end
972
+
924
973
  def create_item_at_parent(parent, item_id)
925
974
  # Strip "wr:" prefix convention (e.g. "wr:IP1" → "IP1")
926
975
  ip_id = extract_ip_id(item_id)
@@ -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.43".freeze
6
+ VERSION = "2.4.45".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.43
4
+ version: 2.4.45
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-25 00:00:00.000000000 Z
11
+ date: 2026-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -227,6 +227,7 @@ files:
227
227
  - ".github/workflows/codeql.yml"
228
228
  - ".github/workflows/conformance.yml"
229
229
  - ".github/workflows/docs.yml"
230
+ - ".github/workflows/gemspec-floor.yml"
230
231
  - ".github/workflows/links.yml"
231
232
  - ".github/workflows/native-gems.yml"
232
233
  - ".github/workflows/performance-release.yml"