lutaml-model 0.8.103 → 0.8.105

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: 02366b5cf16f9598e6eae1c391d7b21002fe9744cc4d617bf4bb9ffc785141a8
4
- data.tar.gz: b3e2928f228d2aa613e506c6b07ce70a765bb33d1574e61e871fd7f6d226ee43
3
+ metadata.gz: c9d133a4a89e51d9a1ed811bd992faeac4f0a8513e283b46f079707bcc083f7e
4
+ data.tar.gz: e4df7bb98a1f7518bdd9f2194934e91feea5367bbf536f75aa4353a067b65049
5
5
  SHA512:
6
- metadata.gz: '096e5a0302f14999f0f0c33c5c08455cdea460bee145d0a2cbb222d5bf19157f931a8aecd9e99a2232e176db0a7c546b62908a91f851c083aedf75a4cbfb5993'
7
- data.tar.gz: 79a62c6ce918c5ea416eec0a9343a9b52b65f5f9d7e234bcc94beaba817e2658d25ba680215ffaee282a8bfc4ba3f4d4fd2900ecfe5907c36f0ff317ec293028
6
+ metadata.gz: 7040ad58b0a31222207e14245293f8f20eb674180dcd55936de938151f16fc5260615f5ec3244769e4644af0bd6f02c9262e87717cbdff9d578a6b1365bbbb3a
7
+ data.tar.gz: f5d38407f6e5ad1b87bad426f68398a6e1249b57c928aa1a94f0585b144517542f0565c38ad2f53b31bc2c80bdaf63a5425968ba883c9d222508940f9b8b1c17
@@ -59,14 +59,14 @@ module Lutaml
59
59
  # updates so recursive type resolution can populate related keys.
60
60
  #
61
61
  # @param name [Symbol, String, Class] The type name or class to resolve
62
- # @param context [TypeContext] The resolution context
62
+ # @param context [TypeContext, nil] The resolution context
63
63
  # @return [Class] The resolved type class
64
64
  # @raise [UnknownTypeError] If type cannot be resolved
65
65
  def resolve(name, context, materialize: true)
66
66
  return @delegate.resolve(name, context, materialize: false) unless materialize
67
67
 
68
68
  cache_key = if name.is_a?(Class)
69
- [context.id, name]
69
+ [context_id_for(context), name]
70
70
  else
71
71
  build_cache_key(name, context)
72
72
  end
@@ -79,11 +79,11 @@ module Lutaml
79
79
  # Check if a type can be resolved, using cache if available.
80
80
  #
81
81
  # @param name [Symbol, String, Class] The type name or class to check
82
- # @param context [TypeContext] The resolution context
82
+ # @param context [TypeContext, nil] The resolution context
83
83
  # @return [Boolean] true if type can be resolved
84
84
  def resolvable?(name, context)
85
85
  cache_key = if name.is_a?(Class)
86
- [context.id, name]
86
+ [context_id_for(context), name]
87
87
  else
88
88
  build_cache_key(name, context)
89
89
  end
@@ -137,10 +137,17 @@ module Lutaml
137
137
  # Uses array for faster comparison than string concatenation.
138
138
  #
139
139
  # @param name [Symbol, String] The type name
140
- # @param context [TypeContext] The resolution context
140
+ # @param context [TypeContext, nil] The resolution context
141
141
  # @return [Array] The cache key (faster than string)
142
142
  def build_cache_key(name, context)
143
- [context.id, name.to_sym]
143
+ [context_id_for(context), name.to_sym]
144
+ end
145
+
146
+ # A nil context (e.g. a register ID that has no registered type
147
+ # context) is a legitimate "no namespace" resolution; the cache
148
+ # key just forgoes the namespace slot.
149
+ def context_id_for(context)
150
+ context&.id
144
151
  end
145
152
  end
146
153
  end
@@ -44,7 +44,9 @@ module Lutaml
44
44
  # 5. Raise UnknownTypeError if not found anywhere
45
45
  #
46
46
  # @param name [Symbol, String, Class] The type name or class to resolve
47
- # @param context [TypeContext] The resolution context
47
+ # @param context [TypeContext, nil] The resolution context (nil means
48
+ # no primary registry and no fallbacks — an id registered in the
49
+ # Register namespace but not as a type context)
48
50
  # @return [Class] The resolved type class
49
51
  # @raise [UnknownTypeError] If type cannot be resolved
50
52
  #
@@ -66,11 +68,11 @@ module Lutaml
66
68
  type_name = name.to_sym
67
69
 
68
70
  # 1. Check primary registry
