opencdd 0.2.3 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0782b45aeee8bad692974cd0480dd8430f13c92f405760188a13df3e3b2c77ed'
4
- data.tar.gz: a4e26b1842e838b8e9464d0ff782ec745d59b96e9f5637e8630de9343218805d
3
+ metadata.gz: b88da823f5a7adb8f4d88f16a2f1e97d4f3136579bea025ef28c178753ab7f0e
4
+ data.tar.gz: 6171d27282b147b1c80fb137d8523d9644bb58fcaf364d13e89fee8713b7b814
5
5
  SHA512:
6
- metadata.gz: 12267bf271cabfeedb613766a6f679843d3be7fe542a96b097c9804ae7014c2a6fa5d96c2bafeeeefdaf7bbd9878ccde9aeca85423bbc40ad07ef3b7fb1428b7
7
- data.tar.gz: 23a8abce3684e130d3e7bff46973b579c3a71d9212153aea4f216314e6ab9ff7747b3a0030ecb1a0cd9134a3dab6dc83b782132d83a4feea8c2c36052994bfb0
6
+ metadata.gz: b373faf0cd5ac6d5e2d6f62af3e0a3a57abe2300589fd9d4c19135ccaf7ac28b385bf9946a4d090d178b293b15dfcce4a77c60a1b452d2e31cbf0ceab2f3ab89
7
+ data.tar.gz: b7ceac12c85b84943405c25fd04b112e5e282332b30c24ccc3367779a2a4bb3688c945efb81ee3e9641108e6b99e57d3c889f32e5dbad1249e50c9e2728cd344
@@ -24,7 +24,7 @@ module Opencdd
24
24
  :source_file, :loaded_modules
25
25
 
26
26
  def initialize(database = nil, resolver: nil, source_file: nil,
27
- loaded_modules: nil, loading_stack: nil)
27
+ loaded_modules: nil, loading_stack: nil, qualified_table: nil)
28
28
  @database = database || Opencdd::Database.new
29
29
  @resolver = resolver || Opencdd::Cddal.default_resolver
30
30
  @source_file = source_file
@@ -32,7 +32,7 @@ module Opencdd
32
32
  @loading_stack = loading_stack || []
33
33
  @alias_table = Opencdd::AliasTable.new(defaults: true)
34
34
  @symbol_table = {}
35
- @qualified_table = {} # "qualifier.name" => entity (qualified imports)
35
+ @qualified_table = qualified_table || {}
36
36
  @instance_decls = []
37
37
  @meta_class_overrides = {}
38
38
  end
@@ -108,84 +108,14 @@ module Opencdd
108
108
  # names are accessible without qualification.
109
109
 
110
110
  def apply_import_declarations(document)
