edoxen 0.7.2 → 2.1.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -1
  3. data/.rubocop_todo.yml +1 -1
  4. data/CHANGELOG.md +246 -0
  5. data/README.adoc +308 -153
  6. data/edoxen.gemspec +10 -10
  7. data/lib/edoxen/action.rb +1 -1
  8. data/lib/edoxen/agenda.rb +4 -7
  9. data/lib/edoxen/agenda_item.rb +10 -3
  10. data/lib/edoxen/approval.rb +1 -1
  11. data/lib/edoxen/attendance.rb +16 -10
  12. data/lib/edoxen/body_vocabulary_entry.rb +24 -0
  13. data/lib/edoxen/body_vocabulary_host.rb +29 -0
  14. data/lib/edoxen/cli.rb +117 -120
  15. data/lib/edoxen/component_localization.rb +11 -0
  16. data/lib/edoxen/consideration.rb +2 -2
  17. data/lib/edoxen/contact.rb +24 -0
  18. data/lib/edoxen/contact_identifier.rb +13 -0
  19. data/lib/edoxen/contact_method.rb +16 -0
  20. data/lib/edoxen/decision.rb +69 -0
  21. data/lib/edoxen/decision_collection.rb +10 -0
  22. data/lib/edoxen/decision_date.rb +9 -0
  23. data/lib/edoxen/{resolution_metadata.rb → decision_metadata.rb} +5 -6
  24. data/lib/edoxen/decision_relation.rb +11 -0
  25. data/lib/edoxen/entity_ref.rb +74 -0
  26. data/lib/edoxen/enums.rb +117 -9
  27. data/lib/edoxen/error.rb +1 -1
  28. data/lib/edoxen/extension_attribute.rb +78 -0
  29. data/lib/edoxen/host_ref.rb +1 -1
  30. data/lib/edoxen/link_checker.rb +8 -8
  31. data/lib/edoxen/localization.rb +2 -2
  32. data/lib/edoxen/localization_host.rb +23 -0
  33. data/lib/edoxen/meeting.rb +63 -39
  34. data/lib/edoxen/meeting_collection.rb +1 -2
  35. data/lib/edoxen/meeting_collection_metadata.rb +3 -1
  36. data/lib/edoxen/meeting_component.rb +41 -0
  37. data/lib/edoxen/meeting_extension.rb +25 -0
  38. data/lib/edoxen/meeting_identifier.rb +1 -1
  39. data/lib/edoxen/meeting_localization.rb +6 -1
  40. data/lib/edoxen/meeting_series.rb +21 -0
  41. data/lib/edoxen/minutes.rb +2 -2
  42. data/lib/edoxen/minutes_section.rb +8 -11
  43. data/lib/edoxen/motion.rb +51 -0
  44. data/lib/edoxen/name.rb +29 -0
  45. data/lib/edoxen/officer.rb +19 -0
  46. data/lib/edoxen/officers_host.rb +19 -0
  47. data/lib/edoxen/person.rb +5 -9
  48. data/lib/edoxen/physical_venue.rb +15 -0
  49. data/lib/edoxen/recurrence.rb +27 -0
  50. data/lib/edoxen/recurrence_by_day.rb +10 -0
  51. data/lib/edoxen/reference_data.rb +35 -41
  52. data/lib/edoxen/schema_validator.rb +2 -3
  53. data/lib/edoxen/source_url.rb +3 -3
  54. data/lib/edoxen/structured_identifier.rb +2 -2
  55. data/lib/edoxen/topic.rb +37 -0
  56. data/lib/edoxen/topic_asset.rb +14 -0
  57. data/lib/edoxen/topic_document.rb +16 -0
  58. data/lib/edoxen/venue.rb +80 -0
  59. data/lib/edoxen/venue_validator.rb +56 -0
  60. data/lib/edoxen/version.rb +1 -1
  61. data/lib/edoxen/virtual_venue.rb +15 -0
  62. data/lib/edoxen/vote_record.rb +17 -13
  63. data/lib/edoxen/voting.rb +45 -0
  64. data/lib/edoxen/voting_counts.rb +23 -0
  65. data/lib/edoxen.rb +40 -13
  66. data/schema/edoxen.yaml +792 -145
  67. data/schema/meeting.yaml +600 -142
  68. data/sig/edoxen.rbs +581 -1
  69. metadata +57 -20
  70. data/lib/edoxen/_metadata.rb.deprecated +0 -57
  71. data/lib/edoxen/location.rb +0 -15
  72. data/lib/edoxen/resolution.rb +0 -43
  73. data/lib/edoxen/resolution_collection.rb +0 -10
  74. data/lib/edoxen/resolution_date.rb +0 -11
  75. data/lib/edoxen/resolution_relation.rb +0 -11
  76. data/lib/edoxen/resolution_set.rb +0 -17
  77. data/lib/edoxen/schedule_item.rb +0 -27
  78. data/lib/edoxen/schedule_item_localization.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69a86986a86602665699b5598622d32a63658c302737587f68b7a9b095a471e1