69
- type = context.lookup_local(type_name)
71
+ type = context&.lookup_local(type_name)
70
72
  return apply_substitutions(type, context) if type
71
73
 
72
74
  # 2. Check fallback contexts in order
73
- if context.has_fallbacks?
75
+ if context&.has_fallbacks?
74
76
  context.fallback_contexts.each do |fallback_context|
75
77
  type = resolve_from_fallback(type_name, fallback_context)
76
78
  return apply_substitutions(type, context) if type
@@ -97,8 +99,8 @@ module Lutaml
97
99
  # 5. Type not found - raise error
98
100
  raise UnknownTypeError.new(
99
101
  type_name,
100
- context_id: context.id,
101
- available_types: available_type_names(context),
102
+ context_id: context&.id,
103
+ available_types: context ? available_type_names(context) : [],
102
104
  )
103
105
  end
104
106
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.103"
5
+ VERSION = "0.8.105"
6
6
  end
7
7
  end
@@ -20,10 +20,9 @@ module Lutaml
20
20
  raw_xml = xml
21
21
  xml = normalize_xml_for_parse(xml)
22
22
  parsed = parse_with_moxml(xml, parse_encoding)
23
- assert_no_truncated_recovered_parse!(parsed)
24
23
  root_element = parsed.root
25
24
 
26
- raise_empty_document_error if root_element.nil?
25
+ raise_empty_document_error(parsed.parse_errors) if root_element.nil?
27
26
 
28
27
  root = self::PARSED_ELEMENT_CLASS.new(root_element)
29
28
  doc_pis = extract_document_processing_instructions(parsed)
@@ -31,31 +30,6 @@ module Lutaml
31
30
  new(root, parse_encoding, **parse_document_options(raw_xml))
32
31
  end
33
32
 
34
- # Recover-mode parsing must never masquerade input truncation as
35
- # success.
36
- #
37
- # moxml releases before 0.5.84 parsed Nokogiri documents without
38
- # XML_PARSE_HUGE, so libxml2 capped its input buffer at 10 MB;
39
- # longer documents tripped a fatal "Resource limit exceeded:
40
- # Buffer size limit exceeded, try XML_PARSE_HUGE" error mid-input.
41
- # In recover mode Nokogiri records that error on the document and
42
- # returns the partial tree, so every node past the limit silently
43
- # vanishes (lutaml-model#871). A resource-limit fatal means the
44
- # engine stopped early with input left — refuse to deserialize the
45
- # partial document instead of dropping trailing content without a
46
- # trace.
47
- #
48
- # moxml 0.5.84 and later always pass XML_PARSE_HUGE, so the
49
- # Nokogiri path no longer truncates and this guard stays silent;
50
- # it remains for older moxml releases and for any other adapter
51
- # that reports the resource-limit family.
52
- #
53
- # Other recover-mode fatals (e.g. an XML declaration after leading
54
- # whitespace) do not drop content and keep the long-standing
55
- # recover behavior; only the resource-limit family is truncating.
56
- TRUNCATION_FATAL_MARKER =
57
- /Resource limit exceeded|Buffer size limit exceeded/
58
-
59
33
  private
60
34
 
61
35
  def normalize_xml_for_parse(xml)
@@ -88,23 +62,6 @@ module Lutaml
88
62
  end
89
63
  end
90
64
 
91
- def assert_no_truncated_recovered_parse!(parsed)
92
- errors = parsed.parse_errors
93
- return if errors.nil? || errors.empty?
94
-
95
- fatal = errors.find do |message|
96
- message.match?(TRUNCATION_FATAL_MARKER)
97
- end
98
- return if fatal.nil?
99
-
100
- raise Lutaml::Model::InvalidFormatError.new(
101
- :xml,
102
- "the XML engine reported a fatal resource limit and recovered " \
103
- "with a truncated document; refusing to deserialize " \
104
- "partial input (#{fatal})",
105
- )
106
- end
107
-
108
65
  def parse_document_options(xml)
109
66
  {
110
67
  doctype: extract_doctype_from_xml(xml),
@@ -112,8 +69,11 @@ module Lutaml
112
69
  }
113
70
  end
114
71
 
115
- def raise_empty_document_error
72
+ def raise_empty_document_error(parse_errors = nil)
116
73
  message = self::EMPTY_DOCUMENT_ERROR_MESSAGE
74
+ if parse_errors && !parse_errors.empty?
75
+ message += " (the XML engine reported: #{parse_errors.join('; ')})"
76
+ end
117
77
 
118
78
  case self::EMPTY_DOCUMENT_ERROR_TYPE
