edoxen 0.1.2 → 0.3.1
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/CLAUDE.md +234 -0
- data/README.adoc +153 -498
- data/lib/edoxen/_metadata.rb.deprecated +57 -0
- data/lib/edoxen/action.rb +4 -25
- data/lib/edoxen/approval.rb +5 -16
- data/lib/edoxen/cli.rb +137 -129
- data/lib/edoxen/consideration.rb +4 -18
- data/lib/edoxen/enums.rb +43 -0
- data/lib/edoxen/error.rb +37 -0
- data/lib/edoxen/localization.rb +18 -0
- data/lib/edoxen/meeting_identifier.rb +2 -11
- data/lib/edoxen/resolution.rb +32 -36
- data/lib/edoxen/resolution_collection.rb +10 -0
- data/lib/edoxen/resolution_date.rb +5 -11
- data/lib/edoxen/resolution_metadata.rb +16 -0
- data/lib/edoxen/resolution_relation.rb +5 -45
- data/lib/edoxen/schema_validator.rb +158 -262
- data/lib/edoxen/source_url.rb +12 -0
- data/lib/edoxen/structured_identifier.rb +12 -0
- data/lib/edoxen/url.rb +2 -9
- data/lib/edoxen/version.rb +1 -1
- data/lib/edoxen.rb +37 -14
- data/schema/edoxen.yaml +300 -339
- metadata +11 -3
- data/lib/edoxen/metadata.rb +0 -27
data/README.adoc
CHANGED
|
@@ -1,547 +1,202 @@
|
|
|
1
1
|
= Edoxen
|
|
2
2
|
|
|
3
3
|
https://github.com/metanorma/edoxen[image:https://img.shields.io/github/stars/metanorma/edoxen.svg?style=social[GitHub Stars]]
|
|
4
|
-
https://github.com/metanorma/edoxen[image:https://img.shields.io/github/forks/metanorma/edoxen.svg?style=social[GitHub Forks]]
|
|
5
4
|
image:https://img.shields.io/github/license/metanorma/edoxen.svg[License]
|
|
6
5
|
image:https://img.shields.io/github/actions/workflow/status/metanorma/edoxen/test.yml?branch=main[Build Status]
|
|
7
6
|
image:https://img.shields.io/gem/v/edoxen.svg[RubyGems Version]
|
|
8
7
|
|
|
9
8
|
== Purpose
|
|
10
9
|
|
|
11
|
-
Edoxen is a
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
approvals.
|
|
10
|
+
Edoxen is a Ruby library for the canonical Edoxen information model of
|
|
11
|
+
formal resolutions and decisions. It is built on top of the
|
|
12
|
+
https://github.com/lutaml/lutaml[ lutaml-model] serialization framework.
|
|
15
13
|
|
|
16
|
-
The
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
The information model is defined in
|
|
15
|
+
https://github.com/metanorma/edoxen-model/tree/main/models[ LutaML UML
|
|
16
|
+
files] (one `.lutaml` per concept). This gem mirrors that model exactly —
|
|
17
|
+
attribute declarations, enum values, and field shapes — so that anything
|
|
18
|
+
you can express in LutaML you can also construct, serialize, and validate
|
|
19
|
+
in Ruby.
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
== Origin
|
|
27
|
-
|
|
28
|
-
"Edoxen" is how all resolutions of Ancient Athens started.
|
|
29
|
-
|
|
30
|
-
____
|
|
31
|
-
"It was the opinion of... (the people and city that...)"
|
|
32
|
-
____
|
|
33
|
-
|
|
34
|
-
The word "edoxen" originates from the Ancient Greek word edokeō (ἔδοξεν),
|
|
35
|
-
meaning "it was the opinion of" or "it seemed good to". This term was used in
|
|
36
|
-
the context of formal resolutions and decisions made by the Athenian assembly,
|
|
37
|
-
reflecting the collective will and judgment of the citizens.
|
|
38
|
-
|
|
39
|
-
== Features
|
|
40
|
-
|
|
41
|
-
* Classes for modeling resolutions, actions, considerations, and approvals
|
|
42
|
-
* Support for resolution collections with rich metadata
|
|
43
|
-
* Structured date handling with semantic meaning (meeting, decision, effective dates)
|
|
44
|
-
* YAML and JSON serialization with round-trip compatibility
|
|
45
|
-
* Structured identifiers and meeting information
|
|
46
|
-
* Resolution relationships and dependencies
|
|
47
|
-
* Integration with the `lutaml-model` serialization framework
|
|
48
|
-
* Comprehensive YAML schema for validation
|
|
49
|
-
* Command-line interface for validation and processing
|
|
50
|
-
* Real-world compatibility with real-world resolutions (e.g., ISO/TCs, CIPM)
|
|
21
|
+
The intended users are standards organizations and governance bodies that
|
|
22
|
+
need to publish structured records of formal decision-making. Real-world
|
|
23
|
+
samples (ISO/TC 154, CIPM, OIML CIML) ship in `spec/fixtures/`.
|
|
51
24
|
|
|
52
25
|
== Installation
|
|
53
26
|
|
|
54
|
-
Add this line to your application's Gemfile:
|
|
55
|
-
|
|
56
27
|
[source,ruby]
|
|
57
28
|
----
|
|
58
29
|
gem 'edoxen'
|
|
59
30
|
----
|
|
60
31
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
[source,sh]
|
|
64
|
-
----
|
|
65
|
-
$ bundle install
|
|
66
|
-
----
|
|
67
|
-
|
|
68
|
-
Or install it yourself as:
|
|
69
|
-
|
|
70
|
-
[source,sh]
|
|
71
|
-
----
|
|
72
|
-
$ gem install edoxen
|
|
73
|
-
----
|
|
74
|
-
|
|
75
|
-
== Command Line Interface
|
|
76
|
-
|
|
77
|
-
The `edoxen` command provides utilities for working with resolution data files.
|
|
78
|
-
|
|
79
|
-
=== Available Commands
|
|
80
|
-
|
|
81
|
-
Get help on available commands:
|
|
82
|
-
|
|
83
|
-
[source,sh]
|
|
84
|
-
----
|
|
85
|
-
$ edoxen help
|
|
86
|
-
Commands:
|
|
87
|
-
edoxen help [COMMAND] # Describe available commands or one specific command
|
|
88
|
-
edoxen normalize YAML_FILE_PATTERN # Normalize YAML files using Edoxen schema
|
|
89
|
-
edoxen validate YAML_FILE_PATTERN # Validate YAML files against Edoxen schema
|
|
90
|
-
----
|
|
91
|
-
|
|
92
|
-
=== Validation
|
|
93
|
-
|
|
94
|
-
Validate resolution data files against the Edoxen schema:
|
|
95
|
-
|
|
96
|
-
[source,sh]
|
|
97
|
-
----
|
|
98
|
-
# Validate a single file
|
|
99
|
-
$ edoxen validate resolutions.yaml
|
|
100
|
-
|
|
101
|
-
# Validate multiple files
|
|
102
|
-
$ edoxen validate file1.yaml file2.yaml file3.yaml
|
|
103
|
-
|
|
104
|
-
# Validate files using patterns
|
|
105
|
-
$ edoxen validate "*.yaml"
|
|
106
|
-
$ edoxen validate "resolutions/*.yml"
|
|
107
|
-
|
|
108
|
-
# Example output
|
|
109
|
-
🔍 Validating 3 file(s)...
|
|
110
|
-
resolutions.yaml... ✅ VALID
|
|
111
|
-
file1.yaml... ✅ VALID
|
|
112
|
-
file2.yaml... ❌ INVALID
|
|
113
|
-
- Line 15: Invalid action type 'invalid_type'
|
|
114
|
-
|
|
115
|
-
📊 Validation Summary:
|
|
116
|
-
Valid files: 2
|
|
117
|
-
Invalid files: 1
|
|
118
|
-
Success rate: 66.7%
|
|
119
|
-
----
|
|
120
|
-
|
|
121
|
-
Get detailed help for the validate command:
|
|
122
|
-
|
|
123
|
-
[source,sh]
|
|
124
|
-
----
|
|
125
|
-
$ edoxen help validate
|
|
126
|
-
Usage:
|
|
127
|
-
edoxen validate YAML_FILE_PATTERN
|
|
128
|
-
|
|
129
|
-
Description:
|
|
130
|
-
Validate YAML files against the Edoxen schema. Supports file patterns and multiple files.
|
|
131
|
-
|
|
132
|
-
Examples:
|
|
133
|
-
edoxen validate resolutions.yaml
|
|
134
|
-
edoxen validate *.yaml
|
|
135
|
-
edoxen validate "data/*.yml"
|
|
136
|
-
----
|
|
137
|
-
|
|
138
|
-
=== Normalization
|
|
139
|
-
|
|
140
|
-
Normalize YAML files to ensure they conform to the Edoxen schema format.
|
|
141
|
-
|
|
142
|
-
This command loads the YAML files, parses them through the Edoxen models, and
|
|
143
|
-
outputs clean, properly formatted YAML.
|
|
144
|
-
|
|
145
|
-
[source,sh]
|
|
146
|
-
----
|
|
147
|
-
# Normalize files to a specific output directory
|
|
148
|
-
$ edoxen normalize resolutions.yaml --output /path/to/output
|
|
149
|
-
|
|
150
|
-
# Normalize multiple files
|
|
151
|
-
$ edoxen normalize "*.yaml" --output normalized/
|
|
152
|
-
|
|
153
|
-
# Normalize files in place (overwrites original files)
|
|
154
|
-
$ edoxen normalize resolutions.yaml --inplace
|
|
155
|
-
|
|
156
|
-
# Example output
|
|
157
|
-
🔄 Normalizing 2 file(s)...
|
|
158
|
-
resolutions.yaml... ✅ NORMALIZED → /path/to/output/resolutions.yaml
|
|
159
|
-
meeting-notes.yaml... ✅ NORMALIZED → /path/to/output/meeting-notes.yaml
|
|
160
|
-
|
|
161
|
-
📊 Normalization Summary:
|
|
162
|
-
Successful: 2
|
|
163
|
-
Failed: 0
|
|
164
|
-
Success rate: 100.0%
|
|
165
|
-
Output directory: /path/to/output
|
|
166
|
-
----
|
|
167
|
-
|
|
168
|
-
Get detailed help for the normalize command:
|
|
169
|
-
|
|
170
|
-
[source,sh]
|
|
171
|
-
----
|
|
172
|
-
$ edoxen help normalize
|
|
173
|
-
Usage:
|
|
174
|
-
edoxen normalize YAML_FILE_PATTERN
|
|
175
|
-
|
|
176
|
-
Options:
|
|
177
|
-
[--output=OUTPUT] # Output directory for normalized files
|
|
178
|
-
[--inplace], [--no-inplace], [--skip-inplace] # Modify files in place (no backup)
|
|
179
|
-
|
|
180
|
-
Description:
|
|
181
|
-
Normalize YAML files using Edoxen schema. This ensures consistent formatting
|
|
182
|
-
and structure according to the Edoxen data models.
|
|
183
|
-
|
|
184
|
-
Examples:
|
|
185
|
-
edoxen normalize resolutions.yaml --output clean/
|
|
186
|
-
edoxen normalize "*.yaml" --inplace
|
|
187
|
-
edoxen normalize "data/*.yml" --output normalized/
|
|
188
|
-
----
|
|
189
|
-
|
|
190
|
-
=== Common use cases
|
|
191
|
-
|
|
192
|
-
==== Validating resolution collections
|
|
193
|
-
|
|
194
|
-
[source,sh]
|
|
195
|
-
----
|
|
196
|
-
# Validate all YAML files in a directory
|
|
197
|
-
$ edoxen validate "resolutions/*.yaml"
|
|
198
|
-
|
|
199
|
-
# Validate specific meeting files
|
|
200
|
-
$ edoxen validate "plenary-*.yaml" "ballots-*.yaml"
|
|
201
|
-
----
|
|
202
|
-
|
|
203
|
-
==== Cleaning up legacy data
|
|
204
|
-
|
|
205
|
-
[source,sh]
|
|
206
|
-
----
|
|
207
|
-
# Normalize legacy files to new schema format
|
|
208
|
-
$ edoxen normalize "legacy/*.yaml" --output clean/
|
|
209
|
-
|
|
210
|
-
# Update files in place after backup
|
|
211
|
-
$ cp -r data/ data-backup/
|
|
212
|
-
$ edoxen normalize "data/*.yaml" --inplace
|
|
213
|
-
----
|
|
214
|
-
|
|
215
|
-
==== Batch processing
|
|
216
|
-
|
|
217
|
-
[source,sh]
|
|
218
|
-
----
|
|
219
|
-
# Validate and normalize in sequence
|
|
220
|
-
$ edoxen validate "input/*.yaml" && edoxen normalize "input/*.yaml" --output output/
|
|
221
|
-
----
|
|
222
|
-
|
|
223
|
-
== Usage
|
|
32
|
+
Then `bundle install`, or `gem install edoxen` standalone.
|
|
224
33
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
==== From YAML
|
|
228
|
-
|
|
229
|
-
[source,yaml]
|
|
230
|
-
----
|
|
231
|
-
metadata:
|
|
232
|
-
title: "Resolutions of the 42nd plenary meeting of ISO/TC 154"
|
|
233
|
-
dates:
|
|
234
|
-
- kind: meeting
|
|
235
|
-
start: 2024-01-15
|
|
236
|
-
end: 2024-01-17
|
|
237
|
-
source: "ISO/TC 154 Secretariat"
|
|
238
|
-
urls:
|
|
239
|
-
- href: "https://example.com/meeting"
|
|
240
|
-
title: "Meeting Information"
|
|
241
|
-
|
|
242
|
-
resolutions:
|
|
243
|
-
- identifier: "2024-01"
|
|
244
|
-
title: "Adoption of new standard"
|
|
245
|
-
category: "Technical resolutions"
|
|
246
|
-
dates:
|
|
247
|
-
- kind: decision
|
|
248
|
-
start: 2024-01-16
|
|
249
|
-
subject: "ISO/TC 154"
|
|
250
|
-
|
|
251
|
-
considerations:
|
|
252
|
-
- type: "having"
|
|
253
|
-
message: "reviewed the technical specifications"
|
|
254
|
-
|
|
255
|
-
actions:
|
|
256
|
-
- type: "resolves"
|
|
257
|
-
dates:
|
|
258
|
-
- kind: effective
|
|
259
|
-
start: 2024-01-16
|
|
260
|
-
message: "to adopt ISO 12345 as a new standard"
|
|
261
|
-
|
|
262
|
-
approvals:
|
|
263
|
-
- type: "affirmative"
|
|
264
|
-
degree: "unanimous"
|
|
265
|
-
message: "UNANIMOUS"
|
|
266
|
-
----
|
|
34
|
+
== Quick start
|
|
267
35
|
|
|
268
36
|
[source,ruby]
|
|
269
37
|
----
|
|
270
38
|
require 'edoxen'
|
|
271
39
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
resolution_set = Edoxen::ResolutionSet.from_yaml(yaml_content)
|
|
275
|
-
|
|
276
|
-
# Access metadata
|
|
277
|
-
puts "Title: #{resolution_set.metadata.title}"
|
|
278
|
-
puts "Meeting dates: #{resolution_set.metadata.dates.first.start} to #{resolution_set.metadata.dates.first.end}"
|
|
279
|
-
puts "Source: #{resolution_set.metadata.source}"
|
|
40
|
+
yaml = File.read('resolutions.yaml')
|
|
41
|
+
collection = Edoxen::ResolutionCollection.from_yaml(yaml)
|
|
280
42
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
puts "
|
|
285
|
-
puts "Subject: #{resolution.subject}"
|
|
43
|
+
collection.resolutions.each do |resolution|
|
|
44
|
+
prefix = resolution.identifier.first.prefix
|
|
45
|
+
number = resolution.identifier.first.number
|
|
46
|
+
puts "#{prefix}/#{number}"
|
|
286
47
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
# Access considerations
|
|
293
|
-
resolution.considerations.each do |consideration|
|
|
294
|
-
puts " Consideration (#{consideration.type}): #{consideration.message}"
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
# Access actions
|
|
298
|
-
resolution.actions.each do |action|
|
|
299
|
-
puts " Action (#{action.type}): #{action.message}"
|
|
300
|
-
action.dates.each do |date|
|
|
301
|
-
puts " #{date.kind.capitalize} date: #{date.start}"
|
|
48
|
+
resolution.localizations.each do |loc|
|
|
49
|
+
puts " [#{loc.language_code}/#{loc.script}] #{loc.title}"
|
|
50
|
+
loc.actions.each do |action|
|
|
51
|
+
puts " - #{action.type}: #{action.message}"
|
|
302
52
|
end
|
|
303
53
|
end
|
|
304
|
-
|
|
305
|
-
# Access approvals
|
|
306
|
-
resolution.approvals.each do |approval|
|
|
307
|
-
puts " Approval: #{approval.type} (#{approval.degree})"
|
|
308
|
-
end
|
|
309
54
|
end
|
|
310
|
-
----
|
|
311
|
-
|
|
312
|
-
==== From JSON
|
|
313
|
-
|
|
314
|
-
[source,ruby]
|
|
315
|
-
----
|
|
316
|
-
require 'edoxen'
|
|
317
55
|
|
|
318
|
-
#
|
|
319
|
-
|
|
320
|
-
resolution_set = Edoxen::ResolutionSet.from_json(json_content)
|
|
321
|
-
|
|
322
|
-
# Access data (same as with YAML)
|
|
56
|
+
# Round-trip back to YAML
|
|
57
|
+
puts collection.to_yaml
|
|
323
58
|
----
|
|
324
59
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
==== Basic resolution
|
|
60
|
+
== Data model
|
|
328
61
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
62
|
+
The full model is in `lib/edoxen/*.rb` (Ruby) and `schema/edoxen.yaml`
|
|
63
|
+
(JSON-Schema). Both are kept in lockstep by
|
|
64
|
+
`spec/edoxen/schema_enum_sync_spec.rb`.
|
|
332
65
|
|
|
333
|
-
|
|
334
|
-
meeting_date = Edoxen::ResolutionDate.new(
|
|
335
|
-
kind: "meeting",
|
|
336
|
-
start: Date.new(2024, 1, 15),
|
|
337
|
-
end: Date.new(2024, 1, 17)
|
|
338
|
-
)
|
|
339
|
-
|
|
340
|
-
decision_date = Edoxen::ResolutionDate.new(
|
|
341
|
-
kind: "decision",
|
|
342
|
-
start: Date.new(2024, 1, 16)
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
effective_date = Edoxen::ResolutionDate.new(
|
|
346
|
-
kind: "effective",
|
|
347
|
-
start: Date.new(2024, 1, 16)
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
# Create metadata
|
|
351
|
-
metadata = Edoxen::Metadata.new(
|
|
352
|
-
title: "Resolutions of the 42nd plenary meeting of ISO/TC 154",
|
|
353
|
-
dates: [meeting_date],
|
|
354
|
-
source: "ISO/TC 154 Secretariat",
|
|
355
|
-
urls: [
|
|
356
|
-
Edoxen::Url.new(
|
|
357
|
-
href: "https://example.com/meeting",
|
|
358
|
-
title: "Meeting Information"
|
|
359
|
-
)
|
|
360
|
-
]
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
# Create a resolution
|
|
364
|
-
resolution = Edoxen::Resolution.new(
|
|
365
|
-
identifier: "2024-01",
|
|
366
|
-
title: "Adoption of new standard",
|
|
367
|
-
category: "Technical resolutions",
|
|
368
|
-
subject: "ISO/TC 154",
|
|
369
|
-
dates: [decision_date]
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
# Add considerations
|
|
373
|
-
consideration = Edoxen::Consideration.new(
|
|
374
|
-
type: "having",
|
|
375
|
-
message: "reviewed the technical specifications"
|
|
376
|
-
)
|
|
377
|
-
resolution.considerations = [consideration]
|
|
378
|
-
|
|
379
|
-
# Add actions
|
|
380
|
-
action = Edoxen::Action.new(
|
|
381
|
-
type: "resolves",
|
|
382
|
-
dates: [effective_date],
|
|
383
|
-
message: "to adopt ISO 12345 as a new standard"
|
|
384
|
-
)
|
|
385
|
-
resolution.actions = [action]
|
|
386
|
-
|
|
387
|
-
# Add approvals
|
|
388
|
-
approval = Edoxen::Approval.new(
|
|
389
|
-
type: "affirmative",
|
|
390
|
-
degree: "unanimous",
|
|
391
|
-
message: "UNANIMOUS"
|
|
392
|
-
)
|
|
393
|
-
resolution.approvals = [approval]
|
|
394
|
-
|
|
395
|
-
# Create resolution set
|
|
396
|
-
resolution_set = Edoxen::ResolutionSet.new(
|
|
397
|
-
metadata: metadata,
|
|
398
|
-
resolutions: [resolution]
|
|
399
|
-
)
|
|
66
|
+
[source]
|
|
400
67
|
----
|
|
68
|
+
ResolutionCollection
|
|
69
|
+
├── metadata: ResolutionMetadata
|
|
70
|
+
│ ├── title / title_localized[]
|
|
71
|
+
│ ├── date
|
|
72
|
+
│ ├── source
|
|
73
|
+
│ ├── source_urls: SourceUrl[]
|
|
74
|
+
│ ├── city, country_code
|
|
75
|
+
└── resolutions: Resolution[]
|
|
76
|
+
├── identifier: StructuredIdentifier[1..*]
|
|
77
|
+
├── type: ResolutionType (resolution | recommendation | decision | declaration)
|
|
78
|
+
├── doi, urn, agenda_item
|
|
79
|
+
├── dates: ResolutionDate[]
|
|
80
|
+
├── categories: String[]
|
|
81
|
+
├── meeting: MeetingIdentifier
|
|
82
|
+
├── relations: ResolutionRelation[]
|
|
83
|
+
├── urls: Url[]
|
|
84
|
+
└── localizations: Localization[1..*]
|
|
85
|
+
├── language_code (ISO 639-3)
|
|
86
|
+
├── script (ISO 15924)
|
|
87
|
+
├── title, subject, message, considering
|
|
88
|
+
├── considerations: Consideration[]
|
|
89
|
+
├── approvals: Approval[]
|
|
90
|
+
└── actions: Action[]
|
|
91
|
+
----
|
|
92
|
+
|
|
93
|
+
Every translatable field on a Resolution is wrapped inside one of its
|
|
94
|
+
`localizations[]` entries. Language-agnostic admin fields
|
|
95
|
+
(`identifier`, `doi`, `urn`, `agenda_item`, `dates`, `categories`,
|
|
96
|
+
`meeting`, `relations`, `urls`) live on the parent `Resolution`.
|
|
97
|
+
|
|
98
|
+
== Multilingual resolution sets
|
|
401
99
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
[source,ruby]
|
|
405
|
-
----
|
|
406
|
-
# Actions can have multiple types
|
|
407
|
-
action = Edoxen::Action.new(
|
|
408
|
-
type: ["resolves", "requests"],
|
|
409
|
-
dates: [effective_date],
|
|
410
|
-
message: "to adopt the standard and request implementation guidance"
|
|
411
|
-
)
|
|
100
|
+
[source,yaml]
|
|
412
101
|
----
|
|
102
|
+
resolutions:
|
|
103
|
+
- identifier:
|
|
104
|
+
- prefix: CIML
|
|
105
|
+
number: "2025-44"
|
|
106
|
+
doi: 10.63493/resolutions/ciml202544
|
|
107
|
+
agenda_item: "16.2"
|
|
108
|
+
dates:
|
|
109
|
+
- date: 2025-10-13
|
|
110
|
+
type: discussed
|
|
111
|
+
localizations:
|
|
112
|
+
- language_code: eng
|
|
113
|
+
script: Latn
|
|
114
|
+
title: Decision on the renewal of the contract of Mr Anthony Donnellan
|
|
115
|
+
subject: CIML
|
|
116
|
+
actions:
|
|
117
|
+
- type: decides
|
|
118
|
+
date_effective:
|
|
119
|
+
date: 2025-10-13
|
|
120
|
+
type: adoption
|
|
121
|
+
message: |
|
|
122
|
+
The Committee decides to renew the contract of
|
|
123
|
+
Mr Anthony Donnellan as BIML Director.
|
|
124
|
+
- language_code: fra
|
|
125
|
+
script: Latn
|
|
126
|
+
title: Décision sur le renouvellement du contrat de M. Anthony Donnellan
|
|
127
|
+
subject: CIML
|
|
128
|
+
actions:
|
|
129
|
+
- type: decides
|
|
130
|
+
date_effective:
|
|
131
|
+
date: 2025-10-13
|
|
132
|
+
type: adoption
|
|
133
|
+
message: |
|
|
134
|
+
Le Comité décide de renouveler le contrat de
|
|
135
|
+
M. Anthony Donnellan en tant que Directeur du BIML.
|
|
136
|
+
----
|
|
137
|
+
|
|
138
|
+
== Command-line interface
|
|
139
|
+
|
|
140
|
+
The `edoxen` executable exposes two commands.
|
|
141
|
+
|
|
142
|
+
=== `validate` — JSON-Schema validation + model parsing
|
|
413
143
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
==== To YAML
|
|
417
|
-
|
|
418
|
-
[source,ruby]
|
|
144
|
+
[source,sh]
|
|
419
145
|
----
|
|
420
|
-
|
|
421
|
-
yaml_content = resolution_set.to_yaml
|
|
422
|
-
File.write('resolutions.yaml', yaml_content)
|
|
146
|
+
$ edoxen validate "spec/fixtures/*.yaml"
|
|
423
147
|
----
|
|
424
148
|
|
|
425
|
-
|
|
149
|
+
Runs both `Edoxen::SchemaValidator` and `Edoxen::ResolutionCollection.from_yaml`
|
|
150
|
+
against each matching file. The schema catches `additionalProperties`,
|
|
151
|
+
`required`, enum, and pattern violations; the model catches structural
|
|
152
|
+
problems the schema can't express.
|
|
426
153
|
|
|
427
|
-
|
|
428
|
-
----
|
|
429
|
-
# Serialize to JSON
|
|
430
|
-
json_content = resolution_set.to_json
|
|
431
|
-
File.write('resolutions.json', json_content)
|
|
432
|
-
----
|
|
154
|
+
Exit code is non-zero if any file fails.
|
|
433
155
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
The Edoxen gem provides the following models:
|
|
156
|
+
=== `normalize` — round-trip every fixture through the model
|
|
437
157
|
|
|
438
|
-
[source]
|
|
439
|
-
----
|
|
440
|
-
┌─────────────────────────┐
|
|
441
|
-
│ ResolutionSet │
|
|
442
|
-
│ │
|
|
443
|
-
│ ◊metadata │
|
|
444
|
-
│ ◊resolutions │
|
|
445
|
-
└───────────┬─────────────┘
|
|
446
|
-
│ 1..*
|
|
447
|
-
┌───────────────┴────────────────┐
|
|
448
|
-
┌───────────▼─────────────┐ ┌────────────▼───────────┐
|
|
449
|
-
│ Resolution │ │ Metadata │
|
|
450
|
-
│ │ │ │
|
|
451
|
-
│ •identifier │ │ •title │
|
|
452
|
-
│ •title │ │ ◊dates │
|
|
453
|
-
│ •category │ │ •source │
|
|
454
|
-
│ •subject │ │ ◊urls │
|
|
455
|
-
│ ◊dates │ └────────────────────────┘
|
|
456
|
-
│ ◊considerations │
|
|
457
|
-
│ ◊actions │
|
|
458
|
-
│ ◊approvals │
|
|
459
|
-
└────────────┬────────────┘
|
|
460
|
-
├────────────────────┬────────────────────┐
|
|
461
|
-
│ 1..* │ 1..* │ 1..*
|
|
462
|
-
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
|
463
|
-
│ Consideration │ │ Action │ │ Approval │
|
|
464
|
-
│ │ │ │ │ │
|
|
465
|
-
│ •type │ │ •type │ │ •type │
|
|
466
|
-
│ •message │ │ •message │ │ •degree │
|
|
467
|
-
│ •date_effective │ │ •subject │ │ •message │
|
|
468
|
-
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
158
|
+
[source,sh]
|
|
469
159
|
----
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
`
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
`
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
`
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
`subject`:: The subject body as a string (optional)
|
|
510
|
-
`dates`:: A collection of `ResolutionDate` objects
|
|
511
|
-
`considerations`:: A collection of `Consideration` objects
|
|
512
|
-
`actions`:: A collection of `Action` objects
|
|
513
|
-
`approvals`:: A collection of `Approval` objects
|
|
514
|
-
|
|
515
|
-
=== Action
|
|
516
|
-
|
|
517
|
-
Represents actions taken within a resolution.
|
|
518
|
-
|
|
519
|
-
`type`:: The action type(s). Can be a string or array of strings
|
|
520
|
-
`dates`:: A collection of `ResolutionDate` objects
|
|
521
|
-
`message`:: The action description as a string
|
|
522
|
-
`subject`:: The action subject as a string (optional)
|
|
523
|
-
|
|
524
|
-
=== Consideration
|
|
525
|
-
|
|
526
|
-
Represents considerations that lead to a resolution.
|
|
527
|
-
|
|
528
|
-
`type`:: The consideration type as a string
|
|
529
|
-
`message`:: The consideration description as a string
|
|
530
|
-
`date_effective`:: The effective date as a Date object (optional)
|
|
531
|
-
|
|
532
|
-
=== Approval
|
|
533
|
-
|
|
534
|
-
Represents approval information for a resolution.
|
|
535
|
-
|
|
536
|
-
`type`:: The approval type as a string
|
|
537
|
-
`degree`:: The degree of approval as a string (optional)
|
|
538
|
-
`message`:: The approval message as a string (optional)
|
|
539
|
-
|
|
540
|
-
== Copyright
|
|
541
|
-
|
|
542
|
-
Copyright https://www.ribose.com[Ribose].
|
|
160
|
+
$ edoxen normalize "spec/fixtures/*.yaml" --output clean/
|
|
161
|
+
$ edoxen normalize legacy.yaml --inplace
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
Loads each YAML file, parses it through the Ruby model, and writes the
|
|
165
|
+
result back. Used to migrate between model versions. Either `--output DIR`
|
|
166
|
+
or `--inplace` is required (mutually exclusive).
|
|
167
|
+
|
|
168
|
+
== Architecture
|
|
169
|
+
|
|
170
|
+
* `lib/edoxen.rb` is the single entry-point. It configures
|
|
171
|
+
`Lutaml::Model::Config` and `autoload`s every model class and service.
|
|
172
|
+
* Each model lives in its own file under `lib/edoxen/`. No `require_relative`
|
|
173
|
+
— all cross-references resolve through Ruby autoload.
|
|
174
|
+
* `lib/edoxen/enums.rb` is the single source of truth for every enum
|
|
175
|
+
value used by the gem. Both the Ruby model (`attribute :type, :string,
|
|
176
|
+
values: Enums::ACTION_TYPE`) and `schema/edoxen.yaml` refer to the
|
|
177
|
+
same constant; `spec/edoxen/schema_enum_sync_spec.rb` asserts equality
|
|
178
|
+
at runtime.
|
|
179
|
+
* `lib/edoxen/schema_validator.rb` is intentionally small: a
|
|
180
|
+
`SchemaValidator::ValidationError`, a `validate_file` method, a
|
|
181
|
+
`validate_content` method, and a `LineMap` module for line-accurate
|
|
182
|
+
error reporting. Adding new collection paths never requires touching
|
|
183
|
+
it — the lookup is longest-prefix match against an indent-heuristic
|
|
184
|
+
line map.
|
|
185
|
+
* `lib/edoxen/cli.rb` is the Thor surface that glues SchemaValidator and
|
|
186
|
+
`ResolutionCollection.from_yaml` together for the two CLI commands.
|
|
187
|
+
|
|
188
|
+
== Contributing
|
|
189
|
+
|
|
190
|
+
Follow the rules in `CLAUDE.md`:
|
|
191
|
+
|
|
192
|
+
* All public methods have specs.
|
|
193
|
+
* Specs use real model instances — never `double()`.
|
|
194
|
+
* Serialization goes through `lutaml-model` only — no hand-rolled
|
|
195
|
+
`to_h`, `from_h`, `to_yaml`, or `to_json` on a model class.
|
|
196
|
+
* Schema and Ruby enum values must agree; the schema_sync_spec catches
|
|
197
|
+
drift.
|
|
198
|
+
* All changes go through PRs. Never commit to `main`, never push tags.
|
|
543
199
|
|
|
544
200
|
== License
|
|
545
201
|
|
|
546
|
-
|
|
547
|
-
https://opensource.org/licenses/BSD-2-Clause[2-Clause BSD License].
|
|
202
|
+
BSD-2-Clause. Copyright Ribose Inc.
|