111
- document.import_declarations.each { |decl| process_import(decl) }
112
- end
113
-
114
- def process_import(decl)
115
- canonical, source = resolve_specifier(decl.specifier)
116
- # Resolution may return [nil, nil] in non-strict mode when
117
- # the specifier points at an unreachable URL or a missing
118
- # file. Skip the import with a warning — preserves the
119
- # graceful-degradation behavior CDDAL authors expect for
120
- # cross-dictionary URLs.
121
- return if canonical.nil?
122
- return if @loaded_modules.key?(canonical)
123
- check_cycle!(canonical, decl.specifier)
124
-
125
- @loading_stack.push(canonical)
126
- sub_doc = Opencdd::Cddal::Parser.parse(source)
127
- Builder.new(@database, resolver: @resolver, source_file: canonical,
128
- loaded_modules: @loaded_modules,
129
- loading_stack: @loading_stack)
130
- .build(sub_doc)
131
- @loading_stack.pop
132
- @loaded_modules[canonical] = true
133
-
134
- # Symbol scoping is applied AFTER the sub-document is built,
135
- # so the named entities exist in @database by the time we
136
- # look them up.
137
- apply_import_scope(decl, canonical)
138
- end
139
-
140
- def resolve_specifier(specifier)
141
- @resolver.resolve(specifier, importing_file: @source_file)
142
- end
143
-
144
- def check_cycle!(canonical, specifier)
145
- return unless @loading_stack.include?(canonical)
146
- cycle = @loading_stack.dup
147
- cycle << canonical
148
- raise Opencdd::Cddal::ImportError,
149
- "circular CDDAL import detected: #{cycle.join(' → ')} " \
150
- "(originally imported as #{specifier.inspect})"
151
- end
152
-
153
- def apply_import_scope(decl, _canonical)
154
- case decl.kind
155
- when :bare
156
- # Nothing to do — bare imports leave the sub-document's
157
- # names in the shared @database symbol table.
158
- when :qualified
159
- register_qualified_symbols(decl.qualifier)
160
- when :selective
161
- register_selective_symbols(decl.imported_names)
162
- end
163
- end
164
-
165
- def register_qualified_symbols(qualifier)
166
- @database.entities.each do |entity|
167
- name = entity_alias_name(entity)
168
- next unless name
169
- qualified = "#{qualifier}.#{name}"
170
- @qualified_table[qualified] = entity
171
- @database.register_symbol(qualified, entity)
172
- end
173
- end
174
-
175
- def register_selective_symbols(imported_names)
176
- imported_names.each do |imported|
177
- entity = @database.resolve_reference(imported.name)
178
- next unless entity
179
- # Register in the parent Database's symbol table so the
180
- # renamed name resolves during link phase.
181
- @database.register_symbol(imported.local_name, entity)
182
- end
183
- end
184
-
185
- def entity_alias_name(entity)
186
- code = entity.code
187
- return code if code && !code.empty?
188
- entity.preferred_name.to_s
111
+ Opencdd::Cddal::ImportPipeline.new(
112
+ database: @database,
113
+ resolver: @resolver,
114
+ source_file: @source_file,
115
+ loaded_modules: @loaded_modules,
116
+ loading_stack: @loading_stack,
117
+ qualified_table: @qualified_table,
118
+ ).process(document.import_declarations)
189
119
  end
190
120
 
191
121
  def register_instance_symbols(document)
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Opencdd
4
+ module Cddal
5
+ # Handles CDDAL module imports: resolution, cycle detection,
6
+ # recursive sub-document building, and symbol scoping.
7
+ #
8
+ # Extracted from Builder so the import pipeline has its own
9
+ # testable interface. Builder creates an ImportPipeline and
10
+ # delegates +apply_import_declarations+ to it.
11
+ #
12
+ # The pipeline holds shared mutable state (loaded_modules,
13
+ # loading_stack) that persists across recursive sub-builds.
14
+ # This state is passed by reference from the parent Builder
15
+ # so all levels of the import tree share the same cycle
16
+ # tracking.
17
+ class ImportPipeline
18
+ def initialize(database:, resolver:, source_file:, loaded_modules:, loading_stack:, qualified_table:)
19
+ @database = database
20
+ @resolver = resolver
21
+ @source_file = source_file
22
+ @loaded_modules = loaded_modules
23
+ @loading_stack = loading_stack
24
+ @qualified_table = qualified_table
25
+ end
26
+
27
+ def process(import_declarations)
28
+ import_declarations.each { |decl| process_import(decl) }
29
+ end
30
+
31
+ private
32
+
33
+ def process_import(decl)
34
+ canonical, source = @resolver.resolve(decl.specifier, importing_file: @source_file)
35
+ return if canonical.nil?
36
+ return if @loaded_modules.key?(canonical)
37
+ check_cycle!(canonical, decl.specifier)
38
+
39
+ @loading_stack.push(canonical)
40
+ sub_doc = Opencdd::Cddal::Parser.parse(source)
41
+ Builder.new(@database, resolver: @resolver, source_file: canonical,
42
+ loaded_modules: @loaded_modules,
43
+ loading_stack: @loading_stack,
44
+ qualified_table: @qualified_table)
45
+ .build(sub_doc)
46
+ @loading_stack.pop
47
+ @loaded_modules[canonical] = true
48
+
49
+ apply_import_scope(decl)
50
+ end
51
+
52
+ def check_cycle!(canonical, specifier)
53
+ return unless @loading_stack.include?(canonical)
54
+ cycle = @loading_stack.dup
55
+ cycle << canonical
56
+ raise Opencdd::Cddal::ImportError,
57
+ "circular CDDAL import detected: #{cycle.join(' → ')} " \
58
+ "(originally imported as #{specifier.inspect})"
59
+ end
60
+
61
+ def apply_import_scope(decl)
62
+ case decl.kind
63
+ when :bare
64
+ # Bare imports leave names in the shared symbol table.
65
+ when :qualified
66
+ register_qualified_symbols(decl.qualifier)
67
+ when :selective
68
+ register_selective_symbols(decl.imported_names)
69
+ end
70
+ end
71
+
72
+ def register_qualified_symbols(qualifier)
73
+ @database.entities.each do |entity|
74
+ name = entity_alias_name(entity)
75
+ next unless name
76
+ qualified = "#{qualifier}.#{name}"
77
+ @qualified_table[qualified] = entity
78
+ @database.register_symbol(qualified, entity)
79
+ end
80
+ end
81
+
82
+ def register_selective_symbols(imported_names)
83
+ imported_names.each do |imported|
84
+ entity = @database.resolve_reference(imported.name)
85
+ next unless entity
86
+ @database.register_symbol(imported.local_name, entity)
87
+ end
88
+ end
89
+
90
+ def entity_alias_name(entity)
91
+ code = entity.code
92
+ return code if code && !code.empty?
93
+ entity.preferred_name.to_s
94
+ end
95
+ end
96
+ end
97
+ end
data/lib/opencdd/cddal.rb CHANGED
@@ -7,6 +7,7 @@ module Opencdd
7
7
  autoload :Parser, "opencdd/cddal/parser"
