opencdd 0.1.1 → 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.
@@ -1,16 +1,28 @@
1
- # frozen_string: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
4
 
5
5
  module Opencdd
6
- module Model
7
- # CDD-native YAML entity. Every attribute uses the semantic name
8
- # from the CDD ontology (preferred_name, superclass, class_type)
9
- # rather than the IEC 62656-1 wire-format key (MDC_P004, MDC_P010,
10
- # MDC_P011). Multilingual fields are Hash<String, String>;
11
- # collection fields are Array<String>.
6
+ class Entity < Lutaml::Model::Serializable
7
+ # CDD-native YAML serialization model.
12
8
  #
13
- # This is the canonical YAML shape for a CDD entity:
9
+ # This is the deepened YAML adapter: the conversion logic that
10
+ # translates between semantic CDD attribute names (preferred_name,
11
+ # superclass, class_type) and IEC 62656-1 wire-format keys
12
+ # (MDC_P004, MDC_P010, MDC_P011) lives here, inside Entity's
13
+ # namespace. Entity delegates +to_yaml+ / +from_yaml+ to this
14
+ # class.
15
+ #
16
+ # Why a separate class? lutaml-model serialization calls
17
+ # +public_send(attr_name)+ during +to_format+, which invokes the
18
+ # getter method. Entity's field DSL getters read from +@properties+
19
+ # and return domain objects (IRDI, source-language String, parsed
20
+ # Array). The YAML attrs need flat types (String, Hash, Array).
21
+ # Sharing the same name would make the getter return the wrong
22
+ # type for serialization. Keeping the YAML model here avoids that
23
+ # conflict while keeping the adapter "inside" Entity.
24
+ #
25
+ # Canonical YAML shape:
14
26
  #
15
27
  # ---
16
28
  # irdi: 0112/2///61360_4#AAA001
@@ -20,15 +32,12 @@ module Opencdd
20
32
  # en: Vehicle
21
33
  # fr: Véhicule
22
34
  # class_type: ITEM_CLASS
23
- # superclass: UNIVERSE
35
+ # superclass: 0112/2///61360_4#AAA000
24
36
  # applicable_properties:
25
- # - vehicle_length
26
- # - vehicle_weight
27
- #
28
- # Conversion between YamlEntity and the existing Entity model
29
- # is via #from_entity and #to_entity, which bridge the semantic
30
- # attributes to the raw properties Hash.
31
- class YamlEntity < Lutaml::Model::Serializable
37
+ # - 0112/2///61360_4#AAAP001
38
+ # extra:
39
+ # C016: released
40
+ class Yaml < Lutaml::Model::Serializable
32
41
  # ── Identity ────────────────────────────────────────────
33
42
  attribute :irdi, :string
34
43
  attribute :type, :string # :class, :property, :unit, ...
@@ -41,6 +50,7 @@ module Opencdd
41
50
  attribute :definition, :hash
42
51
  attribute :note, :hash
43
52
  attribute :remark, :hash
53
+ attribute :description, :hash
44
54
 
45
55
  # ── Version ─────────────────────────────────────────────
46
56
  attribute :version, :string
@@ -67,18 +77,26 @@ module Opencdd
67
77
  attribute :unit_text, :string
68
78
 
69
79
  # ── Value-list-specific ─────────────────────────────────
70
- attribute :enumerated_values, :string, collection: true
71
80
  attribute :list_type, :string
81
+ attribute :code_list, :string, collection: true
82
+ attribute :term_irdis, :string, collection: true
83
+
84
+ # ── Value-term-specific ─────────────────────────────────
85
+ attribute :enumeration_code, :string
72
86
 
73
87
  # ── Relation-specific ───────────────────────────────────
74
88
  attribute :relation_type, :string
75
- attribute :domain, :string, collection: true
76
89
  attribute :codomain, :string
90
+ attribute :formula, :string
91
+
92
+ # ── View-control-specific ───────────────────────────────
93
+ attribute :controlled_classes, :string, collection: true
94
+ attribute :shown_properties, :string, collection: true
77
95
 
