lutaml-model 0.8.37 → 0.8.39

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.
@@ -12,6 +12,20 @@ module Lutaml
12
12
  # - Used by model's serialization pipeline via Transform.for(:xml)
13
13
  #
14
14
  class ModelTransform < ::Lutaml::Model::Transform
15
+ require "cgi" unless Lutaml::Model.opal?
16
+
17
+ # Per (model class, register): compiled records for the mapping
18
+ # rules — attribute, derived/valid flags, and #765 group-skip —
19
+ # keyed on the mapping's finalize version so late changes rebuild.
20
+ # The loop body used to re-derive these per parsed instance.
21
+ # Concurrent::Map under threaded MRI, plain Hash under Opal
22
+ # (Concurrent is unavailable there); class identity keys either.
23
+ RULE_RECORDS = if Lutaml::Model.opal?
24
+ {}
25
+ else
26
+ Concurrent::Map.new # rubocop:disable Lint/HashCompareByIdentity
27
+ end
28
+
15
29
  # Namespaced rule name -> [local_name, rule_uri]. Pure string
16
30
  # splitting, deterministic per spelling, shared across parses.
17
31
  # Concurrent::Map under threaded MRI, plain Hash under Opal.
@@ -36,7 +50,19 @@ module Lutaml
36
50
 
37
51
  instance_is_serialize = model_class.include?(::Lutaml::Model::Serialize)
38
52
  if instance_is_serialize
53
+ # TODO.max-perf/17: the same child element was being re-hydrated
54
+ # per matching rule (593k instantiations for 12k elements on the
55
+ # ISO-13849 profile). Memoize one instance per (element, class)
56
+ # for the whole parse; the memo travels in the propagated
57
+ # deserialization options.
58
+ memo = options[:__lutaml_child_memo] ||= {}
59
+ memo_key = [data.object_id, model_class.object_id]
60
+ if (existing = memo[memo_key])
61
+ return existing
62
+ end
63
+
39
64
  instance = model_class.allocate_for_deserialization(child_register)
65
+ memo[memo_key] = instance
40
66
  else
41
67
  instance = model_class.new
42
68
  register_accessor_methods_for(instance, child_register)
@@ -281,24 +307,33 @@ module Lutaml
281
307
  effective_register,
282
308
  )
283
309
 
310
+ serializable = instance.class.include?(Lutaml::Model::Serialize)
311
+ records = if serializable
312
+ compiled_rule_records(instance.class, effective_register,
313
+ grouped_plain_rules)
314
+ end
284
315
  mappings.each do |rule|
285
- # Performance: Cache rule properties accessed multiple times
286
- rule.name
316
+ record = records&.[](rule)
287
317
  rule_to = rule.to
288
- rule_namespace_set = rule.namespace_set?
289
- rule_namespace_set ? rule.namespace_param : nil
290
318
 
291
- attr = attribute_for_rule(rule)
292
- next if attr&.derived?
319
+ if record
320
+ attr = record.attr
321
+ next if record.derived
322
+ next if record.group_skip
293
323
 
294
- if (group = grouped_plain_rules[rule_to]) &&
295
- group.size > 1 && attr&.collection? && !group.first.equal?(rule)
296
- next
297
- end
324
+ raise "Attribute '#{rule_to}' not found in #{context}" unless record.valid
325
+ else
326
+ attr = attribute_for_rule(rule)
327
+ next if attr&.derived?
298
328
 
299
- raise "Attribute '#{rule_to}' not found in #{context}" unless valid_rule?(
300
- rule, attr
301
- )
329
+ group = grouped_plain_rules[rule_to]
330
+ next if group && group.size > 1 && attr&.collection? &&
331
+ !group.first.equal?(rule)
332
+
333
+ raise "Attribute '#{rule_to}' not found in #{context}" unless valid_rule?(
334
+ rule, attr
335
+ )
336
+ end
302
337
 