8
8
  autoload :GeneratedParser, "opencdd/cddal/generated_parser"
9
9
  autoload :Builder, "opencdd/cddal/builder"
10
+ autoload :ImportPipeline, "opencdd/cddal/import_pipeline"
10
11
  autoload :Serializer, "opencdd/cddal/serializer"
11
12
  autoload :Resolver, "opencdd/cddal/resolver"
12
13
  autoload :Fetcher, "opencdd/cddal/fetcher"
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Opencdd
4
+ class Entity
5
+ # Reverse name-to-ID resolution for Parcel sheets that omit the
6
+ # #PROPERTY_ID directive row. Some cdd.iec.ch export formats
7
+ # (notably RELATION exports) include only #PROPERTY_NAME.en,
8
+ # leaving the importer unable to map column positions to property
9
+ # IDs. NameSynthesizer bridges that gap using the IEC 62656-1
10
+ # defined column names.
11
+ #
12
+ # Extracted from SheetSchema so SheetSchema focuses on header
13
+ # parsing + Column construction. Name synthesis is a separate
14
+ # concern with its own test surface.
15
+ module NameSynthesizer
16
+ # IEC 62656-1 defined column names mapped to canonical
17
+ # property IDs. These names appear in the #PROPERTY_NAME
18
+ # directive row when #PROPERTY_ID is absent.
19
+ NAME_TO_PROPERTY_ID = {
20
+ "Code" => "MDC_P001_5",
21
+ "Version" => "MDC_P002_1",
22
+ "Revision" => "MDC_P002_2",
23
+ "VersionInitiationDate" => "MDC_P003_1",
24
+ "VersionReleaseDate" => "MDC_P003_2",
25
+ "RevisionReleaseDate" => "MDC_P003_3",
26
+ "PreferredName" => "MDC_P004",
27
+ "SynonymousName" => "MDC_P007",
28
+ "ShortName" => "MDC_P005",
29
+ "Definition" => "MDC_P006",
30
+ "DefinitionSource" => "MDC_P006_1",
31
+ "Note" => "MDC_P008",
32
+ "Remark" => "MDC_P009",
33
+ "Drawing" => "MDC_P008_1",
34
+ "GUID" => "MDC_P066",
35
+ "TimeStamp" => "MDC_P067",
36
+ "ClassType" => "MDC_P011",
37
+ "Superclass" => "MDC_P010",
38
+ "IsCaseOf" => "MDC_P013",
39
+ "ApplicableProperties" => "MDC_P014",
40
+ "ImportedProperties" => "MDC_P090",
41
+ "SubClassSelection" => "MDC_P016",
42
+ "DataType" => "MDC_P022",
43
+ "ValueFormat" => "MDC_P024",
44
+ "DefinitionClass" => "MDC_P021",
45
+ "Unit" => "MDC_P041",
46
+ "Condition" => "MDC_P028",
47
+ "ListType" => "MDC_P046",
48
+ "CodeList" => "MDC_P044",
49
+ "TermList" => "MDC_P043",
50
+ "EnumerationCode" => "MDC_P044",
51
+ "RelationType" => "MDC_P200",
52
+ "RelationDomain" => "MDC_P201",
53
+ "FunctionDomain" => "MDC_P202",
54
+ "FunctionCodomain" => "MDC_P203",
55
+ "Formula" => "MDC_P204",
56
+ "FormulaLanguage" => "MDC_P205",
57
+ "FormulaExternalSolver" => "MDC_P206",
58
+ "TriggerEvent" => "MDC_P207",
59
+ "DomainElementType" => "MDC_P208",
60
+ "CodomainElementType" => "MDC_P209",
61
+ "Role" => "MDC_P210",
62
+ "Segment" => "MDC_P211",
63
+ "SuperRelation" => "MDC_P212",
64
+ }.freeze
65
+
66
+ # Given a PROPERTY_NAME directive row (Array of name strings),
67
+ # returns a parallel Array of property IDs (or nil where the
68
+ # name is unrecognized). Language suffixes (.EN, .FR) are
69
+ # stripped for lookup and re-appended to the synthesized ID.
70
+ #
71
+ # synthesize(["Code", "PreferredName.EN", "Unknown"])
72
+ # => ["MDC_P001_5", "MDC_P004.en", nil]
73
+ def self.synthesize(name_values)
74
+ Array.new(name_values.size) do |idx|
75
+ val = name_values[idx]
76
+ next nil if val.nil? || val.to_s.strip.empty?
77
+ raw = val.to_s.strip
78
+ if raw =~ /\A(.+)\.([A-Za-z]{2})\z/
79
+ base = $1
80
+ lang = $2
81
+ pid = NAME_TO_PROPERTY_ID[base]
82
+ pid ? "#{pid}.#{lang.downcase}" : nil
83
+ else
84
+ NAME_TO_PROPERTY_ID[raw]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -96,55 +96,52 @@ module Opencdd
96
96
  # ── Catch-all for unknown properties (lossless) ────────
