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.
- checksums.yaml +4 -4
- data/bench/gate_config.rb +5 -4
- data/docs/_guides/custom-methods.adoc +71 -0
- data/docs/_guides/index.adoc +1 -0
- data/lib/compat/opal/lutaml_model_boot.rb +1 -0
- data/lib/lutaml/key_value/transform.rb +51 -5
- data/lib/lutaml/key_value/transformation.rb +10 -2
- data/lib/lutaml/model/attribute.rb +5 -1
- data/lib/lutaml/model/compiled_rule.rb +9 -0
- data/lib/lutaml/model/format_registry.rb +3 -1
- data/lib/lutaml/model/mapping/mapping_rule.rb +51 -11
- data/lib/lutaml/model/serialize/attribute_definition.rb +143 -65
- data/lib/lutaml/model/serialize/deserialization_context.rb +1 -0
- data/lib/lutaml/model/serialize.rb +66 -17
- data/lib/lutaml/model/services/transformer.rb +30 -11
- data/lib/lutaml/model/type/string.rb +4 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/format.rb +1 -0
- data/lib/lutaml/xml/html_entities.rb +290 -0
- data/lib/lutaml/xml/mapping.rb +19 -0
- data/lib/lutaml/xml/model_transform.rb +181 -53
- data/lib/lutaml/xml/parse_session.rb +24 -11
- data/lib/lutaml/xml/transformation/rule_applier.rb +1 -7
- data/lib/lutaml/yaml/format.rb +1 -0
- data/spec/lutaml/model/custom_method_context_spec.rb +62 -0
- data/spec/lutaml/model/yaml_stream_spec.rb +70 -0
- data/spec/lutaml/xml/html_entities_spec.rb +45 -0
- metadata +7 -2
|
@@ -33,6 +33,16 @@ module Lutaml
|
|
|
33
33
|
# The getter materializes a real Array on first access.
|
|
34
34
|
LAZY_EMPTY_COLLECTION = [].freeze
|
|
35
35
|
|
|
36
|
+
# Ivar symbols for lazy collections, memoized per attribute name:
|
|
37
|
+
# the "@name" interpolation allocated twice per collection read
|
|
38
|
+
# (491k on the ISO-13849 serialize profile).
|
|
39
|
+
LAZY_IVAR = {}.compare_by_identity
|
|
40
|
+
|
|
41
|
+
# Sentinel distinguishing "getter called with no argument" from
|
|
42
|
+
# builder-syntax `g.attr(value)` where value may be nil. Compiled
|
|
43
|
+
# getters default to it instead of a `*args` splat.
|
|
44
|
+
NO_ARG = Object.new.freeze
|
|
45
|
+
|
|
36
46
|
INTERNAL_ATTRIBUTES = %i[@using_default @lutaml_register @lutaml_parent @lutaml_root
|
|
37
47
|
@register_records].freeze
|
|
38
48
|
|
|
@@ -73,6 +83,49 @@ module Lutaml
|
|
|
73
83
|
end
|
|
74
84
|
end
|
|
75
85
|
|
|
86
|
+
# lutaml-model#436: multi-document streams. `from_<f>_stream`
|
|
87
|
+
# parses each document separately; `discriminator:` receives each
|
|
88
|
+
# parsed document hash and returns the model class to build with,
|
|
89
|
+
# enabling polymorphic streams. `to_<f>_stream` joins instances
|
|
90
|
+
# into one stream document.
|
|
91
|
+
def self.register_stream_methods(format)
|
|
92
|
+
ClassMethods.define_method(:"from_#{format}_stream") do |data, options = {}|
|
|
93
|
+
docs = Lutaml::Model::Serialize.stream_documents(data, format, options)
|
|
94
|
+
discriminator = options[:discriminator]
|
|
95
|
+
docs.map do |doc|
|
|
96
|
+
target = discriminator ? discriminator.call(doc) : self
|
|
97
|
+
target.send(:"of_#{format}", doc, options)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
ClassMethods.define_method(:"to_#{format}_stream") do |instances, options = {}|
|
|
102
|
+
instances.map do |instance|
|
|
103
|
+
instance.send(:"to_#{format}", options)
|
|
104
|
+
end.join("---\n")
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.stream_documents(data, format, options)
|
|
109
|
+
require "yaml"
|
|
110
|
+
if YAML.respond_to?(:load_stream, true)
|
|
111
|
+
begin
|
|
112
|
+
docs = YAML.load_stream(data,
|
|
113
|
+
aliases: options.fetch(:aliases, true))
|
|
114
|
+
rescue ArgumentError
|
|
115
|
+
# Older Psych builds without the aliases keyword: aliases are
|
|
116
|
+
# on by default there.
|
|
117
|
+
docs = YAML.load_stream(data)
|
|
118
|
+
end
|
|
119
|
+
docs.is_a?(Array) ? docs.compact : [docs].compact
|
|
120
|
+
else
|
|
121
|
+
data.split(/^---\s*$/).filter_map do |chunk|
|
|
122
|
+
chunk.strip.empty? ? nil : YAML.safe_load(chunk)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
rescue StandardError => e
|
|
126
|
+
raise Lutaml::Model::InvalidFormatError.new(format, e.message)
|
|
127
|
+
end
|
|
128
|
+
|
|
76
129
|
def self.register_to_format_method(format)
|
|
77
130
|
ClassMethods.define_method(:"to_#{format}") do |instance, options = {}|
|
|
78
131
|
to(format, instance, options)
|
|
@@ -108,21 +161,12 @@ module Lutaml
|
|
|
108
161
|
@using_default = nil
|
|
109
162
|
@lutaml_register = register
|
|
110
163
|
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
klass = self.class
|
|
118
|
-
klass.compile_state_defaults! unless klass.method_defined?(:__init_deserialized_state_defaults)
|
|
119
|
-
__init_deserialized_state_defaults
|
|
120
|
-
else
|
|
121
|
-
self.class.attributes(register).each do |name, attr|
|
|
122
|
-
instance_variable_set(:"@#{name}",
|
|
123
|
-
attr.collection? ? LAZY_EMPTY_COLLECTION : Lutaml::Model::UninitializedClass.instance)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
164
|
+
# Seed every attribute through the per-(class, register) compiled
|
|
165
|
+
# method: plain `@x = sentinel` writes, no per-call "@#{name}"
|
|
166
|
+
# string interpolation, no instance_variable_set. The previous
|
|
167
|
+
# generic walk interpolated a name string per attribute per
|
|
168
|
+
# instance — 581k allocations on the ISO-13849 profile.
|
|
169
|
+
public_send(self.class.compiled_state_defaults_name!(self.class.extract_register_id(register)))
|
|
126
170
|
end
|
|
127
171
|
|
|
128
172
|
# Complete deserialization initialization after allocation.
|
|
@@ -367,11 +411,16 @@ module Lutaml
|
|
|
367
411
|
# @param attribute_name [Symbol] the collection attribute
|
|
368
412
|
# @return [Object] the stored collection
|
|
369
413
|
def materialize_lazy_collection(attribute_name)
|
|
370
|
-
|
|
414
|
+
ivar = LAZY_IVAR[attribute_name]
|
|
415
|
+
unless ivar
|
|
416
|
+
ivar = :"@#{attribute_name}"
|
|
417
|
+
LAZY_IVAR[attribute_name] = ivar
|
|
418
|
+
end
|
|
419
|
+
current = instance_variable_get(ivar)
|
|
371
420
|
return current unless current.equal?(LAZY_EMPTY_COLLECTION)
|
|
372
421
|
return current if frozen?
|
|
373
422
|
|
|
374
|
-
instance_variable_set(
|
|
423
|
+
instance_variable_set(ivar, [])
|
|
375
424
|
end
|
|
376
425
|
|
|
377
426
|
# Hand back a reference collection the caller can push onto.
|
|
@@ -2,8 +2,8 @@ module Lutaml
|
|
|
2
2
|
module Model
|
|
3
3
|
class Transformer
|
|
4
4
|
class << self
|
|
5
|
-
def call(value, rule, attribute, format: nil)
|
|
6
|
-
new(rule, attribute, format).call(value)
|
|
5
|
+
def call(value, rule, attribute, format: nil, context: nil)
|
|
6
|
+
new(rule, attribute, format, context).call(value)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
private
|
|
@@ -15,7 +15,7 @@ module Lutaml
|
|
|
15
15
|
transform.is_a?(::Hash) ? transform[direction] : transform
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def apply_static(value, sources, direction, format)
|
|
18
|
+
def apply_static(value, sources, direction, format, context = nil)
|
|
19
19
|
methods = sources.filter_map do |obj|
|
|
20
20
|
get_transform_static(obj, direction)
|
|
21
21
|
end
|
|
@@ -34,16 +34,23 @@ module Lutaml
|
|
|
34
34
|
tc.public_send(apply_direction, v, format)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
methods.reduce(result)
|
|
37
|
+
methods.reduce(result) do |tv, m|
|
|
38
|
+
if m.arity == 2
|
|
39
|
+
m.call(tv, context)
|
|
40
|
+
else
|
|
41
|
+
m.call(tv)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
38
44
|
end
|
|
39
45
|
end
|
|
40
46
|
|
|
41
|
-
attr_reader :rule, :attribute, :format
|
|
47
|
+
attr_reader :rule, :attribute, :format, :context
|
|
42
48
|
|
|
43
|
-
def initialize(rule, attribute, format = nil)
|
|
49
|
+
def initialize(rule, attribute, format = nil, context = nil)
|
|
44
50
|
@rule = rule
|
|
45
51
|
@attribute = attribute
|
|
46
52
|
@format = format
|
|
53
|
+
@context = context
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
def call(value)
|
|
@@ -61,7 +68,19 @@ module Lutaml
|
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
methods.reduce(result) do |transformed_value, method|
|
|
64
|
-
method
|
|
71
|
+
invoke_transform(method, transformed_value)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# lutaml-model#550: `with:` custom methods opt into the options
|
|
76
|
+
# passed to `from_*` / `to_*` by declaring a second parameter
|
|
77
|
+
# (exact arity 2); one-parameter methods keep the historical
|
|
78
|
+
# single-argument call.
|
|
79
|
+
def invoke_transform(method, value)
|
|
80
|
+
if method.arity == 2
|
|
81
|
+
method.call(value, context)
|
|
82
|
+
else
|
|
83
|
+
method.call(value)
|
|
65
84
|
end
|
|
66
85
|
end
|
|
67
86
|
|
|
@@ -87,8 +106,8 @@ module Lutaml
|
|
|
87
106
|
|
|
88
107
|
class ImportTransformer < Transformer
|
|
89
108
|
class << self
|
|
90
|
-
def call(value, rule, attribute, format: nil)
|
|
91
|
-
apply_static(value, [rule, attribute], :import, format)
|
|
109
|
+
def call(value, rule, attribute, format: nil, context: nil)
|
|
110
|
+
apply_static(value, [rule, attribute], :import, format, context)
|
|
92
111
|
end
|
|
93
112
|
end
|
|
94
113
|
|
|
@@ -103,8 +122,8 @@ module Lutaml
|
|
|
103
122
|
|
|
104
123
|
class ExportTransformer < Transformer
|
|
105
124
|
class << self
|
|
106
|
-
def call(value, rule, attribute, format: nil)
|
|
107
|
-
apply_static(value, [attribute, rule], :export, format)
|
|
125
|
+
def call(value, rule, attribute, format: nil, context: nil)
|
|
126
|
+
apply_static(value, [attribute, rule], :export, format, context)
|
|
108
127
|
end
|
|
109
128
|
end
|
|
110
129
|
|
|
@@ -19,7 +19,10 @@ module Lutaml
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
value = value.to_s
|
|
22
|
-
|
|
22
|
+
# tr, not gsub: the regexp engine is measurable on hot text
|
|
23
|
+
# paths; :collapse keeps squeeze+strip (two C passes, no
|
|
24
|
+
# intermediate regexp state).
|
|
25
|
+
value = value.tr("\t\n\r", " ") unless mode == :preserve
|
|
23
26
|
value = value.squeeze(" ").strip if mode == :collapse
|
|
24
27
|
|
|
25
28
|
unless options.equal?(EMPTY_OPTIONS)
|
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/xml/format.rb
CHANGED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# lutaml-model#154: HTML 4.01 named entity table (plus apos) for opt-in
|
|
4
|
+
# decoding of HTML entities in XML text (MathML and friends rely on
|
|
5
|
+
# them although XML itself defines none beyond the predefined five).
|
|
6
|
+
|
|
7
|
+
module Lutaml
|
|
8
|
+
module Xml
|
|
9
|
+
module HtmlEntities
|
|
10
|
+
# The table is the data: a gem dependency or data file for a static
|
|
11
|
+
# W3C list would cost more than the frozen literal itself.
|
|
12
|
+
# The table is the data: a gem dependency or data file for a static
|
|
13
|
+
# W3C list would cost more than the frozen literal itself.
|
|
14
|
+
NAMED_TO_CODEPOINT = { # rubocop:disable Metrics/CollectionLiteralLength
|
|
15
|
+
"AElig" => 198,
|
|
16
|
+
"Aacute" => 193,
|
|
17
|
+
"Acirc" => 194,
|
|
18
|
+
"Agrave" => 192,
|
|
19
|
+
"Alpha" => 913,
|
|
20
|
+
"Aring" => 197,
|
|
21
|
+
"Atilde" => 195,
|
|
22
|
+
"Auml" => 196,
|
|
23
|
+
"Beta" => 914,
|
|
24
|
+
"Ccedil" => 199,
|
|
25
|
+
"Chi" => 935,
|
|
26
|
+
"Dagger" => 8225,
|
|
27
|
+
"Delta" => 916,
|
|
28
|
+
"ETH" => 208,
|
|
29
|
+
"Eacute" => 201,
|
|
30
|
+
"Ecirc" => 202,
|
|
31
|
+
"Egrave" => 200,
|
|
32
|
+
"Epsilon" => 917,
|
|
33
|
+
"Eta" => 919,
|
|
34
|
+
"Euml" => 203,
|
|
35
|
+
"Gamma" => 915,
|
|
36
|
+
"Iacute" => 205,
|
|
37
|
+
"Icirc" => 206,
|
|
38
|
+
"Igrave" => 204,
|
|
39
|
+
"Iota" => 921,
|
|
40
|
+
"Iuml" => 207,
|
|
41
|
+
"Kappa" => 922,
|
|
42
|
+
"Lambda" => 923,
|
|
43
|
+
"Mu" => 924,
|
|
44
|
+
"Ntilde" => 209,
|
|
45
|
+
"Nu" => 925,
|
|
46
|
+
"OElig" => 338,
|
|
47
|
+
"Oacute" => 211,
|
|
48
|
+
"Ocirc" => 212,
|
|
49
|
+
"Ograve" => 210,
|
|
50
|
+
"Omega" => 937,
|
|
51
|
+
"Omicron" => 927,
|
|
52
|
+
"Oslash" => 216,
|
|
53
|
+
"Otilde" => 213,
|
|
54
|
+
"Ouml" => 214,
|
|
55
|
+
"Phi" => 934,
|
|
56
|
+
"Pi" => 928,
|
|
57
|
+
"Prime" => 8243,
|
|
58
|
+
"Psi" => 936,
|
|
59
|
+
"Rho" => 929,
|
|
60
|
+
"Scaron" => 352,
|
|
61
|
+
"Sigma" => 931,
|
|
62
|
+
"THORN" => 222,
|
|
63
|
+
"Tau" => 932,
|
|
64
|
+
"Theta" => 920,
|
|
65
|
+
"Uacute" => 218,
|
|
66
|
+
"Ucirc" => 219,
|
|
67
|
+
"Ugrave" => 217,
|
|
68
|
+
"Upsilon" => 933,
|
|
69
|
+
"Uuml" => 220,
|
|
70
|
+
"Xi" => 926,
|
|
71
|
+
"Yacute" => 221,
|
|
72
|
+
"Yuml" => 376,
|
|
73
|
+
"Zeta" => 918,
|
|
74
|
+
"aacute" => 225,
|
|
75
|
+
"acirc" => 226,
|
|
76
|
+
"acute" => 180,
|
|
77
|
+
"aelig" => 230,
|
|
78
|
+
"agrave" => 224,
|
|
79
|
+
"alefsym" => 8501,
|
|
80
|
+
"alpha" => 945,
|
|
81
|
+
"amp" => 38,
|
|
82
|
+
"and" => 8743,
|
|
83
|
+
"ang" => 8736,
|
|
84
|
+
"apos" => 39,
|
|
85
|
+
"aring" => 229,
|
|
86
|
+
"asymp" => 8776,
|
|
87
|
+
"atilde" => 227,
|
|
88
|
+
"auml" => 228,
|
|
89
|
+
"bdquo" => 8222,
|
|
90
|
+
"beta" => 946,
|
|
91
|
+
"brvbar" => 166,
|
|
92
|
+
"bull" => 8226,
|
|
93
|
+
"cap" => 8745,
|
|
94
|
+
"ccedil" => 231,
|
|
95
|
+
"cedil" => 184,
|
|
96
|
+
"cent" => 162,
|
|
97
|
+
"chi" => 967,
|
|
98
|
+
"circ" => 710,
|
|
99
|
+
"clubs" => 9827,
|
|
100
|
+
"cong" => 8773,
|
|
101
|
+
"copy" => 169,
|
|
102
|
+
"crarr" => 8629,
|
|
103
|
+
"cup" => 8746,
|
|
104
|
+
"curren" => 164,
|
|
105
|
+
"dArr" => 8659,
|
|
106
|
+
"dagger" => 8224,
|
|
107
|
+
"darr" => 8595,
|
|
108
|
+
"deg" => 176,
|
|
109
|
+
"delta" => 948,
|
|
110
|
+
"diams" => 9830,
|
|
111
|
+
"divide" => 247,
|
|
112
|
+
"eacute" => 233,
|
|
113
|
+
"ecirc" => 234,
|
|
114
|
+
"egrave" => 232,
|
|
115
|
+
"empty" => 8709,
|
|
116
|
+
"emsp" => 8195,
|
|
117
|
+
"ensp" => 8194,
|
|
118
|
+
"epsilon" => 949,
|
|
119
|
+
"equiv" => 8801,
|
|
120
|
+
"eta" => 951,
|
|
121
|
+
"eth" => 240,
|
|
122
|
+
"euml" => 235,
|
|
123
|
+
"euro" => 8364,
|
|
124
|
+
"exist" => 8707,
|
|
125
|
+
"fnof" => 402,
|
|
126
|
+
"forall" => 8704,
|
|
127
|
+
"frac12" => 189,
|
|
128
|
+
"frac14" => 188,
|
|
129
|
+
"frac34" => 190,
|
|
130
|
+
"frasl" => 8260,
|
|
131
|
+
"gamma" => 947,
|
|
132
|
+
"ge" => 8805,
|
|
133
|
+
"gt" => 62,
|
|
134
|
+
"hArr" => 8660,
|
|
135
|
+
"harr" => 8596,
|
|
136
|
+
"hearts" => 9829,
|
|
137
|
+
"hellip" => 8230,
|
|
138
|
+
"iacute" => 237,
|
|
139
|
+
"icirc" => 238,
|
|
140
|
+
"iexcl" => 161,
|
|
141
|
+
"igrave" => 236,
|
|
142
|
+
"image" => 8465,
|
|
143
|
+
"infin" => 8734,
|
|
144
|
+
"int" => 8747,
|
|
145
|
+
"iota" => 953,
|
|
146
|
+
"iquest" => 191,
|
|
147
|
+
"isin" => 8712,
|
|
148
|
+
"iuml" => 239,
|
|
149
|
+
"kappa" => 954,
|
|
150
|
+
"lArr" => 8656,
|
|
151
|
+
"lambda" => 955,
|
|
152
|
+
"lang" => 9001,
|
|
153
|
+
"laquo" => 171,
|
|
154
|
+
"larr" => 8592,
|
|
155
|
+
"lceil" => 8968,
|
|
156
|
+
"ldquo" => 8220,
|
|
157
|
+
"le" => 8804,
|
|
158
|
+
"lfloor" => 8970,
|
|
159
|
+
"lowast" => 8727,
|
|
160
|
+
"loz" => 9674,
|
|
161
|
+
"lrm" => 8206,
|
|
162
|
+
"lsaquo" => 8249,
|
|
163
|
+
"lsquo" => 8216,
|
|
164
|
+
"lt" => 60,
|
|
165
|
+
"macr" => 175,
|
|
166
|
+
"mdash" => 8212,
|
|
167
|
+
"micro" => 181,
|
|
168
|
+
"middot" => 183,
|
|
169
|
+
"minus" => 8722,
|
|
170
|
+
"mu" => 956,
|
|
171
|
+
"nabla" => 8711,
|
|
172
|
+
"nbsp" => 160,
|
|
173
|
+
"ndash" => 8211,
|
|
174
|
+
"ne" => 8800,
|
|
175
|
+
"ni" => 8715,
|
|
176
|
+
"not" => 172,
|
|
177
|
+
"notin" => 8713,
|
|
178
|
+
"nsub" => 8836,
|
|
179
|
+
"ntilde" => 241,
|
|
180
|
+
"nu" => 957,
|
|
181
|
+
"oacute" => 243,
|
|
182
|
+
"ocirc" => 244,
|
|
183
|
+
"oelig" => 339,
|
|
184
|
+
"ograve" => 242,
|
|
185
|
+
"oline" => 8254,
|
|
186
|
+
"omega" => 969,
|
|
187
|
+
"omicron" => 959,
|
|
188
|
+
"oplus" => 8853,
|
|
189
|
+
"or" => 8744,
|
|
190
|
+
"ordf" => 170,
|
|
191
|
+
"ordm" => 186,
|
|
192
|
+
"oslash" => 248,
|
|
193
|
+
"otilde" => 245,
|
|
194
|
+
"otimes" => 8855,
|
|
195
|
+
"ouml" => 246,
|
|
196
|
+
"para" => 182,
|
|
197
|
+
"part" => 8706,
|
|
198
|
+
"permil" => 8240,
|
|
199
|
+
"perp" => 8869,
|
|
200
|
+
"phi" => 966,
|
|
201
|
+
"pi" => 960,
|
|
202
|
+
"piv" => 982,
|
|
203
|
+
"plusmn" => 177,
|
|
204
|
+
"pound" => 163,
|
|
205
|
+
"prime" => 8242,
|
|
206
|
+
"prod" => 8719,
|
|
207
|
+
"prop" => 8733,
|
|
208
|
+
"psi" => 968,
|
|
209
|
+
"quot" => 34,
|
|
210
|
+
"rArr" => 8658,
|
|
211
|
+
"radic" => 8730,
|
|
212
|
+
"rang" => 9002,
|
|
213
|
+
"raquo" => 187,
|
|
214
|
+
"rarr" => 8594,
|
|
215
|
+
"rceil" => 8969,
|
|
216
|
+
"rdquo" => 8221,
|
|
217
|
+
"real" => 8476,
|
|
218
|
+
"reg" => 174,
|
|
219
|
+
"rfloor" => 8971,
|
|
220
|
+
"rho" => 961,
|
|
221
|
+
"rlm" => 8207,
|
|
222
|
+
"rsaquo" => 8250,
|
|
223
|
+
"rsquo" => 8217,
|
|
224
|
+
"sbquo" => 8218,
|
|
225
|
+
"scaron" => 353,
|
|
226
|
+
"sdot" => 8901,
|
|
227
|
+
"sect" => 167,
|
|
228
|
+
"shy" => 173,
|
|
229
|
+
"sigma" => 963,
|
|
230
|
+
"sigmaf" => 962,
|
|
231
|
+
"sim" => 8764,
|
|
232
|
+
"spades" => 9824,
|
|
233
|
+
"sub" => 8834,
|
|
234
|
+
"sube" => 8838,
|
|
235
|
+
"sum" => 8721,
|
|
236
|
+
"sup" => 8835,
|
|
237
|
+
"sup1" => 185,
|
|
238
|
+
"sup2" => 178,
|
|
239
|
+
"sup3" => 179,
|
|
240
|
+
"supe" => 8839,
|
|
241
|
+
"szlig" => 223,
|
|
242
|
+
"tau" => 964,
|
|
243
|
+
"there4" => 8756,
|
|
244
|
+
"theta" => 952,
|
|
245
|
+
"thetasym" => 977,
|
|
246
|
+
"thinsp" => 8201,
|
|
247
|
+
"thorn" => 254,
|
|
248
|
+
"tilde" => 732,
|
|
249
|
+
"times" => 215,
|
|
250
|
+
"trade" => 8482,
|
|
251
|
+
"uArr" => 8657,
|
|
252
|
+
"uacute" => 250,
|
|
253
|
+
"uarr" => 8593,
|
|
254
|
+
"ucirc" => 251,
|
|
255
|
+
"ugrave" => 249,
|
|
256
|
+
"uml" => 168,
|
|
257
|
+
"upsih" => 978,
|
|
258
|
+
"upsilon" => 965,
|
|
259
|
+
"uuml" => 252,
|
|
260
|
+
"weierp" => 8472,
|
|
261
|
+
"xi" => 958,
|
|
262
|
+
"yacute" => 253,
|
|
263
|
+
"yen" => 165,
|
|
264
|
+
"yuml" => 255,
|
|
265
|
+
"zeta" => 950,
|
|
266
|
+
"zwj" => 8205,
|
|
267
|
+
"zwnj" => 8204,
|
|
268
|
+
}.freeze
|
|
269
|
+
|
|
270
|
+
NAME_PATTERN = /&(#x?[0-9A-Fa-f]+|[A-Za-z][A-Za-z0-9]*);/
|
|
271
|
+
|
|
272
|
+
module_function
|
|
273
|
+
|
|
274
|
+
def decode(value)
|
|
275
|
+
value.gsub(NAME_PATTERN) do |match|
|
|
276
|
+
body = match[1..-2]
|
|
277
|
+
case body[0]
|
|
278
|
+
when "x", "X"
|
|
279
|
+
body[1..].to_i(16).chr(Encoding::UTF_8)
|
|
280
|
+
when "#"
|
|
281
|
+
body[1..].to_i.chr(Encoding::UTF_8)
|
|
282
|
+
else
|
|
283
|
+
codepoint = NAMED_TO_CODEPOINT[body]
|
|
284
|
+
codepoint ? codepoint.chr(Encoding::UTF_8) : match
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
data/lib/lutaml/xml/mapping.rb
CHANGED
|
@@ -121,9 +121,16 @@ module Lutaml
|
|
|
121
121
|
@cached_attributes.clear
|
|
122
122
|
@cached_mappings.clear
|
|
123
123
|
@cached_attribute_name_counts.clear
|
|
124
|
+
@rule_records_version = (@rule_records_version || 0) + 1
|
|
124
125
|
@finalized = true
|
|
125
126
|
end
|
|
126
127
|
|
|
128
|
+
# Bumped at finalize; ModelTransform's compiled rule records are
|
|
129
|
+
# keyed on it so late mapping changes rebuild instead of going stale.
|
|
130
|
+
def rule_records_version
|
|
131
|
+
@rule_records_version ||= 0
|
|
132
|
+
end
|
|
133
|
+
|
|
127
134
|
def finalized?
|
|
128
135
|
@finalized
|
|
129
136
|
end
|
|
@@ -1334,6 +1341,18 @@ module Lutaml
|
|
|
1334
1341
|
!@namespace_class.nil?
|
|
1335
1342
|
end
|
|
1336
1343
|
|
|
1344
|
+
# lutaml-model#154: opt-in decoding of HTML named/numeric entities
|
|
1345
|
+
# (e.g. ©, —) in mapped text and attribute values. XML
|
|
1346
|
+
# documents are otherwise kept byte-faithful: HTML entities are not
|
|
1347
|
+
# defined in XML without a DTD.
|
|
1348
|
+
def html_entities(enabled: true)
|
|
1349
|
+
@decode_html_entities = enabled
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
def decode_html_entities?
|
|
1353
|
+
!!@decode_html_entities
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1337
1356
|
# Whether namespace was explicitly set via DSL
|
|
1338
1357
|
def namespace_set?
|
|
1339
1358
|
!!@namespace_set
|