303
338
  # Performance: Use pre-computed frozen Hash for static namespace overrides.
304
339
  # Falls back to per-element hash only for the dynamic namespace_uri case.
@@ -371,6 +406,9 @@ module Lutaml
371
406
  value = apply_value_map(value, from_map, attr)
372
407
  value = normalize_xml_value(value, rule, attr, new_opts,
373
408
  effective_register)
409
+ value = decode_html_entities_value(value, decode_html_entities_for(
410
+ instance, effective_register
411
+ ))
374
412
  value = rule.transform_value(attr, value, :from, :xml)
375
413
  # An over-count is normally left for `.validate` to report. A mapped
376
414
  # PORO has no `.validate`, so deferring there would discard the
@@ -379,7 +417,8 @@ module Lutaml
379
417
  !rule.content_mapping?
380
418
  attr.valid_collection!(value, context)
381
419
  end
382
- rule.deserialize(instance, value, attributes, context)
420
+ rule.deserialize(instance, value, attributes, context,
421
+ options[:context])
383
422
 
384
423
  instance.value_set_for(rule_to)
385
424
  end
@@ -512,11 +551,13 @@ _effective_register)
512
551
  # its result array entirely on that path.
513
552
  if rule_names.size == 1
514
553
  rn = rule_names[0]
515
- converted = convert_rule_name_to_attribute_name(doc, rn)
554
+ converted = convert_rule_name_to_attribute_name(doc, rn,
555
+ session: session)
516
556
  value = doc.root.find_attribute_value(converted || rn)
517
557
  else
518
558
  attribute_names = rule_names.map do |rn|
519
- converted = convert_rule_name_to_attribute_name(doc, rn)
559
+ converted = convert_rule_name_to_attribute_name(doc, rn,
560
+ session: session)
520
561
  converted || rn
521
562
  end
522
563
 
@@ -545,7 +586,7 @@ _effective_register)
545
586
  # @param doc [XmlElement] the parsed XML document root
546
587
  # @param rule_name [String] the rule name (e.g., "urn:...:ext", "http://.../ns:val", "my-ns:val")
547
588
  # @return [String, nil] the attribute name to look up, or nil if no conversion needed
548
- def convert_rule_name_to_attribute_name(doc, rule_name)
589
+ def convert_rule_name_to_attribute_name(doc, rule_name, session: nil)
549
590
  return nil unless rule_name.include?(":")
550
591
 
551
592
  # URI-vs-prefix detection works on the whole rule name: the
@@ -564,13 +605,27 @@ _effective_register)
564
605
  # match, and unprefixed_name is not called per attribute
565
606
  # (it splits prefixed names and allocates).
566
607
  local_name = rule_name[(last_colon_index + 1)..]
567
- doc.root.attributes.each_value.find do |attr|
568
- ns = attr.namespace
569
- next false if ns.nil? || ns.length > last_colon_index
570
- next false unless rule_name.start_with?(ns) && rule_name[ns.length] == ":"
608
+ if session
609
+ candidates = session.element_local_attribute_index(doc.root)[local_name]
610
+ # No candidate carries this local name: nothing in the
611
+ # element can match, the scan is skipped entirely.
612
+ matched = candidates&.find do |attr|
613
+ ns = attr.namespace
614
+ next false if ns.nil? || ns.length > last_colon_index
615
+ next false unless rule_name.start_with?(ns) && rule_name[ns.length] == ":"
616
+
617
+ attr.unprefixed_name == local_name
618
+ end
619
+ else
620
+ matched = doc.root.attributes.each_value.find do |attr|
621
+ ns = attr.namespace
622
+ next false if ns.nil? || ns.length > last_colon_index
623
+ next false unless rule_name.start_with?(ns) && rule_name[ns.length] == ":"
571
624
 
572
- attr.unprefixed_name == local_name
573
- end&.name
625
+ attr.unprefixed_name == local_name
626
+ end
627
+ end
628
+ matched&.name
574
629
  else
