lutaml-lml 0.1.8 → 0.2.0
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/lib/lutaml/lml/cli.rb +12 -3
- data/lib/lutaml/lml/data_processor/attribute_processing.rb +49 -9
- data/lib/lutaml/lml/data_processor/value_declaration_processing.rb +62 -0
- data/lib/lutaml/lml/data_processor.rb +3 -0
- data/lib/lutaml/lml/document_builder.rb +161 -6
- data/lib/lutaml/lml/grammar/lml.artifact.json +5837 -2815
- data/lib/lutaml/lml/grammar/lml.parg +64 -17
- data/lib/lutaml/lml/model_compiler.rb +209 -18
- data/lib/lutaml/lml/models/data_type.rb +1 -0
- data/lib/lutaml/lml/models/document.rb +2 -0
- data/lib/lutaml/lml/models/enum.rb +2 -0
- data/lib/lutaml/lml/models/instance.rb +1 -0
- data/lib/lutaml/lml/models/instance_collection.rb +1 -0
- data/lib/lutaml/lml/models/mapping_rule.rb +16 -0
- data/lib/lutaml/lml/models/namespace.rb +14 -0
- data/lib/lutaml/lml/models/serialization_mapping.rb +17 -0
- data/lib/lutaml/lml/models/top_element_attribute.rb +4 -0
- data/lib/lutaml/lml/models/uml_class.rb +1 -0
- data/lib/lutaml/lml/preprocessor.rb +82 -6
- data/lib/lutaml/lml/validator.rb +276 -0
- data/lib/lutaml/lml/version.rb +1 -1
- data/lib/lutaml/lml.rb +4 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bdefb219dd99a2335669d57b9e7a79a21d822485ebc1a3748d915dee88d2901
|
|
4
|
+
data.tar.gz: a63770177d2c8ff0b85e0d30caa6c9033529c75eee9738fbe59b25dc7c1d30eb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e12f920e7b614aa4902cd953e311fdde4dae2cd93a15bd9c5fe10b1c0ca234412a8228ca6cf2f0d22956ddb67cfd681127dedac417d43cd9b0c5b962e3e777a
|
|
7
|
+
data.tar.gz: c7a727c6d617731e160d1ad6bebbba053cd6216a10c93c2d177f391881f0405f0304b3bd7a28230c18cd85cddb3383ff7639a348b36489f142d1e6c6e052ec76
|
data/lib/lutaml/lml/cli.rb
CHANGED
|
@@ -186,13 +186,22 @@ module Lutaml
|
|
|
186
186
|
input_path = Pathname.new(path_string)
|
|
187
187
|
return report_file_error(input_path, 'File does not exist') unless input_path.exist?
|
|
188
188
|
|
|
189
|
-
parse_document(input_path)
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
document = parse_document(input_path)
|
|
190
|
+
violations = Lutaml::Lml::Validator.violations(document)
|
|
191
|
+
if violations.any?
|
|
192
|
+
report_file_error(input_path, violations_message(violations))
|
|
193
|
+
else
|
|
194
|
+
say "✓ #{input_path}", :green
|
|
195
|
+
nil
|
|
196
|
+
end
|
|
192
197
|
rescue StandardError => e
|
|
193
198
|
report_file_error(input_path, e.message)
|
|
194
199
|
end
|
|
195
200
|
|
|
201
|
+
def violations_message(violations)
|
|
202
|
+
violations.map { |v| "[#{v.rule}] #{v.message}" }.join('; ')
|
|
203
|
+
end
|
|
204
|
+
|
|
196
205
|
def report_file_error(input_path, message)
|
|
197
206
|
say "✗ #{input_path}: #{message}", :red
|
|
198
207
|
"#{input_path}: #{message}"
|
|
@@ -15,18 +15,52 @@ module Lutaml
|
|
|
15
15
|
|
|
16
16
|
def process_attributes_array(obj)
|
|
17
17
|
return [] if obj.empty?
|
|
18
|
-
return obj.map { |item| process_attributes(item) } unless
|
|
18
|
+
return obj.map { |item| process_attributes(item) } unless named_attribute_entries?(obj)
|
|
19
|
+
return merge_named_entries(obj) unless single_key_hashes?(obj)
|
|
19
20
|
|
|
21
|
+
obj.each_with_object({}) { |item, hash| append_uniform_entry(hash, item) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def append_uniform_entry(hash, item)
|
|
25
|
+
hash[:properties] ||= []
|
|
26
|
+
if item.key?(:properties)
|
|
27
|
+
hash[:properties] << process_attributes(item[:properties])
|
|
28
|
+
else
|
|
29
|
+
hash.merge!(process_attributes(item))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Attribute-definition groups split across capture depths (e.g. the
|
|
34
|
+
# comma form: {name, type} beside a nested {value_set}) combine into
|
|
35
|
+
# one attribute entry. Key/value pair lists (import/export maps)
|
|
36
|
+
# carry :key and are never merged.
|
|
37
|
+
def named_attribute_entries?(obj)
|
|
38
|
+
obj.any? { |e| e.is_a?(Hash) && e.key?(:name) } &&
|
|
39
|
+
obj.all? { |e| e.is_a?(Hash) && !e.key?(:key) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
COLLECTION_KEYS = %i[properties attributes instances].freeze
|
|
43
|
+
|
|
44
|
+
def merge_named_entries(obj)
|
|
20
45
|
obj.each_with_object({}) do |item, hash|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
hash[:properties] << process_attributes(item[:properties])
|
|
24
|
-
else
|
|
25
|
-
hash.merge!(process_attributes(item))
|
|
46
|
+
process_attributes(item).each do |key, value|
|
|
47
|
+
hash[key] = merge_attribute_value(hash[key], key, value)
|
|
26
48
|
end
|
|
27
49
|
end
|
|
28
50
|
end
|
|
29
51
|
|
|
52
|
+
def merge_attribute_value(existing, key, value)
|
|
53
|
+
return merge_collection_value(existing, value) if COLLECTION_KEYS.include?(key)
|
|
54
|
+
return existing + value if existing.is_a?(Array) && value.is_a?(Array)
|
|
55
|
+
|
|
56
|
+
value
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def merge_collection_value(existing, value)
|
|
60
|
+
current = existing.is_a?(Array) ? existing : (existing ? [existing] : [])
|
|
61
|
+
current + (value.is_a?(Array) ? value : [value])
|
|
62
|
+
end
|
|
63
|
+
|
|
30
64
|
def process_attributes_hash(obj)
|
|
31
65
|
result = extract_name_and_value(obj)
|
|
32
66
|
convert_instance_type(result)
|
|
@@ -34,18 +68,17 @@ module Lutaml
|
|
|
34
68
|
result[:literal] = true if obj.key?(:type_literal)
|
|
35
69
|
apply_nested_attributes(result, obj)
|
|
36
70
|
apply_remaining_keys(result, obj)
|
|
71
|
+
translate_values_argument(result)
|
|
37
72
|
result
|
|
38
73
|
end
|
|
39
74
|
|
|
40
|
-
private
|
|
41
|
-
|
|
42
75
|
def single_key_hashes?(arr)
|
|
43
76
|
arr.all? { |e| e.is_a?(Hash) && e.keys.size == 1 }
|
|
44
77
|
end
|
|
45
78
|
|
|
46
79
|
def extract_name_and_value(obj)
|
|
47
80
|
result = {}
|
|
48
|
-
result[:name] = obj[:key] if obj.key?(:key)
|
|
81
|
+
result[:name] = strip_argument_colon(obj[:key]) if obj.key?(:key)
|
|
49
82
|
|
|
50
83
|
if obj.key?(:comments)
|
|
51
84
|
result[:name] = 'Comment'
|
|
@@ -74,9 +107,16 @@ module Lutaml
|
|
|
74
107
|
obj.each do |key, value|
|
|
75
108
|
next if EXCLUDED_PASS_THROUGH_KEYS.include?(key)
|
|
76
109
|
|
|
110
|
+
value = strip_argument_colon(value) if key == :name
|
|
77
111
|
result[key] = value unless result.key?(key)
|
|
78
112
|
end
|
|
79
113
|
end
|
|
114
|
+
|
|
115
|
+
# `default: "Clear"` uses the colon separator (RS 3001 §Attribute);
|
|
116
|
+
# the colon lands in the captured name and must not.
|
|
117
|
+
def strip_argument_colon(name)
|
|
118
|
+
name.to_s.sub(/:\z/, "")
|
|
119
|
+
end
|
|
80
120
|
end
|
|
81
121
|
end
|
|
82
122
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Lml
|
|
5
|
+
class DataProcessor
|
|
6
|
+
# RS 3001 §Value/§Attribute argument normalization: the canonical
|
|
7
|
+
# `value` construct and the attribute `values` argument.
|
|
8
|
+
module ValueDeclarationProcessing
|
|
9
|
+
# `value <name> { block }` is the canonical enum member; the bare
|
|
10
|
+
# identifier member is its documented shortcut. Normalize into the
|
|
11
|
+
# member shape the shortcut produces: the value block's `definition`
|
|
12
|
+
# lines become the member description and its attribute pairs become
|
|
13
|
+
# member payload properties.
|
|
14
|
+
def process_value_declaration(decl)
|
|
15
|
+
inner = { name: unwrap_string(decl[:value_name]) }
|
|
16
|
+
Array(decl[:members]).each do |member|
|
|
17
|
+
normalize_value_member(inner, member)
|
|
18
|
+
end
|
|
19
|
+
{ attributes: inner }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# `values <enum>` types the attribute with the named enum;
|
|
23
|
+
# `values { v1, v2 }` carries an inline value set that the compiler
|
|
24
|
+
# expands into an anonymous enum.
|
|
25
|
+
def translate_values_argument(result)
|
|
26
|
+
if result.key?(:value_set_ref)
|
|
27
|
+
ref = unwrap_string(result.delete(:value_set_ref))
|
|
28
|
+
result[:type] = ref unless result[:type]
|
|
29
|
+
end
|
|
30
|
+
return unless result.key?(:value_set)
|
|
31
|
+
|
|
32
|
+
result[:value_set] = Array(result[:value_set][:items]).map { |item| unwrap_data_value(item[:item]) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def normalize_value_member(inner, member)
|
|
38
|
+
if member.key?(:definition)
|
|
39
|
+
(inner[:attributes] ||= []) << { name: 'description', type: unwrap_string(member[:definition]) }
|
|
40
|
+
elsif member.key?(:instance)
|
|
41
|
+
(inner[:instances] ||= []).concat(Array(member[:instance]))
|
|
42
|
+
elsif member.key?(:attributes)
|
|
43
|
+
(inner[:properties] ||= []) << member[:attributes]
|
|
44
|
+
elsif member.key?(:key)
|
|
45
|
+
(inner[:properties] ||= []) << extract_name_and_value(member)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def unwrap_data_value(item)
|
|
50
|
+
return item unless item.is_a?(Hash)
|
|
51
|
+
|
|
52
|
+
item[:string] || item[:number] || item[:float] ||
|
|
53
|
+
item[:boolean] || item[:name] || item
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def unwrap_string(value)
|
|
57
|
+
value.is_a?(Hash) && value.key?(:string) ? value[:string] : value
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -5,12 +5,14 @@ module Lutaml
|
|
|
5
5
|
class DataProcessor
|
|
6
6
|
autoload :ValueProcessing, "lutaml/lml/data_processor/value_processing"
|
|
7
7
|
autoload :AttributeProcessing, "lutaml/lml/data_processor/attribute_processing"
|
|
8
|
+
autoload :ValueDeclarationProcessing, "lutaml/lml/data_processor/value_declaration_processing"
|
|
8
9
|
autoload :InstanceProcessing, "lutaml/lml/data_processor/instance_processing"
|
|
9
10
|
autoload :CollectionProcessing, "lutaml/lml/data_processor/collection_processing"
|
|
10
11
|
autoload :ViewProcessing, "lutaml/lml/data_processor/view_processing"
|
|
11
12
|
|
|
12
13
|
include ValueProcessing
|
|
13
14
|
include AttributeProcessing
|
|
15
|
+
include ValueDeclarationProcessing
|
|
14
16
|
include InstanceProcessing
|
|
15
17
|
include CollectionProcessing
|
|
16
18
|
include ViewProcessing
|
|
@@ -20,6 +22,7 @@ module Lutaml
|
|
|
20
22
|
instances: :process_instances,
|
|
21
23
|
instance: :process_instance,
|
|
22
24
|
attributes: :process_attributes,
|
|
25
|
+
value_decl: :process_value_declaration,
|
|
23
26
|
show_list: :process_show_list,
|
|
24
27
|
hide_list: :process_hide_list
|
|
25
28
|
}.freeze
|
|
@@ -24,7 +24,9 @@ module Lutaml
|
|
|
24
24
|
mapping: ::Lutaml::Lml::Mapping,
|
|
25
25
|
string_format: ::Lutaml::Lml::StringFormat,
|
|
26
26
|
derived_field: ::Lutaml::Lml::DerivedField,
|
|
27
|
-
member_field: ::Lutaml::Lml::MemberField
|
|
27
|
+
member_field: ::Lutaml::Lml::MemberField,
|
|
28
|
+
namespace: ::Lutaml::Lml::Namespace,
|
|
29
|
+
serialization_mapping: ::Lutaml::Lml::SerializationMapping
|
|
28
30
|
}.freeze
|
|
29
31
|
|
|
30
32
|
attr_reader :registry
|
|
@@ -36,7 +38,7 @@ module Lutaml
|
|
|
36
38
|
FACTORY_KEYS = %i[
|
|
37
39
|
document package class enum data_type diagram view_import view_filter
|
|
38
40
|
attribute association operation constraint value cardinality mapping
|
|
39
|
-
string_format derived_field
|
|
41
|
+
string_format derived_field namespace serialization_mapping
|
|
40
42
|
].freeze
|
|
41
43
|
|
|
42
44
|
MEMBER_KEY_MAP = {
|
|
@@ -52,7 +54,9 @@ module Lutaml
|
|
|
52
54
|
constraints: :constraint,
|
|
53
55
|
values: :value,
|
|
54
56
|
mappings: :mapping,
|
|
55
|
-
member_fields: :member_field
|
|
57
|
+
member_fields: :member_field,
|
|
58
|
+
namespaces: :namespace,
|
|
59
|
+
serialization_mappings: :serialization_mapping
|
|
56
60
|
}.freeze
|
|
57
61
|
|
|
58
62
|
FACTORY_KEYS.each do |key|
|
|
@@ -102,9 +106,11 @@ module Lutaml
|
|
|
102
106
|
end
|
|
103
107
|
|
|
104
108
|
def add_members(model, hash)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
expand_declared_members(model, hash)
|
|
110
|
+
normalize_namespace_declaration(hash)
|
|
111
|
+
normalize_serialization_mapping(hash)
|
|
112
|
+
expand_instances_collection_name(hash)
|
|
113
|
+
expand_nested_members(model, hash)
|
|
108
114
|
MEMBER_KEY_MAP.each do |plural_key, singular_key|
|
|
109
115
|
data = hash.delete(plural_key)
|
|
110
116
|
next if data.nil?
|
|
@@ -116,6 +122,138 @@ module Lutaml
|
|
|
116
122
|
remap_filter_keys(hash, model)
|
|
117
123
|
end
|
|
118
124
|
|
|
125
|
+
# RS 3001 §Enums: an enum-level `values { v1, v2 }` shorthand
|
|
126
|
+
# expands into plain members.
|
|
127
|
+
def expand_value_set_declaration(model, hash)
|
|
128
|
+
decl = hash.delete(:value_set_decl) or return
|
|
129
|
+
Array(decl[:items]).each do |item|
|
|
130
|
+
name = unquote(item[:item])
|
|
131
|
+
model.attributes << TopElementAttribute.new(name: name)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# RS 3001 §Value: the canonical `value` construct normalizes into
|
|
136
|
+
# the same member shape the bare-identifier shortcut produces.
|
|
137
|
+
def expand_value_declaration(model, hash)
|
|
138
|
+
decl = hash.delete(:value_decl) or return
|
|
139
|
+
apply_attribute(model, :attributes, decl.fetch(:attributes))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Namespace declaration bodies arrive as keyword-line members
|
|
143
|
+
# ({uri: …}, {prefix: …}); flatten them onto the namespace hash.
|
|
144
|
+
def normalize_namespace_declaration(hash)
|
|
145
|
+
ns = hash[:namespaces] or return
|
|
146
|
+
Array(ns.delete(:members)).each do |pair|
|
|
147
|
+
pair.each { |key, value| ns[key] = unquote(value) }
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Serialization mapping rules arrive keyed by their line construct
|
|
152
|
+
# ({map_element: {wire:, field:}}); normalize into plain MappingRule
|
|
153
|
+
# hashes so the model cast lands typed rules.
|
|
154
|
+
def normalize_serialization_mapping(hash)
|
|
155
|
+
mapping = hash[:serialization_mappings] or return
|
|
156
|
+
rules = Array(mapping[:rules])
|
|
157
|
+
return if rules.any? && rules.all? { |e| e.is_a?(Hash) && e.key?(:kind) }
|
|
158
|
+
|
|
159
|
+
rules = rules.filter_map do |entry|
|
|
160
|
+
next nil unless entry.is_a?(Hash)
|
|
161
|
+
|
|
162
|
+
if entry[:wire] && entry[:to]
|
|
163
|
+
next { kind: 'map', wire: unquote(entry[:wire]), field: unquote(entry[:to]) }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
first_key = entry.keys.first
|
|
167
|
+
kind = first_key.to_s
|
|
168
|
+
body = entry[first_key] || {}
|
|
169
|
+
unless body.is_a?(Hash)
|
|
170
|
+
kind_n = normalize_mapping_kind(kind)
|
|
171
|
+
next nil if body.to_s.empty?
|
|
172
|
+
|
|
173
|
+
term = unquote(body)
|
|
174
|
+
next { kind: kind_n, wire: term, field: term } if %w[element attribute map].include?(kind_n)
|
|
175
|
+
if kind_n == 'namespace'
|
|
176
|
+
mapping[:namespace_ref] = term
|
|
177
|
+
next nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
next { kind: kind_n, field: term }
|
|
181
|
+
end
|
|
182
|
+
if kind == 'element_name'
|
|
183
|
+
mapping[:element_name] = unquote(body[:element_name])
|
|
184
|
+
next nil
|
|
185
|
+
end
|
|
186
|
+
if kind == 'map_namespace'
|
|
187
|
+
mapping[:namespace_ref] = unquote(body[:namespace_ref])
|
|
188
|
+
next nil
|
|
189
|
+
end
|
|
190
|
+
normalized = { kind: normalize_mapping_kind(kind) }
|
|
191
|
+
wire = body[:wire] || body[:element_wire]
|
|
192
|
+
normalized[:wire] = unquote(wire) if wire
|
|
193
|
+
field = body[:field] || body[:to_field] || body[:element_field] ||
|
|
194
|
+
body[:attribute_field] || body[:to]
|
|
195
|
+
normalized[:field] = unquote(field) if field
|
|
196
|
+
normalized[:mode] = body[:mode] if body[:mode]
|
|
197
|
+
normalized[:namespace_ref] = unquote(body[:namespace_ref]) if body[:namespace_ref]
|
|
198
|
+
normalized
|
|
199
|
+
end
|
|
200
|
+
mapping[:rules] = rules
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def normalize_mapping_kind(kind)
|
|
204
|
+
{
|
|
205
|
+
'map_element' => 'element', 'map_attribute' => 'attribute',
|
|
206
|
+
'map_content' => 'content', 'map_namespace' => 'namespace',
|
|
207
|
+
'map_key' => 'key', 'map_value' => 'value', 'map' => 'map',
|
|
208
|
+
'legacy_map' => 'map'
|
|
209
|
+
}.fetch(kind, kind)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# A named instances collection (`instances "Glazes" { ... }`) arrives
|
|
213
|
+
# with its name as a sibling of the collection body; move it inside
|
|
214
|
+
# so the InstanceCollection carries it.
|
|
215
|
+
def expand_instances_collection_name(hash)
|
|
216
|
+
return unless hash.key?(:instances) && hash.key?(:name)
|
|
217
|
+
|
|
218
|
+
name = unquote(hash.delete(:name))
|
|
219
|
+
body = hash[:instances].is_a?(Hash) ? hash[:instances] : {}
|
|
220
|
+
body[:name] = name unless body.key?(:name)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# The document root is a repetition of top-level constructs (RS 3001:
|
|
224
|
+
# documents interleave declarations), so models/packages blocks nest
|
|
225
|
+
# their members one level down; recurse into them.
|
|
226
|
+
def expand_nested_members(model, hash)
|
|
227
|
+
return unless hash.key?(:members)
|
|
228
|
+
|
|
229
|
+
members = hash.delete(:members)
|
|
230
|
+
members.to_a.each do |nested|
|
|
231
|
+
add_members(model, nested)
|
|
232
|
+
set_model_attributes(model, nested)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Declaration-level member hooks: single-line definitions, canonical
|
|
237
|
+
# `value` members, mapping sections, class declarations, enum
|
|
238
|
+
# member_fields and from_table all arrive as bare member hashes.
|
|
239
|
+
def expand_declared_members(model, hash)
|
|
240
|
+
expand_definition_text(model, hash)
|
|
241
|
+
expand_value_declaration(model, hash)
|
|
242
|
+
expand_value_set_declaration(model, hash)
|
|
243
|
+
expand_mapping_section(model, hash)
|
|
244
|
+
expand_class_declarations(model, hash)
|
|
245
|
+
expand_enum_member_fields(model, hash)
|
|
246
|
+
expand_from_table(model, hash)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# RS 3001 §Definition single-line form (`definition "text"`) arrives
|
|
250
|
+
# as a quoted-string capture; unwrap it for the entity's definition.
|
|
251
|
+
def expand_definition_text(model, hash)
|
|
252
|
+
return unless hash.key?(:definition) && model.class.attributes.key?(:definition)
|
|
253
|
+
|
|
254
|
+
model.definition = unquote(hash.delete(:definition))
|
|
255
|
+
end
|
|
256
|
+
|
|
119
257
|
# A mapping section arrives as a bare member hash
|
|
120
258
|
# { format:, maps: [{ wire:, to: }] }; expand to one Mapping entry
|
|
121
259
|
# per map line, each carrying the format, and strip the section keys
|
|
@@ -158,6 +296,23 @@ module Lutaml
|
|
|
158
296
|
type: unquote(derived[:type]))
|
|
159
297
|
end
|
|
160
298
|
|
|
299
|
+
# L7a: from_table declares the enum's members as an artifact table
|
|
300
|
+
# { from_table_name:, from_table_artifact? } — expansion happens at
|
|
301
|
+
# compile time (deterministic materialization, no runtime data dep)
|
|
302
|
+
def expand_from_table(model, hash)
|
|
303
|
+
decl = hash.delete(:from_table_decl)
|
|
304
|
+
return unless decl && model.is_a?(Enum)
|
|
305
|
+
|
|
306
|
+
# the members repetition splits the declaration across iteration
|
|
307
|
+
# hashes (name part, artifact part) — merge the parts
|
|
308
|
+
parts = decl.is_a?(Array) ? decl : [decl]
|
|
309
|
+
decl = parts.each_with_object({}) do |part, acc|
|
|
310
|
+
acc.merge!(part) { |_key, _old, new_val| new_val }
|
|
311
|
+
end
|
|
312
|
+
model.from_table_name = unquote(decl[:from_table_name])
|
|
313
|
+
model.from_table_artifact = unquote(decl[:artifact_table]) if decl[:artifact_table]
|
|
314
|
+
end
|
|
315
|
+
|
|
161
316
|
def expand_enum_member_fields(model, hash)
|
|
162
317
|
decl = hash.delete(:member_fields_decl)
|
|
163
318
|
return unless decl && model.is_a?(Enum)
|