lutaml-model 0.8.115 → 0.8.117

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: b924b08953808c53f7f6db9f9832ffa870b11a5d2c7fe450b1340d8b2f289562
4
- data.tar.gz: 3ba1f1afe7b2a95e83f137f8d3944e56e0ba516962b261665c4c3b91de74824f
3
+ metadata.gz: 49af9f0bc6f8dd954e93d9d3ac641e212dbf526486c9c8a2eb3460ccfbf01cbf
4
+ data.tar.gz: cf20d264b0827b500c0dde7340b721c5ea89235341a55ef603f3e41dd461a31a
5
5
  SHA512:
6
- metadata.gz: 2d956aca0be2bdbae7e513391962490429057f04ca4371be1477d4bbc29e79f8ad942beffbb40debf27db64f7be7e7c301110832439a3ece8eb4ece5e6c673d9
7
- data.tar.gz: 7a384e7b06e927f9b78a129914de369c2befa23b14e84c6a9c2ba24e769c145dd0b0814fa3f92483516132020ced34c885c71872c251d41a8bdc6dff995b41db
6
+ metadata.gz: 62359095df53c8835748e08c60c8648f6e07a4e892536da699ded2f255ec176a8aee3e648012d27e62909af5e34426118f465d93adf616ed005e22273e990fbc
7
+ data.tar.gz: 7e1d1f54a24252a44a31e2a0d45e878c13b9a5d44115a856aafd383cf24adb731ad585d52b8a8d9d648557ee150e2c0b776a1edd18b405bf16c79e76fbf4c2a8
@@ -977,6 +977,24 @@ instance_object = nil)
977
977
  end
978
978
 
979
979
  klass = resolve_polymorphic_class(resolved_type, value, options)
980
+ # A scalar handed to a mapped-model attribute crashed
981
+ # apply_mappings with an internal NoMethodError (key? on
982
+ # String, #978). A type declaring its own .cast owns scalar
983
+ # construction; base-cast identity types fall through to the
984
+ # legacy conversion chain, whose parse error names the token.
985
+ if serializable_type?(register) && !value.nil? &&
986
+ !value.is_a?(::Hash) && !value.is_a?(::Array) &&
987
+ !value.respond_to?(:key?) && klass.respond_to?(:cast) &&
988
+ klass.method(:cast).owner == klass.singleton_class
989
+ casted = klass.cast(value)
990
+ return casted if casted.is_a?(klass)
991
+
992
+ raise Lutaml::Model::IncorrectModelError,
993
+ "attribute '#{name}': the #{format} input for type " \
994
+ "#{klass} is a #{value.class}, and #{klass}.cast did " \
995
+ "not build one (received #{casted.class})"
996
+ end
997
+
980
998
  if can_serialize?(klass, value, format, options)
981
999
  propagated = Serialize::DeserializationContext.propagate(options)