575
630
  # Simple prefix format: look up the actual prefix from document's namespace declarations
576
631
  # The namespace_part is the namespace URI declared in the document (e.g., "my-ns").
@@ -594,39 +649,46 @@ _effective_register)
594
649
  # @return [String, nil] the attribute value or nil
595
650
  def find_attribute_by_local_name(doc, rule_names,
596
651
  flexible_local: false, session: nil)
597
- root_index = session&.root_local_attribute_index
598
-
599
- rule_names.each do |rn|
600
- next unless rn.include?(":")
601
-
602
- # Splitting a namespaced rule name is pure string work repeated
603
- # per element; memoized per spelling (bounded by the model's
604
- # distinct rule names).
605
- parts = NAMESPACED_NAME_PARTS[rn]
606
- unless parts
607
- last_colon_index = rn.rindex(":")
608
- parts = [rn[(last_colon_index + 1)..], rn[0...last_colon_index]]
609
- NAMESPACED_NAME_PARTS[rn] = parts
610
- end
611
- local_name, rule_uri = parts
652
+ root_index = session&.element_local_attribute_index(doc.root)
612
653
 
613
- candidates = root_index&.[](local_name)
614
- matched_attr = if candidates
615
- candidates.find do |attr|
616
- local_name_match?(attr, local_name, rule_uri,
617
- flexible_local)
618
- end
619
- else
620
- doc.root.attributes.each_value.find do |attr|
621
- local_name_match?(attr, local_name, rule_uri,
622
- flexible_local)
623
- end
624
- end
625
- return matched_attr&.value if matched_attr
654
+ rule_names.each do |rule_name|
655
+ next unless rule_name.include?(":")
656
+
657
+ resolved = match_attribute_by_local_name(
658
+ doc, rule_name, root_index, flexible_local
659
+ )
660
+ return resolved if resolved
626
661
  end
627
662
  nil
628
663
  end
629
664
 
665
+ def match_attribute_by_local_name(doc, rule_name, root_index, flexible_local)
666
+ # Splitting a namespaced rule name is pure string work repeated
667
+ # per element; memoized per spelling (bounded by the model's
668
+ # distinct rule names).
669
+ parts = NAMESPACED_NAME_PARTS[rule_name]
670
+ unless parts
671
+ last_colon_index = rule_name.rindex(":")
672
+ parts = [rule_name[(last_colon_index + 1)..], rule_name[0...last_colon_index]]
673
+ NAMESPACED_NAME_PARTS[rule_name] = parts
674
+ end
675
+ local_name, rule_uri = parts
676
+
677
+ candidates = root_index&.[](local_name)
678
+ matched_attr = if candidates
679
+ candidates.find do |attr|
680
+ local_name_match?(attr, local_name, rule_uri,
681
+ flexible_local)
682
+ end
683
+ else
684
+ doc.root.attributes.each_value.find do |attr|
685
+ local_name_match?(attr, local_name, rule_uri,
686
+ flexible_local)
687
+ end
688
+ end
689
+ matched_attr&.value
690
+ end
691
+
630
692
  # Namespace discipline of the lenient local-name fallback
631
693
  # (#744/#758/#790): sole claimants bind any qualification; shared
632
694
  # names must resolve to the rule's namespace.
@@ -648,6 +710,68 @@ _effective_register)
648
710
  (colon = attr.name.rindex(":")) ? attr.name[(colon + 1)..] : attr.name
649
711
  end
650
712
 
