lutaml-model 0.8.57 → 0.8.58

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: 6c9021265ddf0425193ec4ce5656b3209e73291534bac83f7863494ca5dd2f55
4
- data.tar.gz: d389a3ad85a1d8b72d953df5cf3ff6b0728625aeabc9ebd6563163ee44374071
3
+ metadata.gz: 9c115914d1bb1d737ad93c4ef3d5ecb9090200d8fd3558309421e23fca5a9171
4
+ data.tar.gz: a017029e28d68c4eda43e5e4a79733b6f31c4258ccfb47b0969dae50fe9f0107
5
5
  SHA512:
6
- metadata.gz: 8580ac7ac155d6d72a008b7f29c1f6e8001b45f1e19235c863d53b8f23bf84cf7953b5bbdd2e1b57f9056e583cb49fad114f9487b82eb331172a88a3b071fcd2
7
- data.tar.gz: c74e408cc7a50c3b7badc51cd3ca3711a54c41dfe0fdedf2f37f3a3232a6ec615a92539befc891b9d3235c0ed873aadc674f58fcc8d542032da03d436baba9a6
6
+ metadata.gz: 5a9f912ee3bdd11d2fe2266b2de6880553fe64ab95cc6891a5184bf067205a9ab76fc61faee3812ded44af10dcc430f5b7aa6acc6051c6dfdcd622c5276127cc
7
+ data.tar.gz: 61b39b97fe5c532f4aa367b2c7c9d5efe5b43acc786bfe192832d3379f8b8f7b253538550478028f676ea7b8c5e31f5dea5cda189374318bcab7ea0f1c441a32
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.57"
5
+ VERSION = "0.8.58"
6
6
  end
7
7
  end
@@ -22,7 +22,7 @@ module Lutaml
22
22
  # hydrate natively against their own nodes
23
23
  def call(model_class, plan, value, parent: nil, node: nil)
24
24
  buckets = node && plan[:needs_nodes] ? element_buckets(node) : nil
25
- attr_kwargs = attributes_kwargs(plan, value)
25
+ attr_kwargs = attributes_kwargs(plan, value, node)
26
26
  child_kwargs, children, delegates =
27
27
  children_kwargs(model_class, plan, value, buckets)
28
28
  plan[:collection_defaults].each do |name|
@@ -53,10 +53,18 @@ module Lutaml
53
53
  Lutaml::Model::Config.default_register
54
54
  end
55
55
 
56
- def attributes_kwargs(plan, value)
56
+ def attributes_kwargs(plan, value, node = nil)
57
57
  kwargs = {}
58
+ sole_claimants = sole_claimant_names(plan)
58
59
  plan[:attr_rows].each do |rule, attr|
59
60
  v = value.attribute(rule.name.to_s)
61
+ if v.nil? && node && sole_claimants.include?(rule.name.to_s)
62
+ # #754/#790 parity: a sole-claimant attribute rule binds
63
+ # any qualification (the interpretive matcher's lenient
64
+ # recovery). The walk captures exact (URI, local) matches
65
+ # only, so the qualified spelling is read off the node.
66
+ v = lenient_node_attribute(node, rule.name.to_s)
67
+ end
60
68
  next if v.nil?
61
69
 
62
70
  v = v.split(rule.delimiter) if rule.delimiter
@@ -331,6 +339,31 @@ module Lutaml
331
339
  Lutaml::Xml::Adapter::LeptrisAdapter.parse(raw).root
332
340
  end
333
341
 
342
+ # Attribute names claimed by exactly one attr row — the only
343
+ # names eligible for #754/#790 lenient binding (multi-claimant
344
+ # names stay exact; per #841 leniency is sole-claimant-only).
345
+ def sole_claimant_names(plan)
346
+ plan[:attr_rows].map { |rule, _attr| rule.name.to_s }
347
+ .tally
348
+ .select { |_n, c| c == 1 }
349
+ .keys
350
+ end
351
+
352
+ # Local-name attribute lookup on the source node, qualified
353
+ # spellings included (w:val matches rule "val").
354
+ def lenient_node_attribute(node, local_name)
355
+ return nil unless node.respond_to?(:attributes)
356
+
357
+ node.attributes.each_value do |attr|
358
+ # Leptris::XML::Attr carries the raw (possibly prefixed)
359
+ # name; strip the prefix for the local-name comparison.
360
+ name = attr.name.to_s
361
+ name = name.split(":").last if name.include?(":")
362
+ return attr.value if name == local_name
363
+ end
364
+ nil
365
+ end
366
+
334
367
  def group_children(value, row_tags = nil)
335
368
  grouped = {}
336
369
  tagged = {}
@@ -713,4 +713,31 @@ RSpec.describe "XML plan fast path" do
713
713
  )
714
714
  expect(plan[:namespaced]).to be(true)
715
715
  end
716
+
717
+ # lutaml-model#850: a sole-claimant attribute rule binds any
718
+ # qualification on the plan path (#754/#790 parity with the
719
+ # interpretive matcher) — the qualified spelling is read off the
720
+ # source node when the exact (URI, local) walk capture misses.
721
+ it "binds qualified spellings to sole-claimant attribute rules" do
722
+ ns = Class.new(Lutaml::Xml::Namespace) do
723
+ uri "http://example.com/w"
724
+ prefix_default "w"
725
+ end
726
+ stub_const("PlanFastPath::W850Ns", ns)
727
+ klass = Class.new(Lutaml::Model::Serializable) do
728
+ attribute :value, :boolean
729
+ xml do
730
+ element "b"
731
+ namespace PlanFastPath::W850Ns
732
+ map_attribute "val", to: :value
733
+ end
734
+ end
735
+ stub_const("PlanFastPath::Sole850", klass)
736
+
737
+ doc = %(<w:b xmlns:w="http://example.com/w" w:val="false"/>)
738
+ expect(klass.from_xml(doc).value).to be(false)
739
+ expect(klass.from_xml(
740
+ %(<b xmlns="http://example.com/w" val="false"/>),
741
+ ).value).to be(false)
742
+ end
716
743
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.57
4
+ version: 0.8.58
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-09-23 00:00:00.000000000 Z
11
+ date: 2026-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64