lutaml-model 0.8.108 → 0.8.110

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: 50f81556cebfd601f3ff6ede463ed9f20d31a0e2c12e8c1362c655b5dd9408e8
4
- data.tar.gz: c9ec847109db4b87bbb4e4e46fde663eb83d7b9eae54d43f55e1c04024345013
3
+ metadata.gz: a49c2cdf5702fe7400d2cc4439632340cf8abe4ed214219d9d53a3bbacff6943
4
+ data.tar.gz: 887431c911fa16aff6372c9c7c7addb034d0722f1ba11b0dfd28fb71bb4fb982
5
5
  SHA512:
6
- metadata.gz: cbce6698e13f0d52da843a92c4bae8ca69db7f11cdbd654f1dd924cd171f31ad227da0a610557a17de11856de2030460d5b497a248c3b37b7de76ca286398520
7
- data.tar.gz: d4113a27b55316ad49f5a9263ac3b1d15616c3c7c65408faa1b0a4741ed1966fcd173a8519d85b19dec66a406ce168caf6439703b5e687318ae74dc586ee586d
6
+ metadata.gz: f9c79f442e8be909743b4ecd1eaca9537d61be0d5925336507aa1869907c6795d68aac1a66b67c3fb2cedc0dab86bcab798b7850c3c41d7393a1f9301b862104
7
+ data.tar.gz: d03289efc62a1b6c3cd9c66b6114879a3e68dc05883f20aab395264c2db7c7f0487da4baf2cce95d40493485f238e914b9acb88d974b7638e54efca43f678a30
@@ -398,11 +398,27 @@ module Lutaml
398
398
  # cast_items already returns a fresh array, so the plain-Array
399
399
  # collection needs no re-wrap — build_collection's
400
400
  # collection_class.new would copy it again.
401
+ assert_flat_collection_items!(items)
401
402
  return items if !custom_collection? && collection_class == ::Array
402
403
 
403
404
  build_collection(items, register: register)
404
405
  end
405
406
 
407
+ # Fail loudly on nested arrays. A collection's declared element
408
+ # type never admits an Array as an item; the legacy
409
+ # build_collection flatten silently absorbed mis-spliced builder
410
+ # output (expressir#506 surfaced it three frames away in a
411
+ # formatter). The error must land at the assignment boundary.
412
+ def assert_flat_collection_items!(items)
413
+ return unless items.any?(::Array)
414
+
415
+ raise TypeError,
416
+ "collection '#{name}': item " \
417
+ "#{items.index { |item| item.is_a?(::Array) }} is an Array " \
418
+ "where the declared element type is expected; nested arrays " \
419
+ "are not flattened — splice list results before assignment"
420
+ end
421
+
406
422
  # Plain Arrays count as list input. On singular attributes
407
423
  # collection_instance? matches Arrays too (their collection class is
408
424
  # Array), preserving the Array so strict cardinality can flag it.
@@ -37,11 +37,11 @@ module Lutaml
37
37
  @conversion_cache = nil
38
38
  @configured = false
39
39
  @xml_plan_fast_path = true
40
- # Opt-in until leptris#1743 lands: the native ordered lane
41
- # runs away (28+ CPU-min / 14GB RSS on sts-ruby's
42
- # ISO_13849-1 document) — flipping this on by default regressed
43
- # the sts bench cell from 4m41s to a 10-minute kill.
44
- @xml_plan_ordered_path = false
40
+ # Opt-in until leptris#1743 lands: the native ordered lane
41
+ # runs away (28+ CPU-min / 14GB RSS on sts-ruby's
42
+ # ISO_13849-1 document) — flipping this on by default regressed
43
+ # the sts bench cell from 4m41s to a 10-minute kill.
44
+ @xml_plan_ordered_path = false
45
45
  @element_order_tracking = true
46
46
  end
47
47
 
@@ -139,7 +139,13 @@ module Lutaml
139
139
  plan = Lutaml::Xml::Leptris::PlanCompiler.compile(self, register)
140
140
  return nil unless plan
141
141
 
142
- root = ::Leptris::XML.parse(data.to_s).root
142
+ # strict: the from_xml contract raises on malformed input —
143
+ # the FFI lane parses leniently by default and would answer a
144
+ # best-effort tree where every other adapter raises. The
145
+ # kwarg spellings vary by engine vintage (strict: landed
146
+ # after 1.9.353, recover: on 1.9.344+), so probe the resolved
147
+ # parse method and pass the strictest spelling it accepts.
148
+ root = ::Leptris::XML.parse(data.to_s, **leptris_strict_kwargs).root
143
149
  return nil if root.nil?
144
150
  return nil unless root.name == plan[:tree][:name]
145
151
 
@@ -154,6 +160,15 @@ module Lutaml
154
160
  raise Lutaml::Model::InvalidFormatError.new(:xml, e.message)
155
161
  end
156
162
 