713
+ # lutaml-model#154: decode HTML entities for mappings that opted in
714
+ # (`html_entities` in the xml block). Values stay untouched
715
+ # otherwise — XML keeps undefined entities literal by design.
716
+ def decode_html_entities_value(value, enabled)
717
+ return value if value.nil? || !value.is_a?(String)
718
+ return value unless enabled
719
+ return value if Lutaml::Model.opal?
720
+
721
+ Lutaml::Xml::HtmlEntities.decode(value)
722
+ end
723
+
724
+ Record = Struct.new(:attr, :derived, :group_skip, :valid)
725
+
726
+ def compiled_rule_records(model_class, register, grouped_plain_rules)
727
+ return {}.compare_by_identity unless model_class.include?(Lutaml::Model::Serialize)
728
+
729
+ mapping = model_class.mappings_for(:xml, register)
730
+ version = mapping&.rule_records_version.to_i
731
+ per_class = RULE_RECORDS[model_class.object_id] # rubocop:disable Lint/HashCompareByIdentity
732
+ unless per_class
733
+ per_class = Lutaml::Model.opal? ? {} : Concurrent::Map.new
734
+ RULE_RECORDS[model_class.object_id] = per_class # rubocop:disable Lint/HashCompareByIdentity
735
+ end
736
+ cached = per_class[register]
737
+ return cached.records if cached && cached.version == version
738
+
739
+ records = {}.compare_by_identity
740
+ mapping&.mappings(register)&.each do |rule| # rubocop:disable Style/SafeNavigationChain,Style/SafeNavigation
741
+ attr = attribute_for_rule_static(model_class, rule, register)
742
+ group = grouped_plain_rules[rule.to]
743
+ records[rule] = Record.new(
744
+ attr,
745
+ attr&.derived? || false,
746
+ !!(group && group.size > 1 && attr&.collection? &&
747
+ !group.first.equal?(rule)),
748
+ !!(attr || rule.custom_methods[:from]),
749
+ )
750
+ end
751
+ entry = Struct.new(:version, :records).new(version, records)
752
+ per_class[register] = entry
753
+ records
754
+ end
755
+
756
+ def attribute_for_rule_static(model_class, rule, register)
757
+ if rule.delegate
758
+ delegate_attr = model_class.attributes(register)[rule.delegate]
759
+ delegate_type = delegate_attr&.type(register)
760
+ delegate_type&.attributes(register)&.[](rule.to) # rubocop:disable Style/SafeNavigationChain
761
+ else
762
+ model_class.attributes(register)[rule.to]
763
+ end
764
+ rescue StandardError
765
+ nil
766
+ end
767
+
768
+ def decode_html_entities_for(instance, register)
769
+ klass = instance.class
770
+ return false unless klass.is_a?(Class) && klass.include?(Lutaml::Model::Serialize)
771
+
772
+ klass.mappings_for(:xml, register)&.decode_html_entities?
773
+ end
774
+
651
775
  def value_for_rule(session, rule, options, cached_attr = nil,
652
776
  extra_rule_names = nil)
653
777
  doc = session.doc
@@ -682,12 +806,16 @@ _effective_register)
682
806
  rule_names = rule_names.concat(extra_rule_names).uniq if extra_rule_names
683
807
 
684
808
  if rule.attribute?
685
- return value_for_xml_attribute(
809
+ value = value_for_xml_attribute(
686
810
  doc, rule, rule_names,
687
811
  flexible_local: attribute_rule_sole_local_claimant?(
688
812
  instance, rule, effective_register
689
- )
813
+ ),
814
+ session: session
690
815
  )
816
+ return decode_html_entities_value(value, decode_html_entities_for(
817
+ instance, effective_register
818
+ ))
691
819
  end
692
820
 
693
821
  # Performance: Pre-compute type-related values used in the hot loop
@@ -24,18 +24,31 @@ module Lutaml
24
24
  @instance_is_serialize ||= instance.is_a?(::Lutaml::Model::Serialize)
25
25
  end
26
26
 