119
79
  when :parse_exception
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Register ids and type-context ids are two namespaces that
6
+ # GlobalContext#resolve_type bridges implicitly: an id present only in the
7
+ # Register namespace (a flavor's `lutaml_default_register`) resolves to a
8
+ # nil type context, and every resolution shape must treat that as a
9
+ # legitimate "no namespace" input instead of crashing (metanorma-jis#531).
10
+ RSpec.describe "type resolution through a register id with no registered type context" do
11
+ let(:collection_class) do
12
+ Class.new(Lutaml::Model::Collection) do
13
+ instances :entries, :string
14
+ end
15
+ end
16
+
17
+ it "resolves collection instance types under a nil context" do
18
+ collection = collection_class.new(["a"], lutaml_register: :jis_document)
19
+
20
+ expect(collection.entries).to eq(["a"])
21
+ end
22
+
23
+ it "resolves built-in symbol names under a nil context" do
24
+ resolved = Lutaml::Model::GlobalContext.resolve_type(:string, :jis_document)
25
+
26
+ expect(resolved).to eq(Lutaml::Model::Type::String)
27
+ end
28
+
29
+ it "raises UnknownTypeError for unknown names under a nil context" do
30
+ expect do
31
+ Lutaml::Model::GlobalContext.resolve_type(:no_such_type, :jis_document)
32
+ end.to raise_error(Lutaml::Model::UnknownTypeError)
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "lutaml/xml/adapter/leptris_adapter"
5
+ require "lutaml/xml/adapter/nokogiri_adapter"
6
+
7
+ # A failed parse reaches the empty-document guard as a document with no
8
+ # root, indistinguishable from genuinely empty input. The engine's parse
9
+ # diagnostics ride the document (moxml attachments :parse_errors) and
10
+ # must surface in the raised message — otherwise the error names the
11
+ # input's emptiness while the input is neither empty nor the problem
12
+ # (lutaml-model#956).
13
+ RSpec.describe "empty-document errors surface engine parse diagnostics" do
14
+ describe Lutaml::Xml::Adapter::LeptrisAdapter do
15
+ it "appends the engine diagnostic when the parse failed" do
16
+ expect do
17
+ described_class.parse("<root><a>x</b></root>")
18
+ end.to raise_error(Lutaml::Model::InvalidFormatError) do |error|
19
+ expect(error.message).to include("the XML engine reported:")
20
+ expect(error.message).to include("XML parse error at byte 14")
21
+ end
22
+ end
23
+
24
+ it "reports the engine diagnostic for whitespace-only input" do
25
+ expect do
26
+ described_class.parse(" ")
27
+ end.to raise_error(Lutaml::Model::InvalidFormatError, /the XML engine reported:/)
28
+ end
29
+ end
30
+
31
+ describe Lutaml::Xml::Adapter::NokogiriAdapter do
32
+ it "keeps the plain empty-document message when the engine reported nothing" do
33
+ expect do
34
+ described_class.parse("")
35
+ end.to raise_error(Lutaml::Model::InvalidFormatError) do |error|
36
+ expect(error.message).to include("The XML may be empty")
37
+ expect(error.message).not_to include("the XML engine reported:")
38
+ end
39
+ end
40
+ end
41
+ 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.103
4
+ version: 0.8.105
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-11 00:00:00.000000000 Z
11
+ date: 2026-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -1863,6 +1863,7 @@ files:
1863
1863
  - spec/lutaml/model/uninitialized_class_spec.rb
1864
1864
  - spec/lutaml/model/union_attribute_spec.rb
1865
1865
  - spec/lutaml/model/union_spec.rb
1866
+ - spec/lutaml/model/unregistered_context_resolution_spec.rb
1866
1867
  - spec/lutaml/model/utils_spec.rb
1867
1868
  - spec/lutaml/model/validation/concerns/has_issues_spec.rb
1868
1869
  - spec/lutaml/model/validation/context_spec.rb
@@ -1913,6 +1914,7 @@ files:
1913
1914
  - spec/lutaml/turtle/mapping_spec.rb
1914
1915
  - spec/lutaml/turtle/transform_spec.rb
1915
1916
  - spec/lutaml/xml/adapter/base_adapter_regression_spec.rb
1917
+ - spec/lutaml/xml/adapter/empty_document_diagnostics_spec.rb
1916
1918
  - spec/lutaml/xml/adapter/nokogiri_adapter_spec.rb
1917
1919
  - spec/lutaml/xml/adapter/oga_adapter_spec.rb
1918
1920
  - spec/lutaml/xml/adapter/order_spec.rb