78
96
  # ── Catch-all for unknown properties (lossless) ────────
79
97
  attribute :extra, :hash
80
98
 
81
- # ── Conversion: Entity → YamlEntity ────────────────────
99
+ # ── Conversion: Entity → Entity::Yaml ───────────────────
82
100
  def self.from_entity(entity)
83
101
  attrs = {
84
102
  irdi: entity.irdi&.to_s,
@@ -89,64 +107,74 @@ module Opencdd
89
107
  revision: entity[Opencdd::PropertyIds::MDC_P002_2],
90
108
  }
91
109
 
92
- # Multilingual fields
93
110
  attrs[:preferred_name] = extract_ml(entity, Opencdd::PropertyIds::MDC_P004)
94
111
  attrs[:short_name] = extract_ml(entity, Opencdd::PropertyIds::MDC_P005)
95
112
  attrs[:definition] = extract_ml(entity, Opencdd::PropertyIds::MDC_P006)
96
113
  attrs[:note] = extract_ml(entity, Opencdd::PropertyIds::MDC_P008)
97
114
  attrs[:remark] = extract_ml(entity, Opencdd::PropertyIds::MDC_P009)
115
+ attrs[:description] = extract_ml(entity, Opencdd::PropertyIds::MDC_P112)
98
116
 
99
- # Class-specific
100
- if entity.type == :class
101
- ct = entity.read_field(:class_type)
102
- attrs[:class_type] = ct&.to_s
117
+ case entity.type
118
+ when :class
119
+ attrs[:class_type] = entity.read_field(:class_type)&.to_s
103
120
  attrs[:superclass] = entity.read_field(:superclass_irdi)&.to_s
104
121
  attrs[:is_case_of] = entity.read_field(:is_case_of_irdis)&.map(&:to_s) || []
105
122
  attrs[:applicable_properties] = entity.read_field(:applicable_property_irdis)&.map(&:to_s) || []
106
123
  attrs[:imported_properties] = entity.read_field(:imported_property_irdis)&.map(&:to_s) || []
107
124
  attrs[:sub_class_selection] = entity.read_field(:sub_class_selection_irdis)&.map(&:to_s) || []
108
- end
109
-
110
- # Property-specific
111
- if entity.type == :property
112
- dt = entity.read_field(:parsed_data_type)
113
- attrs[:data_type] = dt&.to_s
125
+ when :property
126
+ attrs[:data_type] = entity.read_field(:parsed_data_type)&.to_s
114
127
  attrs[:value_format] = entity.read_field(:parsed_value_format)&.to_s
115
- dc = entity.read_field(:definition_class_irdi)
116
- attrs[:definition_class] = dc&.to_s
117
- ui = entity.read_field(:unit_irdi)
118
- attrs[:unit] = ui&.to_s
128
+ attrs[:definition_class] = entity.read_field(:definition_class_irdi)&.to_s
129
+ attrs[:unit] = entity.read_field(:unit_irdi)&.to_s
119
130
  attrs[:condition] = entity.read_field(:condition)&.to_s
120
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) || []
121
148
  end
122
149
 
150
+ extra = extract_extra(entity)
151
+ attrs[:extra] = extra if extra
152
+
123
153
  new(**attrs.compact)
124
154
  end
125
155
 
126
- # ── Conversion: YamlEntity → Entity ────────────────────
127
- def to_entity(database = nil)
156
+ # ── Conversion: Entity::Yaml → Entity ───────────────────
157
+ def to_entity(_database = nil)
128
158
  props = {}
129
159
 
130
160
  props[Opencdd::PropertyIds::MDC_P066] = guid if guid
131
161
  props[Opencdd::PropertyIds::MDC_P002_1] = version if version
132
162
  props[Opencdd::PropertyIds::MDC_P002_2] = revision if revision
133
163
 
134
- # Multilingual
135
164
  merge_ml_into(props, Opencdd::PropertyIds::MDC_P004, preferred_name)