27
- # Local-name -> [attributes] index over the root element's
28
- # attributes, built on first lenient lookup (TODO.max-perf/08).
29
- # The fallback used to rescan every attribute per missed rule;
30
- # elements are parse-frozen, so the index builds at most once per
31
- # session. Each attribute is filed under every local-name spelling
32
- # the matcher compares (unprefixed name and colon-split local).
33
- def root_local_attribute_index
34
- @root_local_attribute_index ||= begin
35
- index = ::Hash.new { |h, k| h[k] = [] }
36
- doc.root.attributes.each_value do |attr|
27
+ # (element, rule name) -> resolved attribute name / matched
28
+ # attribute VALUE for the lenient lookup paths. Both are pure
29
+ # functions of the element and the name; ISO-13849-shaped
30
+ # documents repeat the same resolutions per rule application, so
31
+ # the computed answer is memoized per element for the parse.
32
+ def rule_name_resolution
33
+ @rule_name_resolution ||= {}
34
+ end
35
+
36
+ # Local-name -> [attributes] index over one element's attributes,
37
+ # built on first lenient lookup (TODO.max-perf/08) and memoized per
38
+ # element for the life of the session. The URI-form attribute
39
+ # conversion and the lenient fallback used to rescan every
40
+ # attribute per rule per element (the ISO-13849 pathology);
41
+ # elements are parse-frozen, so each index builds at most once.
42
+ def element_local_attribute_index(element)
43
+ @element_local_attribute_index ||= {}.compare_by_identity
44
+ @element_local_attribute_index[element] ||= begin
45
+ # Plain hash: a miss must stay a nil lookup, not allocate an
46
+ # empty default array — misses dominate (the ISO-13849 shape
47
+ # resolves ~390k lenient lookups that match nothing).
48
+ index = {}
49
+ element.attributes.each_value do |attr|
37
50
  local_names(attr).each do |key|
38
- entries = index[key]
51
+ entries = (index[key] ||= [])
39
52
  entries << attr unless entries.include?(attr)
40
53
  end
41
54
  end
@@ -227,13 +227,7 @@ register_id)
227
227
  # @param rule [CompiledRule] The rule
228
228
  # @return [Boolean] true if custom method only
229
229
  def custom_method_only?(rule)
230
- # Check if attribute name is a placeholder (e.g., :__content__)
231
- return true if rule.attribute_name.to_s.start_with?("__") &&
232
- rule.attribute_name.to_s.end_with?("__")
233
-
234
- # Also check if rule has custom methods but attribute_type is nil
235
- # This handles cases where we inferred an attribute name for custom methods
236
- rule.has_custom_methods? && rule.attribute_type.nil?
230
+ rule.custom_method_only?
237
231
  end
238
232
 
239
233
  # Extract value for a rule
@@ -24,6 +24,7 @@ Lutaml::Model::FormatRegistry.register(
24
24
  available: %i[standard standard_yaml yeptris],
25
25
  default: :standard,
26
26
  },
27
+ stream_methods: true,
27
28
  )
28
29
 
