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.
- checksums.yaml +4 -4
- data/lib/opencdd/database/finalization.rb +113 -0
- data/lib/opencdd/database/mutations.rb +129 -0
- data/lib/opencdd/database/parcel_integration.rb +143 -0
- data/lib/opencdd/database/persistence.rb +30 -0
- data/lib/opencdd/database/queries.rb +171 -0
- data/lib/opencdd/database.rb +25 -594
- data/lib/opencdd/{model/yaml_entity.rb → entity/yaml.rb} +121 -50
- data/lib/opencdd/entity.rb +30 -7
- data/lib/opencdd/model/entity_store.rb +38 -40
- data/lib/opencdd/model/yaml_database.rb +3 -5
- data/lib/opencdd/model.rb +4 -5
- data/lib/opencdd/version.rb +1 -1
- metadata +12 -4
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "lutaml/model"
|
|
4
4
|
|
|
5
5
|
module Opencdd
|
|
6
|
-
|
|
7
|
-
# CDD-native YAML
|
|
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
|
|
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:
|
|
35
|
+
# superclass: 0112/2///61360_4#AAA000
|
|
24
36
|
# applicable_properties:
|
|
25
|
-
# -
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
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 →
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
attrs[:
|
|
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:
|
|
127
|
-
def to_entity(
|
|
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] =
|
|
145
|
-
props[Opencdd::PropertyIds::MDC_P014] =
|
|
146
|
-
props[Opencdd::PropertyIds::MDC_P090] =
|
|
147
|
-
props[Opencdd::PropertyIds::MDC_P016] =
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
162
|
-
|
|
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:
|
|
207
|
+
irdi: irdi ? Opencdd::IRDI.parse(irdi) : nil,
|
|
166
208
|
properties: props,
|
|
167
|
-
meta_class_irdi:
|
|
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
|
data/lib/opencdd/entity.rb
CHANGED
|
@@ -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
|
|
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
|
-
#
|
|
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
|
|
9
|
-
# Each entity is stored as a single YAML file
|
|
10
|
-
# serialized via Lutaml::Model (
|
|
11
|
-
#
|
|
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
|
-
#
|
|
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
|
-
# ├──
|
|
18
|
-
#
|
|
19
|
-
# │ └── AAA010.data
|
|
23
|
+
# ├── 0112_2___61360_4_AAA001.yaml
|
|
24
|
+
# ├── 0112_2___61360_4_AAA010.yaml
|
|
20
25
|
# └── ...
|
|
21
26
|
#
|
|
22
|
-
# The
|
|
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::
|
|
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
|
|
45
|
+
# via DatabaseStore#save_all. Returns self.
|
|
40
46
|
def save_database(database)
|
|
41
|
-
database.entities.
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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,
|
|
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|
|
|
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
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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
|
data/lib/opencdd/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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: []
|