136
165
  merge_ml_into(props, Opencdd::PropertyIds::MDC_P005, short_name)
137
166
  merge_ml_into(props, Opencdd::PropertyIds::MDC_P006, definition)
138
167
  merge_ml_into(props, Opencdd::PropertyIds::MDC_P008, note)
139
168
  merge_ml_into(props, Opencdd::PropertyIds::MDC_P009, remark)
169
+ merge_ml_into(props, Opencdd::PropertyIds::MDC_P112, description)
140
170
 
141
- # Class-specific
142
171
  props[Opencdd::PropertyIds::MDC_P011] = class_type if class_type
143
172
  props[Opencdd::PropertyIds::MDC_P010] = superclass if superclass
144
- props[Opencdd::PropertyIds::MDC_P013] = "{#{is_case_of.join(",")}}" if is_case_of&.any?
145
- props[Opencdd::PropertyIds::MDC_P014] = "{#{applicable_properties.join(",")}}" if applicable_properties&.any?
146
- props[Opencdd::PropertyIds::MDC_P090] = "{#{imported_properties.join(",")}}" if imported_properties&.any?
147
- props[Opencdd::PropertyIds::MDC_P016] = "{#{sub_class_selection.join(",")}}" if sub_class_selection&.any?
173
+ props[Opencdd::PropertyIds::MDC_P013] = rejoin_set(is_case_of) if is_case_of&.any?
174
+ props[Opencdd::PropertyIds::MDC_P014] = rejoin_set(applicable_properties) if applicable_properties&.any?
175
+ props[Opencdd::PropertyIds::MDC_P090] = rejoin_set(imported_properties) if imported_properties&.any?
176
+ props[Opencdd::PropertyIds::MDC_P016] = rejoin_set(sub_class_selection) if sub_class_selection&.any?
148
177
 
149
- # Property-specific
150
178
  props[Opencdd::PropertyIds::MDC_P022] = data_type if data_type
151
179
  props[Opencdd::PropertyIds::MDC_P024] = value_format if value_format
152
180
  props[Opencdd::PropertyIds::MDC_P021] = definition_class if definition_class
@@ -154,17 +182,31 @@ module Opencdd
154
182
  props[Opencdd::PropertyIds::MDC_P028] = condition if condition
155
183
  props[Opencdd::PropertyIds::MDC_P020] = property_data_element_type if property_data_element_type
156
184
 
157
- # Determine entity class from type
158
- entity_class = Opencdd::MetaClasses.entity_class_for_type(type&.to_sym) || Opencdd::Klass
159
- meta_code = Opencdd::MetaClasses.meta_class_for_type(type&.to_sym) || "MDC_C002"
185
+ props[Opencdd::PropertyIds::MDC_P023] = unit_structure if unit_structure
186
+ props[Opencdd::PropertyIds::MDC_P023_1] = unit_text if unit_text
187
+
188
+ 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
+ props[Opencdd::PropertyIds::MDC_P043] = rejoin_set(term_irdis) if term_irdis&.any?
191
+
192
+ props[Opencdd::PropertyIds::MDC_P044] = enumeration_code if enumeration_code
160
193
 
161
- parsed_irdi = irdi ? Opencdd::IRDI.parse(irdi) : nil
162
- parsed_meta = Opencdd::IRDI.parse(meta_code)
194
+ props[Opencdd::PropertyIds::MDC_P200] = relation_type if relation_type
195
+ props[Opencdd::PropertyIds::MDC_P203] = codomain if codomain
196
+ props[Opencdd::PropertyIds::MDC_P204] = formula if formula
197
+
198
+ props[Opencdd::PropertyIds::EXT_P002] = rejoin_set(controlled_classes) if controlled_classes&.any?
199
+ props[Opencdd::PropertyIds::EXT_P003] = rejoin_set(shown_properties) if shown_properties&.any?
200
+
201
+ extra&.each { |k, v| props[k.to_s] = v }
202
+
203
+ entity_class = Opencdd::MetaClasses.entity_class_for_type(type&.to_sym) || Opencdd::Klass
204
+ meta_code = Opencdd::MetaClasses.meta_class_for_type(type&.to_sym) || Opencdd::MetaClasses::MDC_C002
163
205
 