29
30
  require_relative "type/serializers"
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model#550: options passed to `from_*` / `to_*` flow to custom
6
+ # methods that declare the extra context parameter (arity-aware; the
7
+ # historical signatures keep working).
8
+ RSpec.describe "Custom method context forwarding" do
9
+ let(:model_class) do
10
+ Class.new(Lutaml::Model::Serializable) do
11
+ attribute :name, :string
12
+
13
+ def name_from(model, value, ctx = nil)
14
+ model.name = "#{value} (#{ctx ? ctx[:schema] : 'none'})"
15
+ end
16
+
17
+ def name_to(model, hash, ctx = nil)
18
+ hash["name"] = "#{model.name} <#{ctx ? ctx[:schema] : 'none'}>"
19
+ end
20
+
21
+ key_value do
22
+ map "name", to: :name,
23
+ with: { from: :name_from, to: :name_to }
24
+ end
25
+ end
26
+ end
27
+
28
+ it "passes context to custom methods on parse when accepted" do
29
+ parsed = model_class.from_yaml({ "name" => "Alpine" }.to_yaml,
30
+ context: { schema: "ISO" })
31
+ expect(parsed.name).to eq("Alpine (ISO)")
32
+ end
33
+
34
+ it "passes context to custom methods on serialize when accepted" do
35
+ instance = model_class.from_yaml({ "name" => "Alpine" }.to_yaml)
36
+ out = instance.to_yaml(context: { schema: "ISO" })
37
+ expect(out).to include("Alpine (none) <ISO>")
38
+ end
39
+
40
+ it "keeps two-parameter custom methods working without context" do
41
+ parsed = model_class.from_yaml({ "name" => "Alpine" }.to_yaml)
42
+ expect(parsed.name).to eq("Alpine (none)")
43
+
44
+ out = parsed.to_yaml
45
+ expect(out).to include("Alpine (none) <none>")
46
+ end
47
+
48
+ it "forwards context to with-lambdas of exact arity 2" do
49
+ klass = Class.new(Lutaml::Model::Serializable) do
50
+ attribute :code, :string
51
+
52
+ key_value do
53
+ map "code", to: :code,
54
+ with: { from: ->(value, ctx) { "#{value}-#{ctx[:rev]}" } }
55
+ end
56
+ end
57
+
58
+ parsed = klass.from_yaml({ "code" => "A1" }.to_yaml,
59
+ context: { rev: 7 })
60
+ expect(parsed.code).to eq("A1-7")
61
+ end
62
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model#436: multi-document YAML streams with per-document model
6
+ # dispatch (the glossarist-ruby#132 / ISO 5843-6 shape).
7
+ RSpec.describe "YAML streams" do
8
+ let(:concept_class) do
9
+ Class.new(Lutaml::Model::Serializable) do
10
+ attribute :id, :string
11
+ attribute :language_code, :string
12
+
13
+ key_value do
14
+ map "id", to: :id
15
+ map "language_code", to: :language_code
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:localized_class) do
21
+ Class.new(Lutaml::Model::Serializable) do
22
+ attribute :id, :string
23
+ attribute :status, :string
24
+
25
+ key_value do
26
+ map "id", to: :id
27
+ map "status", to: :status
28
+ end
29
+ end
30
+ end
31
+
32
+ let(:stream) do
33
+ <<~YAML
34
+ ---
35
+ id: 1-EN
36
+ language_code: eng
37
+ ---
38
+ id: 3a75
39
+ status: accepted
40
+ ---
41
+ id: 1-FR
42
+ language_code: fra
43
+ YAML
44
+ end
45
+
46
+ it "parses every document in the stream" do
47
+ models = concept_class.from_yaml_stream(stream)
48
+ expect(models.map(&:id)).to eq(%w[1-EN 3a75 1-FR])
49
+ end
50
+
51
+ it "dispatches documents polymorphically via discriminator" do
52
+ models = concept_class.from_yaml_stream(
53
+ stream,
54
+ discriminator: lambda { |doc|
55
+ doc.key?("language_code") ? concept_class : localized_class
56
+ },
57
+ )
58
+ expect(models.map(&:class)).to eq([concept_class, localized_class,
59
+ concept_class])
60
+ expect(models[1].status).to eq("accepted")
61
+ end
62
+
63
+ it "round-trips through to_yaml_stream" do
64
+ models = concept_class.from_yaml_stream(stream)
65
+ out = concept_class.to_yaml_stream(models)
66
+ reparsed = concept_class.from_yaml_stream(out)
67
+ expect(reparsed.map(&:id)).to eq(models.map(&:id))
68
+ expect(out).to include("---")
69
+ end
70
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model#154: HTML named/numeric entities decode only for
6
+ # mappings that opt in via `html_entities` — XML otherwise keeps
7
+ # undefined entities literal.
8
+ RSpec.describe "Opt-in HTML entity decoding" do
9
+ context "when the mapping opts in" do
10
+ let(:model_class) do
11
+ Class.new(Lutaml::Model::Serializable) do
12
+ attribute :t, :string
13
+
14
+ xml do
15
+ element "doc"
16
+ map_element "t", to: :t
17
+ html_entities
18
+ end
19
+ end
20
+ end
21
+
22
+ it "decodes named and numeric entities in element text" do
23
+ parsed = model_class.from_xml("<doc><t>a &amp; b &#8212; c &copy; d</t></doc>")
24
+ expect(parsed.t).to eq("a & b — c © d")
25
+ end
26
+ end
27
+
28
+ context "when the mapping does not opt in" do
29
+ let(:model_class) do
30
+ Class.new(Lutaml::Model::Serializable) do
31
+ attribute :t, :string
32
+
33
+ xml do
34
+ element "doc"
35
+ map_element "t", to: :t
36
+ end
37
+ end
38
+ end
39
+
40
+ it "keeps undefined entities literal" do
41
+ parsed = model_class.from_xml("<doc><t>a &amp; b &copy; d</t></doc>")
42
+ expect(parsed.t).to eq("a & b &copy; d")
43
+ end
44
+ end
45
+ 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.37
4
+ version: 0.8.39
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-18 00:00:00.000000000 Z
11
+ date: 2026-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -202,6 +202,7 @@ files:
202
202
  - docs/_guides/character-encoding.adoc