97
97
  attribute :extra, :hash
98
98
 
99
+ # Fields that should NOT be driven from FieldRegistry in
100
+ # from_entity — they're either identity fields (irdi, code)
101
+ # or catch-all (extra, raw_properties, version_history, etc.)
102
+ # handled separately.
103
+ SKIP_FIELDS = %i[
104
+ raw_properties version_history dates
105
+ source_document_of_definition synonymous_names
106
+ simplified_drawing example time_stamp
107
+ status_level publisher published_in responsible_committee
108
+ change_request_id data_object_identifier
109
+ alternative_unit_irdis domain_of_function_irdis
110
+ formula_language external_solver trigger_event
111
+ domain_element_type codomain_element_type role segment
112
+ super_relation_irdi sgml_representation
113
+ unit_sgml class_value_assignment coded_name
114
+ symbol_in_text constraint type_classification
115
+ applicable_documents imported_documents
116
+ superclass_type_property
117
+ ].freeze
118
+
99
119
  # ── Conversion: Entity → Entity::Yaml ───────────────────
120
+ # Drives from FieldRegistry — the same SSOT that drives the
121
+ # JSON exporter. Each field's wire_name (from the `as:` DSL
122
+ # option) determines the YAML attribute to set. Adding a new
123
+ # field requires zero changes here if the wire_name matches
124
+ # a declared YAML attribute.
100
125
  def self.from_entity(entity)
101
126
  attrs = {
102
127
  irdi: entity.irdi&.to_s,
103
128
  type: entity.type&.to_s,
104
129
  code: entity.code,
105
- guid: entity[Opencdd::PropertyIds::MDC_P066],
106
- version: entity[Opencdd::PropertyIds::MDC_P002_1],
107
- revision: entity[Opencdd::PropertyIds::MDC_P002_2],
108
130
  }
109
131
 