164
206
  entity_class.new(
165
- irdi: parsed_irdi,
207
+ irdi: irdi ? Opencdd::IRDI.parse(irdi) : nil,
166
208
  properties: props,
167
- meta_class_irdi: parsed_meta,
209
+ meta_class_irdi: Opencdd::IRDI.parse(meta_code),
168
210
  )
169
211
  end
170
212
 
@@ -181,6 +223,31 @@ module Opencdd
181
223
  end
182
224
  private_class_method :extract_ml
183
225
 
226
+ KNOWN_WIRE_IDS = %w[
227
+ MDC_P002_1 MDC_P002_2 MDC_P066
228
+ MDC_P004 MDC_P005 MDC_P006 MDC_P008 MDC_P009 MDC_P112
229
+ MDC_P011 MDC_P010 MDC_P010_1
230
+ MDC_P013 MDC_P014 MDC_P090 MDC_P016
231
+ MDC_P022 MDC_P024 MDC_P021 MDC_P041 MDC_P028 MDC_P020
232
+ MDC_P023 MDC_P023_1
233
+ MDC_P046 MDC_P044 MDC_P043
234
+ MDC_P200 MDC_P203 MDC_P204
235
+ EXT_P002 EXT_P003
236
+ ].freeze
237
+
238
+ def self.extract_extra(entity)
239
+ extra = {}
240
+ entity.properties.each do |k, v|
241
+ key = k.to_s
242
+ next if key == "__row_index__"
243
+ base = key.split(".", 2).first
244
+ next if KNOWN_WIRE_IDS.include?(base)
245
+ extra[key] = v
246
+ end
247
+ extra.empty? ? nil : extra
248
+ end
249
+ private_class_method :extract_extra
250
+
184
251
  def self.merge_ml(props, pid, hash)
185
252
  return unless hash&.any?
186
253
  hash.each { |lang, val| props["#{pid}.#{lang}"] = val }
@@ -191,6 +258,10 @@ module Opencdd
191
258
  def merge_ml_into(props, pid, hash)
192
259
  self.class.merge_ml(props, pid, hash)
193
260
  end
261
+
262
+ def rejoin_set(list)
263
+ Opencdd::StructuredValues.rejoin(list)
264
+ end
194
265
  end
195
266
  end
196
267
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "lutaml/model"
4
+
3
5
  module Opencdd
4
- class Entity
6
+ class Entity < Lutaml::Model::Serializable
5
7
  include Opencdd::ParseHelpers
6
8
 
7
9
  # Open/closed field declaration. Each `field :foo, "MDC_P###",
@@ -12,6 +14,7 @@ module Opencdd
12
14
  autoload :FieldRegistry, "opencdd/entity/field_registry"
13
15
  autoload :FieldReader, "opencdd/entity/field_reader"
14
16
  autoload :VersionHistory, "opencdd/entity/version_history"
17
+ autoload :Yaml, "opencdd/entity/yaml"
15
18
 
16
19
  class << self
17
20
  # Declare a typed field on this entity class. Defaults for
@@ -80,9 +83,10 @@ module Opencdd
80
83
  Opencdd::MetaClasses.code_property_id_for(meta_class_irdi&.code)
81
84
  end
82
85
 
83
- def initialize(irdi:, properties:, schema: nil, meta_class_irdi: nil)
86
+ def initialize(irdi: nil, properties: nil, schema: nil, meta_class_irdi: nil)
87
+ super({})
84
88
  @irdi = irdi
85
- @properties = properties
89
+ @properties = properties || {}
86
90
  @schema = schema
87
91
  @meta_class_irdi = meta_class_irdi
88
92
  @version_history = Opencdd::Entity::VersionHistory.new
@@ -98,10 +102,6 @@ module Opencdd
98
102
 