203
203
  - docs/_guides/collection-serialization.adoc
204
204
  - docs/_guides/creating-xsd.adoc
205
+ - docs/_guides/custom-methods.adoc
205
206
  - docs/_guides/document-validation.adoc
206
207
  - docs/_guides/index.adoc
207
208
  - docs/_guides/jsonld-serialization.adoc
@@ -776,6 +777,7 @@ files:
776
777
  - lib/lutaml/xml/format.rb
777
778
  - lib/lutaml/xml/format_chooser.rb
778
779
  - lib/lutaml/xml/hoisting_algorithm.rb
780
+ - lib/lutaml/xml/html_entities.rb
779
781
  - lib/lutaml/xml/leptris/element.rb
780
782
  - lib/lutaml/xml/listener.rb
781
783
  - lib/lutaml/xml/mapping.rb
@@ -1716,6 +1718,7 @@ files:
1716
1718
  - spec/lutaml/model/custom_bibtex_adapter_spec.rb
1717
1719
  - spec/lutaml/model/custom_collection_spec.rb
1718
1720
  - spec/lutaml/model/custom_from_empty_values_spec.rb
1721
+ - spec/lutaml/model/custom_method_context_spec.rb
1719
1722
  - spec/lutaml/model/custom_model_spec.rb
1720
1723
  - spec/lutaml/model/custom_serialization_spec.rb
1721
1724
  - spec/lutaml/model/custom_vobject_adapter_spec.rb
@@ -1859,6 +1862,7 @@ files:
1859
1862
  - spec/lutaml/model/xsd_type_spec.rb
1860
1863
  - spec/lutaml/model/xsd_type_validation_spec.rb
1861
1864
  - spec/lutaml/model/yaml_adapter_spec.rb
1865
+ - spec/lutaml/model/yaml_stream_spec.rb
1862
1866
  - spec/lutaml/model/yamls/standard_adapter_spec.rb
1863
1867
  - spec/lutaml/model/yamls_range_spec.rb
1864
1868
  - spec/lutaml/model/yamls_sequence_spec.rb
@@ -1913,6 +1917,7 @@ files:
1913
1917
  - spec/lutaml/xml/format_chooser_spec.rb
1914
1918
  - spec/lutaml/xml/fpi_namespace_spec.rb
1915
1919
  - spec/lutaml/xml/general_namespace_spec.rb
1920
+ - spec/lutaml/xml/html_entities_spec.rb
1916
1921
  - spec/lutaml/xml/indent_spec.rb
1917
1922
  - spec/lutaml/xml/line_ending_spec.rb
1918
1923
  - spec/lutaml/xml/mapping_finalization_guard_spec.rb