110
- attrs[:preferred_name] = extract_ml(entity, Opencdd::PropertyIds::MDC_P004)
111
- attrs[:short_name] = extract_ml(entity, Opencdd::PropertyIds::MDC_P005)
112
- attrs[:definition] = extract_ml(entity, Opencdd::PropertyIds::MDC_P006)
113
- attrs[:note] = extract_ml(entity, Opencdd::PropertyIds::MDC_P008)
114
- attrs[:remark] = extract_ml(entity, Opencdd::PropertyIds::MDC_P009)
115
- attrs[:description] = extract_ml(entity, Opencdd::PropertyIds::MDC_P112)
116
-
117
- case entity.type
118
- when :class
119
- attrs[:class_type] = entity.read_field(:class_type)&.to_s
120
- attrs[:superclass] = entity.read_field(:superclass_irdi)&.to_s
121
- attrs[:is_case_of] = entity.read_field(:is_case_of_irdis)&.map(&:to_s) || []
122
- attrs[:applicable_properties] = entity.read_field(:applicable_property_irdis)&.map(&:to_s) || []
123
- attrs[:imported_properties] = entity.read_field(:imported_property_irdis)&.map(&:to_s) || []
124
- attrs[:sub_class_selection] = entity.read_field(:sub_class_selection_irdis)&.map(&:to_s) || []
125
- when :property
126
- attrs[:data_type] = entity.read_field(:parsed_data_type)&.to_s
127
- attrs[:value_format] = entity.read_field(:parsed_value_format)&.to_s
128
- attrs[:definition_class] = entity.read_field(:definition_class_irdi)&.to_s
129
- attrs[:unit] = entity.read_field(:unit_irdi)&.to_s
130
- attrs[:condition] = entity.read_field(:condition)&.to_s
131
- attrs[:property_data_element_type] = entity.read_field(:property_data_element_type)&.to_s
132
- when :unit
133
- attrs[:unit_structure] = entity.read_field(:structure)
134
- attrs[:unit_text] = entity.read_field(:text_representation)
135
- when :value_list
136
- attrs[:list_type] = entity.read_field(:list_type)&.to_s
137
- attrs[:code_list] = entity.read_field(:code_list) || []
138
- attrs[:term_irdis] = entity.read_field(:term_irdis)&.map(&:to_s) || []
139
- when :value_term
140
- attrs[:enumeration_code] = entity.read_field(:enumeration_code)
141
- when :relation
142
- attrs[:relation_type] = entity.read_field(:relation_type)&.to_s
143
- attrs[:codomain] = entity.read_field(:codomain_irdi)&.to_s
144
- attrs[:formula] = entity.read_field(:formula)
145
- when :view_control
146
- attrs[:controlled_classes] = entity.read_field(:controlled_class_irdis)&.map(&:to_s) || []
147
- attrs[:shown_properties] = entity.read_field(:shown_property_irdis)&.map(&:to_s) || []
132
+ declared_attrs = attribute_names
133
+ seen_property_ids = {}
134
+
135
+ Opencdd::Entity::FieldRegistry.fields_for(entity.class).each do |field|
136
+ next if SKIP_FIELDS.include?(field.name)
137
+ next unless declared_attrs.include?(field.wire_name)
138
+ next if field.property_id && seen_property_ids.key?(field.property_id)
139
+ seen_property_ids[field.property_id] = true if field.property_id
140
+
141
+ value = read_field_for_yaml(entity, field)
142
+ next if value.nil?
143
+
144
+ attrs[field.wire_name.to_sym] = value
148
145
  end
149
146
 
150
147
  extra = extract_extra(entity)
@@ -186,10 +183,17 @@ module Opencdd
186
183
  props[Opencdd::PropertyIds::MDC_P023_1] = unit_text if unit_text
187
184
 
188
185
  props[Opencdd::PropertyIds::MDC_P046] = list_type if list_type
189
- props[Opencdd::PropertyIds::MDC_P044] = rejoin_set(code_list) if code_list&.any?
190
186
  props[Opencdd::PropertyIds::MDC_P043] = rejoin_set(term_irdis) if term_irdis&.any?
191
187
 