99
103
  alias_method :short, :code
100
104
 
101
- # ── Pure field reads (value_kind and multilingual auto-resolved
102
- # from PropertyIds::REGISTRY). `as:` aliases preserve the
103
- # existing JSON wire shape — ruby method names differ from
104
- # historical JSON keys for backward compatibility. ─────────
105
105
  # Note: irdi and code are NOT declared as DSL fields — they're
106
106
  # emitted explicitly by Exporters::Json#entity_payload because
107
107
  # the model's `irdi` accessor must return the Opencdd::IRDI object
@@ -251,5 +251,28 @@ module Opencdd
251
251
  @source_location = loc
252
252
  self
253
253
  end
254
+
255
+ # ── YAML persistence via Entity::Yaml ──────────────────────
256
+ # Entity extends Lutaml::Model::Serializable but its own attribute
257
+ # set is empty — the typed YAML model lives in Entity::Yaml
258
+ # (the deepened adapter). This avoids name conflicts between the
259
+ # field DSL getters (which read from @properties and return
260
+ # coerced values like IRDI objects, source-language Strings,
261
+ # parsed Arrays) and lutaml-model serialization attrs (which
262
+ # must return flat types: String, Hash, Array).
263
+ #
264
+ # lutaml-model serialization calls +public_send(attr_name)+
265
+ # during to_format, so any getter with the same name as a
266
+ # YAML attr is invoked. Keeping the YAML model in Entity::Yaml
267
+ # lets both worlds coexist: field DSL for domain access,
268
+ # Entity::Yaml for serialization.
269
+
270
+ def to_yaml(*args)
271
+ Opencdd::Entity::Yaml.from_entity(self).to_yaml(*args)
272
+ end
273
+
274
+ def self.from_yaml(yaml_str)
275
+ Opencdd::Entity::Yaml.from_yaml(yaml_str).to_entity
276
+ end
254
277
  end
255
278
  end
@@ -1,25 +1,31 @@
1
- # frozen_string: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/store"
4
4
  require "fileutils"
5
5
 
6
6
  module Opencdd
7
7
  module Model
8
- # Per-entity YAML persistence using lutaml-store as the backend.
9
- # Each entity is stored as a single YAML file in a directory layout,
10
- # serialized via Lutaml::Model (YamlEntity). Diff-friendly at the
11
- # entity level one git diff shows exactly which entity changed.
8
+ # Per-entity YAML persistence using lutaml-store's DatabaseStore
9
+ # as the backend. Each entity is stored as a single YAML file,
10
+ # serialized via Lutaml::Model (Entity::Yaml the deepened
11
+ # adapter inside Entity's namespace). Diff-friendly at the entity
12
+ # level — one git diff shows exactly which entity changed.
12
13
  #
13
- # Directory layout (managed by lutaml-store's FileSystem adapter):
14
+ # Uses DatabaseStore#save_all and #load_all with format: :yaml
15
+ # and layout: :separate (one file per entity). This is the
16
+ # proper lutaml-store API — not the low-level adapter.set/get
17
+ # bypass that the previous version used.
18
+ #
19
+ # Directory layout (managed by DatabaseStore's separate layout):
14
20
  #
15
21
  # data/
16
22
  # └── entities/
17
- # ├── AA/
18
- # ├── AAA001.data
19
- # │ └── AAA010.data
23
+ # ├── 0112_2___61360_4_AAA001.yaml
24
+ # ├── 0112_2___61360_4_AAA010.yaml
20
25
  # └── ...
21
26
  #
22
- # The FileSystem adapter shards by first 2 chars for scalability.
27
+ # The filename is the model's key field (irdi), sanitized by
28
+ # the FileSystem adapter (non-alphanumeric chars → underscore).
23
29
  class EntityStore
24
30
  attr_reader :path, :store
25
31
 
@@ -28,23 +34,21 @@ module Opencdd
28
34
  FileUtils.mkdir_p(@path)