4
- data.tar.gz: 1201e014ac06c2e4accb00dfd00660f577f6f6464f5d7990553714b796af8314
3
+ metadata.gz: 0ba69e84d00cf1307fc4887da2270e2c6c8c30ffd437858aaf6afb2e1a58a1dc
4
+ data.tar.gz: 6e4814249a7417323e6d83dde4d4631451c6a70abdea4106c1af55caf5c5d291
5
5
  SHA512:
6
- metadata.gz: 7ec73b629af4dd2997fea614665347177f273569349d0221e414f9dc9b2dbb8f083b3d964675286bb9a76d449a5a4c7c2cf2981db028ebf5f0d2e54249abb77d
7
- data.tar.gz: 52534618f6468c4da8effd9bcb23e978487e1857da1a9832fa8d1ed665e72324a13397657b0cb8f6c828be4ef45e9d669781a208b7135c09e97a1b833cc6c69e
6
+ metadata.gz: 9d5453a5e99eb21be23dc0ba448cd7b44cdd91d3be947f3eb2bbb6d0c4a7cff171b4b95c1e4f477b8b194f72bff06908e88db7d0bb0bdf0163d9db3051ca3d93
7
+ data.tar.gz: 3dbcb645d7d6fc5e8823d553dd7bbbc740464c5a954c43038c9f5ea2680a53a347fd35b082d53c5c6579b2331d4cc9129ea56dbc5b0f1805c2c3fc737487a714
data/.rubocop.yml CHANGED
@@ -1,7 +1,8 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.6
4
+ TargetRubyVersion: 3.1
5
+ NewCops: enable
5
6
 
6
7
  Style/StringLiterals:
7
8
  Enabled: true
@@ -13,3 +14,18 @@ Style/StringLiteralsInInterpolation:
13
14
 
14
15
  Layout/LineLength:
15
16
  Max: 120
17
+
18
+ # The LutaML parser is a self-contained state machine that lives in
19
+ # spec/support/ only. 101 lines for the whole module (struct types +
20
+ # state-machine + helpers) is acceptable; splitting would obscure the
21
+ # design.
22
+ Metrics/ModuleLength:
23
+ Exclude:
24
+ - "spec/support/lutaml_parser.rb"
25
+
26
+ # RSpec top-level describe blocks routinely run past the default
27
+ # threshold; the suite uses focused single-class spec files instead
28
+ # of splitting for size's own sake.
29
+ Metrics/BlockLength:
30
+ Exclude:
31
+ - "spec/**/*.rb"
data/.rubocop_todo.yml CHANGED
@@ -115,7 +115,7 @@ Metrics/BlockLength:
115
115
  # Offense count: 3
116
116
  # Configuration parameters: CountComments, CountAsOne.