192
- props[Opencdd::PropertyIds::MDC_P044] = enumeration_code if enumeration_code
188
+ # MDC_P044 is overloaded: code_list (value_list, collection)
189
+ # vs enumeration_code (value_term, scalar). Guard by type to
190
+ # prevent the value_term path from clobbering the value_list.
191
+ case type&.to_sym
192
+ when :value_list
193
+ props[Opencdd::PropertyIds::MDC_P044] = rejoin_set(code_list) if code_list&.any?
194
+ when :value_term
195
+ props[Opencdd::PropertyIds::MDC_P044] = enumeration_code if enumeration_code
196
+ end
193
197
 
194
198
  props[Opencdd::PropertyIds::MDC_P200] = relation_type if relation_type
195
199
  props[Opencdd::PropertyIds::MDC_P203] = codomain if codomain
@@ -229,7 +233,7 @@ module Opencdd
229
233
  MDC_P011 MDC_P010 MDC_P010_1
230
234
  MDC_P013 MDC_P014 MDC_P090 MDC_P016
231
235
  MDC_P022 MDC_P024 MDC_P021 MDC_P041 MDC_P028 MDC_P020
232
- MDC_P023 MDC_P023_1
236
+ MDC_P023 MDC_P023_1 MDC_P023_2
233
237
  MDC_P046 MDC_P044 MDC_P043
234
238
  MDC_P200 MDC_P203 MDC_P204
235
239
  EXT_P002 EXT_P003
@@ -262,6 +266,53 @@ module Opencdd
262
266
  def rejoin_set(list)
263
267
  Opencdd::StructuredValues.rejoin(list)
264
268
  end
269
+
270
+ # ── FieldRegistry-driven value conversion ───────────────
271
+ # For multilingual fields, returns the full Hash<lang,text>
272
+ # by scanning @properties for <property_id>.<lang> keys.
273
+ # For scalar/collection fields, reads through the field DSL
274
+ # and converts to YAML-compatible types.
275
+ def self.read_field_for_yaml(entity, field)
276
+ if field.multilingual? && field.property_id
277
+ extract_ml(entity, field.property_id)
278
+ else
279
+ value = entity.read_field(field.name)
280
+ return nil if value.nil?
281
+ field_to_yaml(value, field)
282
+ end
283
+ end
284
+
285
+ def self.field_to_yaml(value, field)
286
+ case field.value_kind
287
+ when :synonym_pairs
288
+ return nil if value.nil?
289
+ value.transform_keys(&:to_s)
290
+ when :irdi, :identifier_ref, :class_ref
291
+ value&.to_s
292
+ when :set_of_refs, :string_list
293
+ return nil if value.nil? || value.empty?
294
+ value.map(&:to_s)
295
+ when :string, :date, :date_time, :condition
296
+ value&.to_s
297
+ when :integer
298
+ value
299
+ when :boolean
300
+ value
301
+ else
302
+ case value
303
+ when Array then value.empty? ? nil : value.map(&:to_s)
304
+ when Hash then value.empty? ? nil : value
305
+ else value.to_s
306
+ end
307
+ end
308
+ end
309
+
310
+ # Returns the list of declared lutaml-model attribute names.
311
+ # Used by from_entity to check if a field's wire_name matches
312
+ # a declared YAML attribute.
313
+ def self.attribute_names
314
+ attributes.map { |name, _| name.to_s }
315
+ end
265
316
  end
266
317
  end
267
318
  end
@@ -15,6 +15,7 @@ module Opencdd
15
15
  autoload :FieldReader, "opencdd/entity/field_reader"
16
16
  autoload :VersionHistory, "opencdd/entity/version_history"
17
17
  autoload :Yaml, "opencdd/entity/yaml"
18
+ autoload :NameSynthesizer, "opencdd/entity/name_synthesizer"
18
19
 
19
20
  class << self
20
21
  # Declare a typed field on this entity class. Defaults for
@@ -197,57 +197,8 @@ module Opencdd
197
197
  freeze
198
198
  end
199
199
 