29
35
  @store = Lutaml::Store.new(
30
36
  adapter: :filesystem,
31
- adapter_options: { path: @path },
37
+ adapter_options: { path: @path, extension: ".yaml" },
32
38
  models: [
33
- { model: Opencdd::Model::YamlEntity, key: :irdi, dir: "entities" },
39
+ { model: Opencdd::Entity::Yaml, key: :irdi, dir: "entities" },
34
40
  ],
35
41
  )
36
42
  end
37
43
 
38
44
  # Save all entities from +database+ to individual YAML files
39
- # via lutaml-store's FileSystem backend. Returns self.
45
+ # via DatabaseStore#save_all. Returns self.
40
46
  def save_database(database)
41
- database.entities.each do |entity|
42
- yaml_entity = Opencdd::Model::YamlEntity.from_entity(entity)
43
- key = safe_key(yaml_entity.irdi || yaml_entity.code)
44
- next unless key
45
- yaml_str = yaml_entity.to_yaml
46
- @store.store.adapter.set(key, yaml_str)
47
+ yaml_entities = database.entities.filter_map do |entity|
48
+ yaml = Opencdd::Entity::Yaml.from_entity(entity)
49
+ yaml.irdi ? yaml : nil
47
50
  end
51
+ @store.save_all(yaml_entities, path: @path, format: :yaml, layout: :separate)
48
52
  self
49
53
  end
50
54
 
@@ -52,33 +56,27 @@ module Opencdd
52
56
  # Returns a finalized Database.
53
57
  def load_database(database = nil)
54
58
  database ||= Opencdd::Database.new
55
- @store.store.adapter.keys.each do |key|
56
- raw = @store.store.adapter.get(key)
57
- next unless raw
58
- begin
59
- yaml_entity = Opencdd::Model::YamlEntity.from_yaml(raw)
60
- entity = yaml_entity.to_entity(database)
61
- database.add_entity(entity)
62
- rescue StandardError => e
63
- warn "EntityStore: skipping #{key}: #{e.message}"
64
- end
59
+ yaml_entities = @store.load_all(
60
+ Opencdd::Entity::Yaml,
61
+ path: @path,
62
+ format: :yaml,
63
+ layout: :separate,
64
+ )
65
+ yaml_entities.each do |yaml_entity|
66
+ entity = yaml_entity.to_entity(database)
67
+ database.add_entity(entity)
68
+ rescue StandardError => e
69
+ warn "EntityStore: skipping entity: #{e.message}"
65
70
  end
66
71
  database.finalize!
67
72
  database
68
73
  end
69
74
 
70
- # Fetch a single entity by key.
75
+ # Fetch a single entity's YAML model by key.
71
76
  def fetch(key)
72
- raw = @store.store.adapter.get(safe_key(key))
73
- return nil unless raw
74
- Opencdd::Model::YamlEntity.from_yaml(raw)
75
- end
76
-
77
- private
78
-
79
- def safe_key(key)
80
- return nil if key.nil? || key.to_s.strip.empty?
81
- key.to_s.split("#").last || key.to_s
77
+ @store.fetch(model: Opencdd::Entity::Yaml, irdi: key)
78
+ rescue Lutaml::Store::InvalidKeyError
79
+ nil
82
80
  end
83
81
  end
84
82
  end
@@ -4,7 +4,7 @@ require "lutaml/model"
4
4
 
5
5
  module Opencdd
6
6
  module Model
7
- # CDD-native YAML database. Wraps a collection of YamlEntity
7
+ # CDD-native YAML database. Wraps a collection of Entity::Yaml
8
8
  # objects with dictionary-level metadata (source language,
9
9
  # translation languages). Serializes to YAML via lutaml-model
10
10
  # — no hand-rolled serialization on the model class itself.
@@ -26,18 +26,16 @@ module Opencdd
26
26
  class YamlDatabase < Lutaml::Model::Serializable
27
27
  attribute :source_language, :string, default: "en"
28
28
  attribute :translation_languages, :string, collection: true
29
- attribute :entities, YamlEntity, collection: true
29
+ attribute :entities, Opencdd::Entity::Yaml, collection: true
30
30
 