117
117
  Metrics/ClassLength:
118
- Max: 167
118
+ Max: 170
119
119
 
120
120
  # Offense count: 6
121
121
  # Configuration parameters: AllowedMethods, AllowedPatterns.
data/CHANGELOG.md ADDED
@@ -0,0 +1,246 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.1.1] — 2026-07-06
9
+
10
+ Patch release. Bundles the v2.2 OCP refactor (Contact family), the
11
+ post-v2.2 audit cleanup, and the gem↔model canonical-schema sync spec.
12
+
13
+ ### Added
14
+
15
+ - **Contact family** (v2.2 OCP refactor): `Contact`, `Name`,
16
+ `ContactMethod`, `ContactIdentifier` classes plus
17
+ `CONTACT_METHOD_KIND` and `CONTACT_IDENTIFIER_KIND` enums. `Person`
18
+ now inherits from `Contact`; the hard-coded `email` / `phone` /
19
+ `orcid` fields are replaced by typed `contact_methods[]` and
20
+ `identifiers[]` collections. New channel/identifier kinds land via
21
+ the enum (or `other` + extensions) — no model change (OCP).
22
+ - **BodyVocabularyHost / LocalizationHost / OfficersHost modules.**
23
+ DRY the `body_vocabulary` + `canonical_type_for` lookup, the
24
+ `in_language` + `primary_localization` accessors, and the
25
+ `officers_with_role` + `chair` accessors across the classes that
26
+ share them.
27
+ - **Behavioral specs for Contact, ContactMethod, ContactIdentifier,
28
+ Name** — each with the typed ExtensionAttribute round-trip via
29
+ `it_behaves_like "extension host"`.
30
+ - **MECE parity spec** (`spec/edoxen/mece_parity_spec.rb`) covering
31
+ all four v2.1 bidirectional pairs (Motion↔Decision, Decision↔
32
+ Component, Decision↔Topic, Motion↔Voting). Guards the v3.0
33
+ stored-side removal.
34
+ - **Canonical-enum ≤5 architectural invariant** in
35
+ `body_vocabulary_spec.rb`.
36
+ - **Body-vocabulary metadata YAML round-trip** on both
37
+ `DecisionMetadata` and `MeetingCollectionMetadata`.
38
+ - **Cross-schema sync spec** (`spec/edoxen/schema_cross_file_sync_spec.rb`):
39
+ 23 shared `$defs` between `schema/edoxen.yaml` and `schema/meeting.yaml`
40
+ enforced byte-equal. Discovery spec catches future omissions from
41
+ `SHARED_NAMES`.
42
+ - **Gem↔model canonical schema sync spec**
43
+ (`spec/edoxen/schema_model_canonical_sync_spec.rb`, 126 examples):
44
+ every `$defs` entry in the gem's mirror schemas is enforced
45
+ byte-equal to the model's canonical `schema/decision-collection.yaml`
46
+ and `schema/meeting.yaml`. Skips when the model repo isn't checked
47
+ out.
48
+
49
+ ### Changed
50
+
51
+ - **EntityRef XOR contract enforced.** `EntityRef#valid?` now returns
52
+ true only when exactly one of `urn` / `identifier` / `local_ref` is
53
+ set (was: any number ≥1). New `#multiple_identities?` predicate
54
+ flags ambiguous data. The wire contract documented in
55
+ `edoxen-model/TODO.refactor/44-entityref-typed-cross-references.md`
56
+ is now truthful.
57
+ - **`Motion#pending?` derived from `Edoxen::Enums::MOTION_TERMINAL`.**
58
+ New constant partitions `MOTION_STATUS` cleanly; a coverage spec
59
+ asserts the union equals `MOTION_STATUS` (MECE invariant).
60
+ - **CLI refactored to a Profile-based dispatch.** `validate` /
61
+ `normalize` / `validate-meetings` / `normalize-meetings` collapse to
62
+ one-line delegations; option parsing and batch scaffolding share a
63
+ single runner.
64
+ - **VenueValidator dispatches on `kind`, not `is_a?`.** Wire-parsed
65
+ Venues (always flat `Venue`, never `PhysicalVenue`) now validate
66
+ correctly.
67
+ - **`LutamlParser.parse` refactored into five small state-handler
68
+ methods.** Cyclomatic and perceived complexity now within rubocop
69
+ limits. `LutamlEnum.values` renamed to `.items` to avoid
70
+ `Struct#values` / `#entries` override.
71
+ - **`Name#display` single-pass reject** (was: two passes).
72
+ - **`annexOf` / `hasAnnex` → `annex_of` / `has_annex`** across gem
73
+ Ruby, gem schema, model lutaml, and model schema. The camelCase
74
+ outliers were the only wire-form inconsistency in any enum.
75
+
76
+ ### Removed
77
+
78
+ - **Dead identity `key_value` blocks** on `EntityRef` and
79
+ `BodyVocabularyEntry` (lutaml-model auto-emits identity maps).
80
+ - **`respond_to?(:to_s)`** in `EntityRef#identities_set` — replaced
81
+ with a `case` over `nil` / `String` / else.
82
+ - **Duplicate `canonical_type_for`**, `in_language` /
83
+ `primary_localization`, and `officers_with_role` / `chair`
84
+ implementations — moved to the new shared modules.
85
+
86
+ ### Infrastructure
87
+
88
+ - **`.rubocop.yml` `TargetRubyVersion` 2.6 → 3.1.** `NewCops: enable`.
89
+ `Metrics/ModuleLength` excluded for the spec-side LutaML parser;
90
+ `Metrics/BlockLength` excluded for spec files generally.
91
+ - **`edoxen.gemspec` `required_ruby_version` 3.0 → 3.1.** Ruby 3.0
92
+ reached EOL March 2024. Migrated to `YAML.safe_load_file`.
93
+ - **`TODO.*/` added to `.gitignore`** — local planning notes never
94
+ committed.
95
+ - **`entry.country` → `entry.country_iso2`** in the `edoxen iata` CLI
96
+ (was calling a method that didn't exist on `Iata::Entry`).
97
+
98
+ ### Test results
99
+
100
+ 1339 examples, 0 failures. 0 rubocop offenses.
101
+
102
+ ## [2.1.0] — 2026-07-05
103
+
104
+ Edoxen v2.1 is a backwards-compatible minor release that tightens the
105
+ profile mechanism and adds the LutaML↔Ruby regression net.
106
+
107
+
108
+ ### Added
109
+
110
+ - **LutaML ↔ Ruby sync spec** (`spec/edoxen/lutaml_ruby_sync_spec.rb`):
111
+ walks every `class` block in `edoxen-model/models/*.lutaml` and
112
+ asserts the matching `Edoxen::*` Ruby class declares the same
113
+ attribute names + collection flags. Closes the root-cause gap from
114
+ the v2.0 drift audit.
115
+ - **LutaML enum ↔ Ruby sync** (in the same spec): walks every `enum`
116
+ block and asserts value-for-value equality with the matching
117
+ `Edoxen::Enums::*` constant.
118
+ - **Typed ExtensionAttribute variants**: `integer_value`, `float_value`,
119
+ `boolean_value`, `date_value`, `date_time_value` plus a `type`
120
+ discriminator. New `#typed_value` reader picks the right variant.
121
+ String values still use the bare `value:` wire name (v2.0 back-compat).
122
+
123
+ ### Changed
124
+
125
+ - **MeetingExtension field semantics documented.** `kind` is the
126
+ in-profile discriminator; `ref` is the URN of an external profile
127
+ document. Field behavior is unchanged; the docs are now explicit.
128
+ - **LutaML model files** synced to match the gem after the drift audit
129
+ closed four real drifts (Voting.method, Agenda three drifts, missing
130
+ url.lutaml, vestigial subject_body.lutaml).
131
+
132
+ ### Removed
133
+
134
+ - **Recursive `extensions[]` slot on MeetingExtension.** YAGNI — no
135
+ documented use case. Profiles needing nesting use dotted keys
136
+ (`vote.count`, `vote.method`) in `attributes[]`.
137
+
138
+ ### Compatibility
139
+
140
+ v2.0 fixtures continue to parse and round-trip unchanged. The v2.0
141
+ bare `value: String` wire shape on ExtensionAttribute still works;
142
+ the gem routes it into the string variant with `type` defaulted to
143
+ `"string"`.
144
+
145
+ ## [2.0.0] — 2026-07-04
146
+
147
+ Edoxen v2.0 broadens the model from a standards-body-specific Resolution
148
+ model to a **generic meeting, agenda, motion, voting, and decision
149
+ model** with profile extensions for domain-specific concepts.
150
+
151
+ ### Post-launch drift closures (2026-07-04)
152
+
153
+ Per the post-v2 model↔gem drift audit (`edoxen-model/TODO.refactor/20-post-v2-gem-drift.md`):
154
+
155
+ - Added `extensions: MeetingExtension[0..*]` to `DecisionMetadata` —
156
+ closes the inverse drift where the gem was missing what every other
157
+ v2 collection-level entity already had.
158
+
159
+
160
+ ### Breaking changes
161
+
162
+ - Renamed `Resolution` → `Decision`. `Resolution` is now a `DecisionKind` value.
163
+ - Renamed `ResolutionCollection` → `DecisionCollection`.
164
+ - Renamed `ResolutionMetadata` → `DecisionMetadata`.
165
+ - Renamed `ResolutionDate` → `DecisionDate`; `ResolutionDateType` → `DecisionDateType`.
166
+ - Renamed `ResolutionRelation` → `DecisionRelation`; `ResolutionRelationType` → `DecisionRelationType`.
167
+ - Renamed `ResolutionType` → `DecisionKind` (with expanded values: resolution, order, ruling, determination, recommendation, statement, finding, opinion, other).
168
+ - Renamed `Voting.method` → `Voting.voting_method` (avoids Ruby Object#method conflict).
169
+ - Removed `Meeting.virtual: Boolean`. Replaced with polymorphic `Meeting.venues: Venue[]`.
170
+ - Removed `Meeting.chair` and `Meeting.secretary` direct shortcuts. Replaced with `Meeting.officers: Officer[]` (role discriminates). `Meeting#chair` and `Meeting#secretary` remain as lookup helpers.
171
+ - Removed `Meeting.schedule: ScheduleItem[]`. Replaced with `Meeting.components: MeetingComponent[]` (flat).
172
+ - Removed `Meeting.host` (singular String). Use `Meeting.hosts: HostRef[]`.
173
+ - Removed `Meeting.year` (derivable from `date_range.start`).
174
+ - Renamed `Meeting.resolution_refs` → `Meeting.decisions` (inline Decision objects).
175
+ - Removed `AgendaItem.resolution_ref`. Use `AgendaItem.decision_ref`.
176
+ - Removed `Agenda.opening_session` / `Agenda.closing_session` (ScheduleItem references). These are now `MeetingComponent`s.
177
+ - Removed `VoteRecord.resolution_ref`. Use `VoteRecord.decision_ref` + `VoteRecord.voting_ref`.
178
+ - lutaml-model attribute type `:datetime` is now `:date_time` (lutaml-model 0.8+ naming).
179
+
180
+ ### Added
181
+
182
+ - `Decision` base type with `kind`, `status`, and procedural fields
183
+ (`brought_by_motions`, `about_topics`, `made_in_component`).
184
+ - `Motion` entity — procedural act with state machine:
185
+ `introduced → seconded → debating → question_put → voting → carried/negatived/withdrawn/lapsed`.
186
+ - `Voting` entity — vote state machine:
187
+ `called → in_progress → decided | withdrawn | deferred`; `voting_method`,
188
+ `counts`, `casting_vote`, `vote_records`.
189
+ - `VotingCounts` — ayes / noes / abstentions / absent; derived `#total`,
190
+ `#margin`, `#tied?`.
191
+ - `Topic`, `TopicDocument`, `TopicAsset` — the subject of discussion.
192
+ Cross-meeting threading via `resumption_of`.
193
+ - `MeetingSeries` — parent of recurring meetings.
194
+ - `MeetingComponent` — flat sub-events (replaces ScheduleItem). Kinds:
195
+ track, session, debate, breakout, bof, keynote, opening, closing,
196
+ break, reception, etc.
197
+ - `ComponentLocalization` — per-language content for a component.
198
+ - `Officer` + `OfficerRole` (replaces chair/secretary shortcuts).
199
+ - `MeetingExtension` + `ExtensionAttribute` — profile mechanism
200
+ (ISO 8601-2 §15). Every core entity has an `extensions[]` slot.
201
+ - `Recurrence` + `RecurrenceByDay` — structured ISO 8601-2 §13
202
+ (queryable, not opaque RRULE).
203
+ - `Venue` polymorphic (kind: physical | virtual). Single flat class on
204
+ the wire; `kind` discriminates which fields are expected.
205
+ - `PhysicalVenue`, `VirtualVenue` — Ruby subclasses for type-checking
206
+ and helper methods.
207
+ - `VenueValidator` — validates Venue instances against the `unlocodes`
208
+ and `iata` gems; optionally auto-populates `city` / `country_code`
209
+ from the UN/LOCODE registry.
210
+ - Extended `Attendance` with `role` (AttendanceRole) and `response`
211
+ (AttendanceResponse, from iCalendar PARTSTAT, plain English).
212
+ - Extended `AgendaItem` with `topics[]` and `components[]`.
213
+ - Extended `Person` with `kind` and `orcid`.
214
+ - Extended `Meeting` with `urn`, `ordinal`, `series_ref`, `visibility`,
215
+ `recurrence`, `landing_url`, `registration_url`.
216
+ - New enums: `VenueKind`, `VirtualFeature`, `Visibility`,
217
+ `AttendanceRole`, `AttendanceResponse`, `ComponentKind`,
218
+ `DecisionKind`, `DecisionStatus`, `MotionStatus`, `VotingStatus`,
219
+ `VotingMethod`, `VotingOutcome`, `TopicStatus`, `OfficerRole`,
220
+ `RecurrenceFreq`.
221
+ - CLI: `edoxen iata CODE` command (parallels `edoxen unlocode`).
222
+ - RBS signatures (`sig/edoxen.rbs`) for every public class and method.
223
+
224
+ ### Integration
225
+
226
+ - `unlocodes` gem for UN/LOCODE validation (runtime dependency).
227
+ - `iata` gem for IATA code validation (runtime dependency).
228
+
229
+ ### References
230
+
231
+ - iCalendar (RFC 5545/7986/5546/9253) — referenced, not duplicated.
232
+ - ISO 8601-2 (2026) §13 for recurrence, §15 for profile mechanism.
233
+
234
+ ### Migration
235
+
236
+ See the [migration guide](https://github.com/edoxen/edoxen.github.io/blob/main/docs/migration-v2.md)
237
+ for the full v0.x → v2.0 transformation.
238
+
239
+ ## [1.0.0] — 2026-06-30
240
+
241
+ Initial public release. Standards-body-focused Resolution model with
242
+ Meeting + Agenda + Minutes + Attendance + VoteRecord side.
243
+
244
+ ## [0.1.0] — 2025-XX-XX
245
+
246
+ Initial development release.