200
- # When a sheet has no #PROPERTY_ID directive row (only
201
- # #PROPERTY_NAME), synthesize property IDs from the names.
202
- # This happens with some export formats (notably RELATION
203
- # exports from cdd.iec.ch) that omit the PROPERTY_ID row.
204
- NAME_TO_PROPERTY_ID = {
205
- "Code" => "MDC_P001_5",
206
- "Version" => "MDC_P002_1",
207
- "Revision" => "MDC_P002_2",
208
- "VersionInitiationDate" => "MDC_P003_1",
209
- "VersionReleaseDate" => "MDC_P003_2",
210
- "RevisionReleaseDate" => "MDC_P003_3",
211
- "PreferredName" => "MDC_P004",
212
- "SynonymousName" => "MDC_P007",
213
- "ShortName" => "MDC_P005",
214
- "Definition" => "MDC_P006",
215
- "DefinitionSource" => "MDC_P006_1",
216
- "Note" => "MDC_P008",
217
- "Remark" => "MDC_P009",
218
- "Drawing" => "MDC_P008_1",
219
- "GUID" => "MDC_P066",
220
- "TimeStamp" => "MDC_P067",
221
- "ClassType" => "MDC_P011",
222
- "Superclass" => "MDC_P010",
223
- "IsCaseOf" => "MDC_P013",
224
- "ApplicableProperties" => "MDC_P014",
225
- "ImportedProperties" => "MDC_P090",
226
- "SubClassSelection" => "MDC_P016",
227
- "DataType" => "MDC_P022",
228
- "ValueFormat" => "MDC_P024",
229
- "DefinitionClass" => "MDC_P021",
230
- "Unit" => "MDC_P041",
231
- "Condition" => "MDC_P028",
232
- "ListType" => "MDC_P046",
233
- "CodeList" => "MDC_P044",
234
- "TermList" => "MDC_P043",
235
- "EnumerationCode" => "MDC_P044",
236
- "RelationType" => "MDC_P200",
237
- "RelationDomain" => "MDC_P201",
238
- "FunctionDomain" => "MDC_P202",
239
- "FunctionCodomain" => "MDC_P203",
240
- "Formula" => "MDC_P204",
241
- "FormulaLanguage" => "MDC_P205",
242
- "FormulaExternalSolver" => "MDC_P206",
243
- "TriggerEvent" => "MDC_P207",
244
- "DomainElementType" => "MDC_P208",
245
- "CodomainElementType" => "MDC_P209",
246
- "Role" => "MDC_P210",
247
- "Segment" => "MDC_P211",
248
- "SuperRelation" => "MDC_P212",
249
- }.freeze
250
-
200
+ # Delegates to Entity::NameSynthesizer when the sheet has no
201
+ # #PROPERTY_ID directive row. See name_synthesizer.rb.
251
202
  def synthesize_ids_from_names
252
203
  names = nil
253
204
  @column_directives.each do |key, vals|
@@ -257,19 +208,7 @@ module Opencdd
257
208
  break
258
209
  end
259
210
  return [] unless names
260
- Array.new(names.size) do |idx|
261
- val = names[idx]
262
- next nil if val.nil? || val.to_s.strip.empty?
263
- raw = val.to_s.strip
264
- if raw =~ /\A(.+)\.([A-Za-z]{2})\z/
265
- base = $1
266
- lang = $2
267
- pid = NAME_TO_PROPERTY_ID[base]
268
- pid ? "#{pid}.#{lang.downcase}" : nil
269
- else
270
- NAME_TO_PROPERTY_ID[raw]
271
- end
272
- end
211
+ Opencdd::Entity::NameSynthesizer.synthesize(names)
273
212
  end
274
213
 
275
214
  def [](property_id_or_name)
@@ -37,10 +37,10 @@ module Opencdd
37
37
  field :type_classification, "MDC_P033", :string
38
38
  field :source_document, "MDC_P006_1", :string
39
39
  field :coded_name, "MDC_P018", :string
40
- field :definition_class_irdi, "MDC_P021"
40
+ field :definition_class_irdi, "MDC_P021", as: "definition_class"
41
41
  field :class_value_assignment, "MDC_P017"
42
- field :unit_irdi, "MDC_P041"
43
- field :alternative_unit_irdis, "MDC_P042"
42
+ field :unit_irdi, "MDC_P041", as: "unit"
43
+ field :alternative_unit_irdis, "MDC_P042", as: "alternative_units"
44
44
 
45
45
  # ── Computed fields with block-form readers (synthetic: true).
46
46
  # Blocks are evaluated via instance_exec on the entity, so
@@ -57,11 +57,11 @@ module Opencdd
57
57
  Opencdd::PropertyDataTypeElement.parse(properties[Opencdd::PropertyIds::MDC_P020])