982
1000
  klass.apply_mappings(value, format,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.115"
5
+ VERSION = "0.8.117"
6
6
  end
7
7
  end
@@ -485,8 +485,12 @@ module Lutaml
485
485
  # namespace). Contested names dispatch strictly by
486
486
  # (URI, local), falling back to URI-less rows.
487
487
  name = object.name
488
+ # Order entries may carry names with no mapped row (the
489
+ # parse records every child in document order); the
490
+ # layout cache kills its default proc to stay
491
+ # mutation-free, so a miss reads nil and drops below.
488
492
  keys = name_keys[name]
489
- if keys.length == 1
493
+ if keys&.length == 1
490
494
  dispatch_key = keys.first
491
495
  entry = rows_by_name[dispatch_key]
492
496
  else
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # #978: a JSON scalar handed to a mapped-model attribute crashed with
6
+ # an internal NoMethodError (key? on String) wrapped as
7
+ # InvalidFormatError. A type that declares its own .cast owns scalar
8
+ # construction from that scalar; a cast-less type falls through to the
9
+ # legacy conversion chain, whose parse error surfaces the offending
10
+ # token — the remedy there is declaring .cast on the type.
11
+ RSpec.describe "from_json scalar into a mapped-model attribute" do
12
+ let(:marked_class) do
13
+ Class.new(Lutaml::Model::Serializable) do
14
+ attribute :content, :string
15
+
16
+ key_value do
17
+ map "content", to: :content
18
+ end
19
+
20
+ def self.cast(value)
21
+ return value if value.is_a?(self)
22
+
23
+ new(content: value.to_s)
24
+ end
25
+ end
26
+ end
27
+
28
+ let(:plain_class) do
29
+ Class.new(Lutaml::Model::Serializable) do
30
+ attribute :content, :string
31
+
32
+ key_value do
33
+ map "content", to: :content
34
+ end
35
+ end
36
+ end
37
+
38
+ let(:holder_class) do
39
+ Class.new(Lutaml::Model::Serializable) do
40
+ attribute :description, "ScalarMarked"
41
+ attribute :plain_desc, "ScalarPlain"
42
+
43
+ key_value do
44
+ map "description", to: :description
45
+ map "plain_desc", to: :plain_desc
46
+ end
47
+ end
48
+ end
49
+
50
+ before do
51
+ stub_const("ScalarMarked", marked_class)
52
+ stub_const("ScalarPlain", plain_class)
53
+ stub_const("ScalarHolder", holder_class)
54
+ end
55
+
56
+ it "constructs the mapped value through the type's .cast" do
57
+ model = ScalarHolder.from_json('{"description": "text"}')
58
+
59
+ expect(model.description).to be_a(ScalarMarked)
60
+ expect(model.description.content).to eq("text")
61
+ end
62
+
63
+ it "falls through to the format parse error for a cast-less mapped type" do
64
+ expect { ScalarHolder.from_json('{"plain_desc": "text"}') }
65
+ .to raise_error(
66
+ Lutaml::Model::InvalidFormatError,
67
+ /unexpected token 'text'/,
68
+ )
69
+ end
70
+ end
@@ -516,6 +516,28 @@ RSpec.describe "XML plan fast path" do
516
516
  expect(fast_out).to include(%(<para kind="a">Hi <emph>there</emph> bye <emph>again</emph>!</para>))
517
517
  end
518
518
 
519
+ it "drops order entries whose names have no mapped row" do
520
+ klass = Class.new(Lutaml::Model::Serializable) do
521
+ attribute :a, :string, collection: true
522
+
523
+ xml do
524
+ element "d"
525
+ ordered
526
+ map_element "a", to: :a
527
+ end
528
+ end
529
+ xml = %(<d><a>1</a><note>extra</note><a>2</a></d>)
530
+
531
+ Lutaml::Model::Config.instance.xml_plan_fast_path = false
532
+ interp = klass.from_xml(xml).to_xml
533
+ Lutaml::Model::Config.instance.xml_plan_fast_path = true
534
+ fast_out = klass.from_xml(xml).to_xml
535
+ Lutaml::Model::Config.instance.xml_plan_fast_path = false
536
+
537
+ expect(fast_out).to eq(interp)
538
+ expect(fast_out).to eq("<d>\n <a>1</a>\n <a>2</a>\n</d>")
539
+ end
540
+
519
541
  it "reflects content mutations through the ordered fast serializer" do
520
542
  para = Class.new(Lutaml::Model::Serializable) do
521
543
  attribute :emph, :string, collection: true
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.115
4
+ version: 0.8.117
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -1750,6 +1750,7 @@ files:
1750
1750
  - spec/lutaml/model/enum_spec.rb
1751
1751
  - spec/lutaml/model/except_spec.rb
1752
1752
  - spec/lutaml/model/finalization_cache_spec.rb
1753
+ - spec/lutaml/model/from_json_scalar_cast_spec.rb
1753
1754
  - spec/lutaml/model/generator_defaults_spec.rb
1754
1755
  - spec/lutaml/model/global_context_spec.rb
1755
1756
  - spec/lutaml/model/global_register_spec.rb