edoxen 0.3.0 → 0.5.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/CLAUDE.md +24 -10
- data/README.adoc +170 -31
- data/TODO.meeting-agenda/01-design-decisions.md +132 -0
- data/TODO.meeting-agenda/02-lutaml-canonical.md +61 -0
- data/TODO.meeting-agenda/03-ruby-models.md +73 -0
- data/TODO.meeting-agenda/04-schema.md +34 -0
- data/TODO.meeting-agenda/05-fixtures.md +43 -0
- data/TODO.meeting-agenda/06-specs.md +52 -0
- data/TODO.meeting-agenda/07-cli.md +55 -0
- data/TODO.meeting-agenda/08-docs.md +35 -0
- data/TODO.meeting-agenda/09-verify-and-release.md +41 -0
- data/TODO.meeting-agenda/10-future-enhancements.md +72 -0
- data/TODO.meeting-agenda/README.md +30 -0
- data/lib/edoxen/action.rb +0 -6
- data/lib/edoxen/agenda.rb +26 -0
- data/lib/edoxen/agenda_item.rb +17 -0
- data/lib/edoxen/approval.rb +0 -7
- data/lib/edoxen/cli.rb +199 -95
- data/lib/edoxen/consideration.rb +0 -6
- data/lib/edoxen/date_range.rb +11 -0
- data/lib/edoxen/deadline.rb +10 -0
- data/lib/edoxen/enums.rb +24 -0
- data/lib/edoxen/error.rb +32 -2
- data/lib/edoxen/host_ref.rb +14 -0
- data/lib/edoxen/localization.rb +0 -12
- data/lib/edoxen/location.rb +15 -0
- data/lib/edoxen/meeting.rb +68 -0
- data/lib/edoxen/meeting_collection.rb +26 -0
- data/lib/edoxen/meeting_collection_metadata.rb +11 -0
- data/lib/edoxen/meeting_identifier.rb +0 -5
- data/lib/edoxen/meeting_localization.rb +15 -0
- data/lib/edoxen/meeting_relation.rb +12 -0
- data/lib/edoxen/person.rb +14 -0
- data/lib/edoxen/reference.rb +13 -0
- data/lib/edoxen/resolution.rb +21 -12
- data/lib/edoxen/resolution_collection.rb +0 -5
- data/lib/edoxen/resolution_date.rb +0 -5
- data/lib/edoxen/resolution_metadata.rb +5 -9
- data/lib/edoxen/resolution_relation.rb +0 -6
- data/lib/edoxen/schedule_item.rb +16 -0
- data/lib/edoxen/schema_validator.rb +12 -28
- data/lib/edoxen/source_url.rb +6 -8
- data/lib/edoxen/structured_identifier.rb +0 -5
- data/lib/edoxen/url.rb +0 -6
- data/lib/edoxen/version.rb +1 -1
- data/lib/edoxen.rb +17 -0
- data/schema/edoxen.yaml +7 -0
- data/schema/meeting.yaml +358 -0
- metadata +27 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# Identity + role + affiliation + contact. Used for meeting officers
|
|
5
|
+
# (chair, secretary, local contact) and host-ref contacts.
|
|
6
|
+
class Person < Lutaml::Model::Serializable
|
|
7
|
+
attribute :name, :string
|
|
8
|
+
attribute :role, :string
|
|
9
|
+
attribute :affiliation, :string
|
|
10
|
+
attribute :email, :string
|
|
11
|
+
attribute :phone, :string
|
|
12
|
+
attribute :orcid, :string
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# Generic document reference (used by AgendaItem.references). `kind`
|
|
5
|
+
# is a free-form discriminator (e.g., "standard", "document",
|
|
6
|
+
# "ballot", "resolution"); the schema leaves it open deliberately so
|
|
7
|
+
# new reference kinds need no schema change.
|
|
8
|
+
class Reference < Lutaml::Model::Serializable
|
|
9
|
+
attribute :ref, :string
|
|
10
|
+
attribute :kind, :string
|
|
11
|
+
attribute :title, :string
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/edoxen/resolution.rb
CHANGED
|
@@ -4,6 +4,11 @@ module Edoxen
|
|
|
4
4
|
# A formal Resolution. Language-agnostic admin fields live here; every
|
|
5
5
|
# translatable field is wrapped inside `localizations[]` (one entry per
|
|
6
6
|
# available language; at least one is required by the schema).
|
|
7
|
+
#
|
|
8
|
+
# Wire names follow lutaml-model's default convention: each declared
|
|
9
|
+
# attribute serializes to its snake_case name on the wire. Override
|
|
10
|
+
# with an explicit `key_value do; map "wire", to: :attr; end` block
|
|
11
|
+
# only when the wire name differs.
|
|
7
12
|
class Resolution < Lutaml::Model::Serializable
|
|
8
13
|
attribute :identifier, StructuredIdentifier, collection: true
|
|
9
14
|
attribute :type, :string, values: Enums::RESOLUTION_TYPE
|
|
@@ -17,18 +22,22 @@ module Edoxen
|
|
|
17
22
|
attribute :urls, Url, collection: true
|
|
18
23
|
attribute :localizations, Localization, collection: true
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
# Lookup by ISO 639-3 language code. Returns nil when no exact match
|
|
26
|
+
# exists and `fallback:` is false (the default); returns the first
|
|
27
|
+
# localization otherwise. Keeps the language-preference policy in
|
|
28
|
+
# one place so callers stop reimplementing `find { |l| ... }`.
|
|
29
|
+
def in_language(code, fallback: false)
|
|
30
|
+
match = localizations&.find { |loc| loc.language_code == code.to_s }
|
|
31
|
+
return match if match
|
|
32
|
+
|
|
33
|
+
fallback ? localizations&.first : nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The canonical rendering — English when available, else the first
|
|
37
|
+
# declared localization. Mirrors the glossarist LocalizedConcept
|
|
38
|
+
# "preferred language" notion.
|
|
39
|
+
def primary_localization
|
|
40
|
+
in_language("eng", fallback: true)
|
|
32
41
|
end
|
|
33
42
|
end
|
|
34
43
|
end
|
|
@@ -6,10 +6,5 @@ module Edoxen
|
|
|
6
6
|
class ResolutionCollection < Lutaml::Model::Serializable
|
|
7
7
|
attribute :metadata, ResolutionMetadata
|
|
8
8
|
attribute :resolutions, Resolution, collection: true
|
|
9
|
-
|
|
10
|
-
key_value do
|
|
11
|
-
map "metadata", to: :metadata
|
|
12
|
-
map "resolutions", to: :resolutions
|
|
13
|
-
end
|
|
14
9
|
end
|
|
15
10
|
end
|
|
@@ -13,14 +13,10 @@ module Edoxen
|
|
|
13
13
|
attribute :city, :string
|
|
14
14
|
attribute :country_code, :string
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
map "source_urls", to: :source_urls
|
|
22
|
-
map "city", to: :city
|
|
23
|
-
map "country_code", to: :country_code
|
|
24
|
-
end
|
|
16
|
+
# URN back-reference to the Meeting that produced this collection.
|
|
17
|
+
# Join key for the Meeting ↔ ResolutionCollection link. Optional —
|
|
18
|
+
# collections without a parent meeting (e.g., ad-hoc resolution
|
|
19
|
+
# sets) simply omit it.
|
|
20
|
+
attribute :meeting_urn, :string
|
|
25
21
|
end
|
|
26
22
|
end
|
|
@@ -7,11 +7,5 @@ module Edoxen
|
|
|
7
7
|
attribute :source, StructuredIdentifier
|
|
8
8
|
attribute :destination, StructuredIdentifier
|
|
9
9
|
attribute :type, :string, values: Enums::RESOLUTION_RELATION_TYPE
|
|
10
|
-
|
|
11
|
-
key_value do
|
|
12
|
-
map "source", to: :source
|
|
13
|
-
map "destination", to: :destination
|
|
14
|
-
map "type", to: :type
|
|
15
|
-
end
|
|
16
10
|
end
|
|
17
11
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# One entry in a meeting timetable. Structural fields (date, time,
|
|
5
|
+
# room) are language-agnostic; descriptive fields (event,
|
|
6
|
+
# description) live here as plain strings in v1 — see
|
|
7
|
+
# TODO.meeting-agenda/10-future-enhancements.md P2.1 for per-item
|
|
8
|
+
# localization.
|
|
9
|
+
class ScheduleItem < Lutaml::Model::Serializable
|
|
10
|
+
attribute :date, :date
|
|
11
|
+
attribute :time, :string
|
|
12
|
+
attribute :event, :string
|
|
13
|
+
attribute :description, :string
|
|
14
|
+
attribute :room, :string
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -21,46 +21,28 @@ module Edoxen
|
|
|
21
21
|
# hard-coded path shapes) so adding new collection fields never requires
|
|
22
22
|
# touching this class.
|
|
23
23
|
class SchemaValidator
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@file = file
|
|
29
|
-
@line = line
|
|
30
|
-
@column = column
|
|
31
|
-
@pointer = pointer
|
|
32
|
-
@message_text = message_text
|
|
33
|
-
super(format_line(file, line, column, message_text, pointer))
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def to_clickable_format
|
|
37
|
-
format_line(@file, @line, @column, @message_text, @pointer)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def format_line(file, line, column, message_text, pointer)
|
|
43
|
-
suffix = pointer.to_s.empty? ? "" : " at `#{pointer}`"
|
|
44
|
-
"#{file}:#{line}:#{column}: #{message_text}#{suffix}"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
24
|
+
# Back-compat alias. The canonical type is Edoxen::ValidationError;
|
|
25
|
+
# this constant lets existing callers keep writing
|
|
26
|
+
# `SchemaValidator::ValidationError` after the unification.
|
|
27
|
+
ValidationError = Edoxen::ValidationError
|
|
47
28
|
|
|
48
29
|
def initialize(schema_path = default_schema_path)
|
|
49
30
|
@schema_path = schema_path
|
|
50
31
|
@schemer = load_schemer(schema_path)
|
|
51
32
|
end
|
|
52
33
|
|
|
53
|
-
# Validate a YAML file. Returns an array of ValidationError
|
|
34
|
+
# Validate a YAML file. Returns an array of Edoxen::ValidationError
|
|
35
|
+
# (empty = ok).
|
|
54
36
|
def validate_file(file_path)
|
|
55
37
|
validate_content(File.read(file_path), file_path)
|
|
56
38
|
rescue Errno::ENOENT
|
|
57
39
|
[ValidationError.new(
|
|
58
40
|
file: file_path, line: 1, column: 1,
|
|
59
|
-
|
|
41
|
+
message_text: "File not found", source: Edoxen::ValidationError::SOURCE_SCHEMA
|
|
60
42
|
)]
|
|
61
43
|
end
|
|
62
44
|
|
|
63
|
-
# Validate a YAML string. Returns an array of ValidationError.
|
|
45
|
+
# Validate a YAML string. Returns an array of Edoxen::ValidationError.
|
|
64
46
|
def validate_content(content, file_path)
|
|
65
47
|
data = normalize_dates(YAML.safe_load(content, permitted_classes: [Date, Time]))
|
|
66
48
|
line_map = LineMap.build(content)
|
|
@@ -71,13 +53,15 @@ module Edoxen
|
|
|
71
53
|
line, column = LineMap.locate(pointer, line_map)
|
|
72
54
|
ValidationError.new(
|
|
73
55
|
file: file_path, line: line, column: column,
|
|
74
|
-
|
|
56
|
+
message_text: message, pointer: pointer,
|
|
57
|
+
source: Edoxen::ValidationError::SOURCE_SCHEMA
|
|
75
58
|
)
|
|
76
59
|
end
|
|
77
60
|
rescue Psych::SyntaxError => e
|
|
78
61
|
[ValidationError.new(
|
|
79
62
|
file: file_path, line: e.line || 1, column: e.column || 1,
|
|
80
|
-
|
|
63
|
+
message_text: "YAML syntax error: #{e.problem}",
|
|
64
|
+
source: Edoxen::ValidationError::SOURCE_SYNTAX
|
|
81
65
|
)]
|
|
82
66
|
end
|
|
83
67
|
|
data/lib/edoxen/source_url.rb
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Edoxen
|
|
4
4
|
# Per-language canonical source URL (e.g. one PDF per language).
|
|
5
|
-
# Carries the URL ref, its format,
|
|
6
|
-
# is the canonical source for
|
|
5
|
+
# Carries the URL ref, its format, the ISO 639-3 language_code the URL
|
|
6
|
+
# is the canonical source for, and an optional `kind` discriminator
|
|
7
|
+
# (agenda_pdf, minutes_pdf, resolutions_pdf, ...) used by the Meeting
|
|
8
|
+
# model. Existing Resolution fixtures without `kind` still parse —
|
|
9
|
+
# the field is optional.
|
|
7
10
|
class SourceUrl < Lutaml::Model::Serializable
|
|
8
11
|
attribute :ref, :string
|
|
9
12
|
attribute :format, :string
|
|
10
13
|
attribute :language_code, :string
|
|
11
|
-
|
|
12
|
-
key_value do
|
|
13
|
-
map "ref", to: :ref
|
|
14
|
-
map "format", to: :format
|
|
15
|
-
map "language_code", to: :language_code
|
|
16
|
-
end
|
|
14
|
+
attribute :kind, :string, values: Enums::SOURCE_URL_KIND
|
|
17
15
|
end
|
|
18
16
|
end
|
data/lib/edoxen/url.rb
CHANGED
data/lib/edoxen/version.rb
CHANGED
data/lib/edoxen.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Edoxen
|
|
|
21
21
|
# (Resolution <-> Localization, ResolutionMetadata <-> Localization, etc.).
|
|
22
22
|
autoload :VERSION, "edoxen/version"
|
|
23
23
|
autoload :Error, "edoxen/error"
|
|
24
|
+
autoload :ValidationError, "edoxen/error"
|
|
24
25
|
autoload :Enums, "edoxen/enums"
|
|
25
26
|
|
|
26
27
|
# Information-model classes (one per file, one concept per class).
|
|
@@ -39,6 +40,22 @@ module Edoxen
|
|
|
39
40
|
autoload :ResolutionMetadata, "edoxen/resolution_metadata"
|
|
40
41
|
autoload :ResolutionCollection, "edoxen/resolution_collection"
|
|
41
42
|
|
|
43
|
+
# Meeting / Agenda side. Mirrors edoxen-model/models/meeting*.lutaml.
|
|
44
|
+
autoload :DateRange, "edoxen/date_range"
|
|
45
|
+
autoload :Location, "edoxen/location"
|
|
46
|
+
autoload :Person, "edoxen/person"
|
|
47
|
+
autoload :HostRef, "edoxen/host_ref"
|
|
48
|
+
autoload :ScheduleItem, "edoxen/schedule_item"
|
|
49
|
+
autoload :Deadline, "edoxen/deadline"
|
|
50
|
+
autoload :Reference, "edoxen/reference"
|
|
51
|
+
autoload :AgendaItem, "edoxen/agenda_item"
|
|
52
|
+
autoload :Agenda, "edoxen/agenda"
|
|
53
|
+
autoload :MeetingRelation, "edoxen/meeting_relation"
|
|
54
|
+
autoload :MeetingLocalization, "edoxen/meeting_localization"
|
|
55
|
+
autoload :Meeting, "edoxen/meeting"
|
|
56
|
+
autoload :MeetingCollectionMetadata, "edoxen/meeting_collection_metadata"
|
|
57
|
+
autoload :MeetingCollection, "edoxen/meeting_collection"
|
|
58
|
+
|
|
42
59
|
# Services.
|
|
43
60
|
autoload :SchemaValidator, "edoxen/schema_validator"
|
|
44
61
|
autoload :Cli, "edoxen/cli"
|
data/schema/edoxen.yaml
CHANGED
|
@@ -207,6 +207,10 @@ $defs:
|
|
|
207
207
|
language_code:
|
|
208
208
|
type: string
|
|
209
209
|
pattern: "^[a-z]{3}$"
|
|
210
|
+
kind:
|
|
211
|
+
type: string
|
|
212
|
+
description: "Optional discriminator (agenda_pdf, minutes_pdf, ...)."
|
|
213
|
+
enum: [agenda_pdf, minutes_pdf, resolutions_pdf, report_pdf, register_url, landing_page]
|
|
210
214
|
|
|
211
215
|
# ====================================================================
|
|
212
216
|
# Top-level structures.
|
|
@@ -289,6 +293,9 @@ $defs:
|
|
|
289
293
|
country_code:
|
|
290
294
|
type: string
|
|
291
295
|
description: "ISO 3166-1 alpha-2 country code (2-letter, e.g. DE, FR, JP) of the host country."
|
|
296
|
+
meeting_urn:
|
|
297
|
+
type: string
|
|
298
|
+
description: "URN back-reference to the Meeting that produced this collection."
|
|
292
299
|
|
|
293
300
|
# ====================================================================
|
|
294
301
|
# Enums. Each is referenced by its $ref name above. The schema
|