58
58
  }
59
59
 
60
- field :parsed_data_type, synthetic: true, &PARSED_DATA_TYPE_BLOCK
60
+ field :parsed_data_type, synthetic: true, as: "data_type", &PARSED_DATA_TYPE_BLOCK
61
61
  field :data_element_type, synthetic: true, as: "data_element_type", &DATA_ELEMENT_TYPE_BLOCK
62
62
  field :property_data_element_type,
63
- synthetic: true, &DATA_ELEMENT_TYPE_BLOCK
64
- field :parsed_value_format, synthetic: true do
63
+ synthetic: true, as: "property_data_element_type", &DATA_ELEMENT_TYPE_BLOCK
64
+ field :parsed_value_format, synthetic: true, as: "value_format" do
65
65
  Opencdd::ValueFormat.parse(properties[Opencdd::PropertyIds::MDC_P024])
66
66
  end
67
67
  field :condition_raw, "MDC_P028", synthetic: true do
@@ -8,8 +8,8 @@ module Opencdd
8
8
  }.freeze
9
9
 
10
10
  # ── Pure field reads ─────────────────────────────────────────
11
- field :domain_of_function_irdis, "MDC_P202"
12
- field :codomain_irdi, "MDC_P203"
11
+ field :domain_of_function_irdis, "MDC_P202", as: "domain_of_function"
12
+ field :codomain_irdi, "MDC_P203", as: "codomain"
13
13
  field :formula, "MDC_P204", :string
14
14
  field :formula_language, "MDC_P205", :string
15
15
  field :external_solver, "MDC_P206", :string
data/lib/opencdd/unit.rb CHANGED
@@ -6,10 +6,10 @@ module Opencdd
6
6
  # MDC_P023/023_1/023_2 are :identifier_ref in REGISTRY but used
7
7
  # as raw strings for unit symbols/representations. Explicit :string
8
8
  # override preserves historical behavior.
9
- field :structure, "MDC_P023", :string
10
- field :text_representation, "MDC_P023_1", :string
11
- field :sgml_representation, "MDC_P023_2", :string
12
- field :definition_class_irdi, "MDC_P021"
9
+ field :structure, "MDC_P023", :string, as: "unit_structure"
10
+ field :text_representation, "MDC_P023_1", :string, as: "unit_text"
11
+ field :sgml_representation, "MDC_P023_2", :string, as: "unit_sgml"
12
+ field :definition_class_irdi, "MDC_P021", as: "definition_class"
13
13
 
14
14
  alias_method :symbol, :text_representation
15
15
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opencdd
4
- VERSION = "0.2.3"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -3,8 +3,8 @@
3
3
  module Opencdd
4
4
  class ViewControl < Opencdd::Entity
5
5
  # ── Pure field reads ─────────────────────────────────────────
6
- field :controlled_class_irdis, "EXT_P002"
7
- field :shown_property_irdis, "EXT_P003"
6
+ field :controlled_class_irdis, "EXT_P002", as: "controlled_classes"
7
+ field :shown_property_irdis, "EXT_P003", as: "shown_properties"
8
8
  # data_object_identifier and time_stamp inherited from Entity base.
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opencdd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenCDD contributors
@@ -217,6 +217,7 @@ files:
217
217
  - lib/opencdd/cddal/fetcher/in_memory.rb
218
218
  - lib/opencdd/cddal/fetcher/net_http.rb
219
219
  - lib/opencdd/cddal/generated_parser.rb
220
+ - lib/opencdd/cddal/import_pipeline.rb
220
221
  - lib/opencdd/cddal/lexer.rb
221
222
  - lib/opencdd/cddal/parser.rb
222
223
  - lib/opencdd/cddal/resolver.rb
@@ -239,6 +240,7 @@ files:
239
240
  - lib/opencdd/entity.rb
240
241
  - lib/opencdd/entity/field_reader.rb
241
242
  - lib/opencdd/entity/field_registry.rb
243
+ - lib/opencdd/entity/name_synthesizer.rb
242
244
  - lib/opencdd/entity/version_history.rb
243
245
  - lib/opencdd/entity/yaml.rb
244
246
  - lib/opencdd/entity_diff.rb