163
+ def leptris_strict_kwargs
164
+ params = ::Leptris::XML.method(:parse).parameters
165
+ .map { |_, key| key }
166
+ kwargs = {}
167
+ kwargs[:recover] = false if params.include?(:recover)
168
+ kwargs[:strict] = true if params.include?(:strict)
169
+ kwargs
170
+ end
171
+
157
172
  # Get list of error types that can be raised during format parsing.
158
173
  # Core errors are always included; format-specific errors come from
159
174
  # FormatRegistry registrations.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.108"
5
+ VERSION = "0.8.110"
6
6
  end
7
7
  end
@@ -9,6 +9,7 @@ module Lutaml
9
9
  MOXML_ADAPTER = Moxml::Adapter::Leptris
10
10
  BUILDER_CLASS = Builder::Leptris
11
11
  PARSED_ELEMENT_CLASS = Lutaml::Xml::LeptrisElement
12
+ PARSE_ERROR_CLASS = Moxml::ParseError
12
13
  end
13
14
  end
14
15
  end
@@ -49,13 +49,13 @@ RSpec.describe Lutaml::Model::AdapterResolver, ".adapter_for with a configured t
49
49
  _stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-I", "lib", "-e", probe)
50
50
  expect(status).to be_success, stderr
51
51
  end
52
- end
53
52
 
54
- RSpec.describe "plan lane defaults" do
55
- it "ships the ordered plan lane opt-in until leptris#1743 is fixed" do
56
- # The native ordered lane runs away on sts-ruby's ISO_13849-1
57
- # document (leptris#1743): 28+ CPU-min / 14 GB RSS vs 6.36s on the
58
- # same parse with the lane off. Opt-in until the engine fix ships.
59
- expect(Lutaml::Model::Config.instance.xml_plan_ordered_path).to be(false)
53
+ describe "plan lane defaults" do
54
+ it "ships the ordered plan lane opt-in until leptris#1743 is fixed" do
55
+ # The native ordered lane runs away on sts-ruby's ISO_13849-1
56
+ # document (leptris#1743): 28+ CPU-min / 14 GB RSS vs 6.36s on the
57
+ # same parse with the lane off. Opt-in until the engine fix ships.
58
+ expect(Lutaml::Model::Config.instance.xml_plan_ordered_path).to be(false)
59
+ end
60
60
  end
61
61
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "strict collection assignment" do
6
+ let(:node_class) do
7
+ Class.new(Lutaml::Model::Serializable) do
8
+ attribute :id, :string
9
+ end
10
+ end
11
+
12
+ let(:holder_class) do
13
+ Class.new(Lutaml::Model::Serializable) do
14
+ attribute :items, "Node", collection: true
15
+ end
16
+ end
17
+
18
+ before do
19
+ stub_const("Node", node_class)
20
+ stub_const("Holder", holder_class)
21
+ end
22
+
23
+ it "raises at the constructor boundary" do
24
+ expect do
25
+ Holder.new(items: [[Node.new(id: "a")], Node.new(id: "b")])
26
+ end.to raise_error(Lutaml::Model::TypeError, /item 0 is an Array/)
27
+ end
28
+
29
+ it "raises at the writer boundary" do
30
+ holder = Holder.new
31
+ expect do
32
+ holder.items = [Node.new, [Node.new]]
33
+ end.to raise_error(Lutaml::Model::TypeError, /item 1 is an Array/)
34
+ end
35
+
36
+ it "raises for a custom collection class" do
37
+ custom = Class.new(Lutaml::Model::Collection) do
38
+ instances :node, "Node"
39
+ end
40
+ stub_const("NodeBag", custom)
41
+ wrapper = Class.new(Lutaml::Model::Serializable) do
42
+ attribute :bag, "NodeBag"
43
+ end
44
+ stub_const("BagHolder", wrapper)
45
+
46
+ expect do
47
+ BagHolder.new(bag: [[Node.new]])
48
+ end.to raise_error(Lutaml::Model::TypeError, /nested arrays are not flattened/)
49
+ end
50
+
51
+ it "keeps flat assignment untouched" do
52
+ holder = Holder.new(items: [Node.new(id: "x")])
53
+ expect(holder.items.map(&:id)).to eq(["x"])
54
+ end
55
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.108
4
+ version: 0.8.110
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -1724,6 +1724,7 @@ files:
1724
1724
  - spec/lutaml/model/collection_single_value_coercion_spec.rb
1725
1725
  - spec/lutaml/model/collection_spec.rb
1726
1726
  - spec/lutaml/model/collection_state_nesting_spec.rb
1727
+ - spec/lutaml/model/collection_strict_assignment_spec.rb
1727
1728
  - spec/lutaml/model/collection_validation_spec.rb
1728
1729
  - spec/lutaml/model/comparable_model_spec.rb
1729
1730
  - spec/lutaml/model/comparison_family_spec.rb