31
- # ── Conversion: Database → YamlDatabase ─────────────────
32
31
  def self.from_database(database)
33
32
  new(
34
33
  source_language: "en",
35
34
  translation_languages: [],
36
- entities: database.entities.map { |e| YamlEntity.from_entity(e) },
35
+ entities: database.entities.map { |e| Opencdd::Entity::Yaml.from_entity(e) },
37
36
  )
38
37
  end
39
38
 
40
- # ── Conversion: YamlDatabase → Database ─────────────────
41
39
  def to_database(database = nil)
42
40
  database ||= Opencdd::Database.new
43
41
  entities.each { |ye| database.add_entity(ye.to_entity(database)) }
data/lib/opencdd/model.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opencdd
4
- # CDD-native model layer backed by Lutaml::Model. Provides typed
5
- # attributes with semantic CDD names (preferred_name, superclass,
6
- # class_type) not wire-format keys (MDC_P004, MDC_P010). YAML
7
- # is the canonical persistence format per user decision (2026-07-13).
4
+ # Database-level YAML persistence helpers. Entity-level YAML
5
+ # serialization lives on +Opencdd::Entity::Yaml+ (the deepened
6
+ # adapter inside Entity's namespace). This module holds the
7
+ # whole-database YAML wrapper and the per-entity file store.
8
8
  module Model
9
- autoload :YamlEntity, "opencdd/model/yaml_entity"
10
9
  autoload :YamlDatabase, "opencdd/model/yaml_database"
11
10
  autoload :EntityStore, "opencdd/model/entity_store"
12
11
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Opencdd
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opencdd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenCDD contributors
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-07-16 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: roo
@@ -229,11 +230,17 @@ files:
229
230
  - lib/opencdd/condition.rb
230
231
  - lib/opencdd/data_type.rb
231
232
  - lib/opencdd/database.rb
233
+ - lib/opencdd/database/finalization.rb
234
+ - lib/opencdd/database/mutations.rb
235
+ - lib/opencdd/database/parcel_integration.rb
236
+ - lib/opencdd/database/persistence.rb
237
+ - lib/opencdd/database/queries.rb
232
238
  - lib/opencdd/effective_properties.rb
233
239
  - lib/opencdd/entity.rb
234
240
  - lib/opencdd/entity/field_reader.rb
235
241
  - lib/opencdd/entity/field_registry.rb
236
242
  - lib/opencdd/entity/version_history.rb
243
+ - lib/opencdd/entity/yaml.rb
237
244
  - lib/opencdd/exporters.rb
238
245
  - lib/opencdd/exporters/json.rb
239
246
  - lib/opencdd/exporters/mermaid.rb
@@ -247,7 +254,6 @@ files:
247
254
  - lib/opencdd/model.rb
248
255
  - lib/opencdd/model/entity_store.rb
249
256
  - lib/opencdd/model/yaml_database.rb
250
- - lib/opencdd/model/yaml_entity.rb
251
257
  - lib/opencdd/parcel.rb
252
258
  - lib/opencdd/parcel/csv_reader.rb
253
259
  - lib/opencdd/parcel/csv_writer.rb
@@ -305,6 +311,7 @@ metadata:
305
311
  homepage_uri: https://github.com/opencdd/opencdd-ruby
306
312
  source_code_uri: https://github.com/opencdd/opencdd-ruby
307
313
  changelog_uri: https://github.com/opencdd/opencdd-ruby/releases
314
+ post_install_message:
308
315
  rdoc_options: []
309
316
  require_paths:
310
317
  - lib
@@ -319,7 +326,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
326
  - !ruby/object:Gem::Version
320
327
  version: '0'
321
328
  requirements: []
322
- rubygems_version: 3.6.9
329
+ rubygems_version: 3.5.22
330
+ signing_key:
323
331
  specification_version: 4
324
332
  summary: Ruby model and Parcel-format importer for the IEC Common Data Dictionary
325
333
  test_files: []