edoxen 0.1.2 → 0.3.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.
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "resolution_date"
5
+ require_relative "url"
6
+ require_relative "localization"
7
+
8
+ module Edoxen
9
+ # A per-language source-URL record. Carries the URL ref, its format
10
+ # (pdf, html, ...), and the language_code (ISO 639-3) for which the
11
+ # URL is the canonical source.
12
+ class SourceUrl < Lutaml::Model::Serializable
13
+ attribute :ref, :string
14
+ attribute :format, :string
15
+ attribute :language_code, :string
16
+
17
+ key_value do
18
+ map "ref", to: :ref
19
+ map "format", to: :format
20
+ map "language_code", to: :language_code
21
+ end
22
+ end
23
+
24
+ class Metadata < Lutaml::Model::Serializable
25
+ attribute :title, :string
26
+ attribute :identifier, :string
27
+ attribute :dates, ResolutionDate, collection: true
28
+ attribute :source, :string
29
+ attribute :venue, :string
30
+ attribute :chair, :string
31
+ attribute :urls, Url, collection: true
32
+
33
+ # OIML extensions — see TODO.complete/14 for the glossarist-style
34
+ # i18n model. `title_localized` carries the per-language title
35
+ # parallel to Resolution#localizations. `source_urls` carries the
36
+ # per-language PDF URLs. `city`/`country_code` carry the IATA /
37
+ # ISO 3166-1 alpha-2 codes for the host venue.
38
+ attribute :title_localized, Localization, collection: true
39
+ attribute :source_urls, SourceUrl, collection: true
40
+ attribute :city, :string
41
+ attribute :country_code, :string
42
+
43
+ key_value do
44
+ map "title", to: :title
45
+ map "identifier", to: :identifier
46
+ map "dates", to: :dates
47
+ map "source", to: :source
48
+ map "venue", to: :venue
49
+ map "chair", to: :chair
50
+ map "urls", to: :urls
51
+ map "title_localized", to: :title_localized
52
+ map "source_urls", to: :source_urls
53
+ map "city", to: :city
54
+ map "country_code", to: :country_code
55
+ end
56
+ end
57
+ end
data/lib/edoxen/action.rb CHANGED
@@ -1,32 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
4
- require_relative "resolution_date"
5
-
6
3
  module Edoxen
4
+ # Verb + one effective date + human-readable message. Used inside a
5
+ # Localization to express the multilingual part of an action.
7
6
  class Action < Lutaml::Model::Serializable
8
- ACTION_TYPE_ENUM = %w[
9
- accepts acknowledges adoption adopts agrees allocates appoints appreciates
10
- appreciation approves asks assigns chairs communicating confirms consults considers
11
- creates decides defines delegates delivering directs disbands drafting elects
12
- empowers encourages endorses estabilishes establishes gathering identifies
13
- instructs investigates notes notifies recognises nominates
14
- recognizes recommends registers regrets request requests resolves restates reminds replaces
15
- scopes secures sends supports thanks welcomes withdraws
16
- ].freeze
17
-
18
- attribute :type, :string, values: ACTION_TYPE_ENUM
19
- attribute :dates, ResolutionDate, collection: true
7
+ attribute :type, :string, values: Enums::ACTION_TYPE
8
+ attribute :date_effective, ResolutionDate
20
9
  attribute :message, :string
21
- attribute :subject, :string
22
- attribute :degree, :string
23
10
 
24
11
  key_value do
25
12
  map "type", to: :type
13
+ map "date_effective", to: :date_effective
26
14
  map "message", to: :message
27
- map "subject", to: :subject
28
- map "degree", to: :degree
29
- map "dates", to: :dates
30
15
  end
31
16
  end
32
17
  end
@@ -1,22 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
4
- require_relative "resolution_date"
5
-
6
3
  module Edoxen
4
+ # Approval record: type (affirmative / negative), degree (consensus level),
5
+ # date of the approval event, and a human-readable elaboration.
7
6
  class Approval < Lutaml::Model::Serializable
8
- APPROVAL_TYPE_ENUM = %w[affirmative negative].freeze
9
- APPROVAL_DEGREE_ENUM = %w[unanimous majority minority].freeze
10
-
11
- attribute :type, :string, values: APPROVAL_TYPE_ENUM
12
- attribute :degree, :string, values: APPROVAL_DEGREE_ENUM
13
- attribute :dates, ResolutionDate, collection: true
7
+ attribute :type, :string, values: Enums::APPROVAL_TYPE
8
+ attribute :degree, :string, values: Enums::APPROVAL_DEGREE
9
+ attribute :date, ResolutionDate
14
10
  attribute :message, :string
15
11
 
16
12
  key_value do
17
13
  map "type", to: :type
18
14
  map "degree", to: :degree
19
- map "dates", to: :dates
15
+ map "date", to: :date
20
16
  map "message", to: :message
21
17
  end
22
18
  end
data/lib/edoxen/cli.rb CHANGED
@@ -2,14 +2,26 @@
2
2
 
3
3
  require "thor"
4
4
  require "fileutils"
5
- require_relative "schema_validator"
6
5
 
7
6
  module Edoxen
7
+ # Thor command-line surface for the gem. Two responsibilities:
8
+ # * `validate PATTERN` — runs both JSON-Schema validation and the
9
+ # Ruby model parser against each matching YAML file.
10
+ # * `normalize PATTERN (--output DIR | --inplace)` — round-trips each
11
+ # matching YAML file through the Ruby model, preserving any
12
+ # `# yaml-language-server: $schema=...` directive on the first line.
13
+ #
14
+ # The CLI deliberately does NOT own schema-or-model decisions — those
15
+ # live in `SchemaValidator` and `Lutaml::Model` respectively. It only
16
+ # glues them together and formats output.
8
17
  class Cli < Thor
9
- desc "validate YAML_FILE_PATTERN", "Validate YAML files against Edoxen schema"
10
- def validate(pattern)
11
- files = expand_pattern(pattern)
18
+ package_name "edoxen"
19
+
20
+ desc "validate YAML_FILE_PATTERN",
21
+ "Validate one or more Edoxen YAML files against the schema and the model."
12
22
 
23
+ def validate(pattern)
24
+ files = expand_yaml_pattern(pattern)
13
25
  if files.empty?
14
26
  say "No files found matching pattern: #{pattern}", :red
15
27
  exit 1
@@ -24,17 +36,8 @@ module Edoxen
24
36
  files.each do |file|
25
37
  print " #{File.basename(file)}... "
26
38
 
27
- # Schema validation
28
39
  schema_errors = validator.validate_file(file)
29
-
30
- # Model parsing validation
31
- model_errors = []
32
- begin
33
- content = File.read(file)
34
- Edoxen::ResolutionSet.from_yaml(content)
35
- rescue StandardError => e
36
- model_errors << "Model parsing failed: #{e.message}"
37
- end
40
+ model_errors = collect_model_errors(file)
38
41
 
39
42
  if schema_errors.empty? && model_errors.empty?
40
43
  say "✅ VALID", :green
@@ -42,36 +45,21 @@ module Edoxen
42
45
  else
43
46
  say "❌ INVALID", :red
44
47
  invalid_count += 1
45
-
46
- # Show schema validation errors with clickable links
47
- unless schema_errors.empty?
48
- say " Schema Validation Errors:", :red
49
- schema_errors.each do |error|
50
- say " #{error.to_clickable_format}", :red
51
- end
52
- end
53
-
54
- # Show model parsing errors
55
- unless model_errors.empty?
56
- say " Model Parsing Errors:", :red
57
- model_errors.each do |error|
58
- say " #{file}:1:1: #{error}", :red
59
- end
60
- end
48
+ schema_errors.each { |e| say " #{e.to_clickable_format}", :red }
49
+ model_errors.each { |m| say " #{file}:1:1: #{m}", :red }
61
50
  end
62
51
  end
63
52
 
64
- say "\n📊 Validation Summary:", :blue
65
- say " Valid files: #{valid_count}", :green
66
- say " Invalid files: #{invalid_count}", invalid_count.positive? ? :red : :green
67
- say " Success rate: #{((valid_count.to_f / files.size) * 100).round(1)}%", :blue
68
-
53
+ print_summary(files.size, valid_count, invalid_count, validator_type: :binary)
69
54
  exit(invalid_count.positive? ? 1 : 0)
70
55
  end
71
56
 
72
- desc "normalize YAML_FILE_PATTERN", "Normalize YAML files using Edoxen schema"
57
+ desc "normalize YAML_FILE_PATTERN",
58
+ "Round-trip YAML file(s) through the Edoxen model (--output DIR or --inplace)."
59
+
73
60
  option :output, type: :string, desc: "Output directory for normalized files"
74
61
  option :inplace, type: :boolean, desc: "Modify files in place (no backup)"
62
+
75
63
  def normalize(pattern)
76
64
  if options[:output] && options[:inplace]
77
65
  say "Error: Cannot use both --output and --inplace options", :red
@@ -83,8 +71,7 @@ module Edoxen
83
71
  exit 1
84
72
  end
85
73
 
86
- files = expand_pattern(pattern)
87
-
74
+ files = expand_yaml_pattern(pattern)
88
75
  if files.empty?
89
76
  say "No files found matching pattern: #{pattern}", :red
90
77
  exit 1
@@ -97,77 +84,73 @@ module Edoxen
97
84
 
98
85
  files.each do |file|
99
86
  print " #{File.basename(file)}... "
100
-
101
87
  begin
102
- # Load and parse the file
103
- content = File.read(file)
104
-
105
- # Extract yaml-language-server comment if present
106
- yaml_language_server_comment = extract_yaml_language_server_comment(content)
107
-
108
- resolution_set = Edoxen::ResolutionSet.from_yaml(content)
109
-
110
- # Normalize by serializing back to YAML
111
- normalized_yaml = resolution_set.to_yaml
112
-
113
- # Prepend the yaml-language-server comment if it was present
114
- normalized_yaml = "#{yaml_language_server_comment}\n#{normalized_yaml}" if yaml_language_server_comment
88
+ yaml_language_server_comment = extract_yaml_language_server_comment(File.read(file))
89
+ normalized = ResolutionCollection.from_yaml(File.read(file)).to_yaml
90
+ normalized = "#{yaml_language_server_comment}\n#{normalized}" if yaml_language_server_comment
115
91
 
116
92
  if options[:inplace]
117
- # Write directly to the original file
118
- File.write(file, normalized_yaml)
93
+ File.write(file, normalized)
119
94
  say "✅ NORMALIZED", :green
120
95
  else
121
- # Write to output directory
122
- output_file = File.join(options[:output], File.basename(file))
123
- FileUtils.mkdir_p(File.dirname(output_file))
124
- File.write(output_file, normalized_yaml)
125
- say "✅ NORMALIZED → #{output_file}", :green
96
+ out = File.join(options[:output], File.basename(file))
97
+ FileUtils.mkdir_p(File.dirname(out))
98
+ File.write(out, normalized)
99
+ say "✅ NORMALIZED → #{out}", :green
126
100
  end
127
-
128
101
  success_count += 1
129
102
  rescue StandardError => e
130
- say "❌ FAILED", :red
131
- say " Error: #{e.message}", :red
103
+ say "❌ FAILED — #{e.message}", :red
132
104
  error_count += 1
133
105
  end
134
106
  end
135
107
 
136
- say "\n📊 Normalization Summary:", :blue
137
- say " Successful: #{success_count}", :green
138
- say " Failed: #{error_count}", error_count.positive? ? :red : :green
139
- say " Success rate: #{((success_count.to_f / files.size) * 100).round(1)}%", :blue
140
-
141
- if options[:output]
142
- say " Output directory: #{options[:output]}", :blue
143
- elsif options[:inplace]
144
- say " Files modified in place", :yellow
145
- end
146
-
108
+ print_summary(files.size, success_count, error_count, validator_type: :lax,
109
+ extra: [
110
+ [" Output directory", options[:output]],
111
+ [" Mode", options[:inplace] ? "in place" : "--output"]
112
+ ].compact)
147
113
  exit(error_count.positive? ? 1 : 0)
148
114
  end
149
115
 
116
+ no_commands do
117
+ # Reserved for private Thor plumbing if we add it later.
118
+ end
119
+
150
120
  private
151
121
 
152
- def expand_pattern(pattern)
153
- # Handle glob patterns
154
- files = Dir.glob(pattern).select { |f| File.file?(f) }
122
+ def expand_yaml_pattern(pattern)
123
+ Dir.glob(pattern).select { |f| File.file?(f) && f.match?(/\.ya?ml\z/i) }.sort
124
+ end
155
125
 
156
- # Filter for YAML files
157
- files.select { |f| f.match?(/\.(ya?ml)$/i) }.sort
126
+ # Round-trip the file through the model to catch structural issues
127
+ # (missing nested classes, type mismatches) that the JSON-Schema can't
128
+ # express. The model is permissive about field names — schema is the
129
+ # strict source.
130
+ def collect_model_errors(file)
131
+ ResolutionCollection.from_yaml(File.read(file))
132
+ []
133
+ rescue StandardError => e
134
+ ["Model parsing failed: #{e.message}"]
158
135
  end
159
136
 
160
137
  def extract_yaml_language_server_comment(content)
161
- lines = content.split("\n")
162
-
163
- # Look for yaml-language-server comment in the first few lines
164
- lines.first(5).each do |line|
165
- if line.strip.match?(/^#\s*yaml-language-server:\s*\$schema=/)
166
- return line.rstrip # Only strip trailing whitespace, keep the #
167
- end
168
- end
138
+ lines = content.split("\n").first(5)
139
+ lines.find { |l| l.strip.match?(/\A#\s*yaml-language-server:\s*\$schema=/) }&.rstrip
140
+ end
169
141
 
170
- nil
142
+ def print_summary(total, ok_count, bad_count, validator_type:, extra: [])
143
+ say "\n📊 Summary:", :blue
144
+ say " Total: #{total}", :blue
145
+ label_text = if validator_type == :binary
146
+ " Valid: #{ok_count}, Invalid: #{bad_count}"
147
+ else
148
+ " Success: #{ok_count}, Failed: #{bad_count}"
149
+ end
150
+ say label_text, bad_count.positive? ? :red : :green
151
+ success_rate = total.zero? ? 0 : ((ok_count.to_f / total) * 100).round(1)
152
+ say " Success rate: #{success_rate}%", :blue
153
+ extra.each { |label, value| say " #{label}: #{value}", :blue }
171
154
  end
172
155
  end
173
156
  end
@@ -1,25 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
4
- require_relative "resolution_date"
5
-
6
3
  module Edoxen
4
+ # Basis for a resolution: a verb (having, noting, considering, ...) plus
5
+ # one effective date and the elaborated reasoning.
7
6
  class Consideration < Lutaml::Model::Serializable
8
- CONSIDERATION_TYPE_ENUM = %w[
9
- acknowledging basing considering identifying noting recalling recognises according following
10
- recognising recognizing
11
- ].freeze
12
-
13
- attribute :type, :string, values: CONSIDERATION_TYPE_ENUM
14
- attribute :dates, ResolutionDate, collection: true
7
+ attribute :type, :string, values: Enums::CONSIDERATION_TYPE
8
+ attribute :date_effective, ResolutionDate
15
9
  attribute :message, :string
16
- attribute :subject, :string
17
10
 
18
11
  key_value do
19
12
  map "type", to: :type
20
- map "dates", to: :dates
13
+ map "date_effective", to: :date_effective
21
14
  map "message", to: :message
22
- map "subject", to: :subject
23
15
  end
24
16
  end
25
17
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # Single source of truth for every enum used by the Edoxen information model.
5
+ #
6
+ # Mirrors ../edoxen-model/models/*.lutaml, deduped.
7
+ # Both:
8
+ # * Ruby model attribute declarations (`attribute :type, :string, values: Enums::ACTION_TYPE`)
9
+ # * JSON-Schema (`schema/edoxen.yaml`)
10
+ # reference these constants.
11
+ #
12
+ # The schema <-> Ruby enum-sync spec asserts the YAML schema's enum arrays
13
+ # equal these arrays. If you change a constant here, change the schema in
14
+ # the same PR.
15
+ module Enums
16
+ ACTION_TYPE = %w[
17
+ adopts thanks approves decides declares asks invites
18
+ resolves confirms welcomes recommends requests congratulates
19
+ instructs urges appoints calls-upon encourages affirms elects
20
+ authorizes charges states remarks judges sanctions abrogates empowers
21
+ ].freeze
22
+
23
+ CONSIDERATION_TYPE = %w[
24
+ having noting recognizing acknowledging recalling reaffirming
25
+ considering taking-into-account pursuant-to bearing-in-mind
26
+ emphasizing concerned accepts observing referring acting empowers
27
+ ].freeze
28
+
29
+ RESOLUTION_TYPE = %w[resolution recommendation decision declaration].freeze
30
+
31
+ RESOLUTION_RELATION_TYPE = %w[
32
+ annexOf hasAnnex updates refines replaces considers
33
+ ].freeze
34
+
35
+ RESOLUTION_DATE_TYPE = %w[adoption drafted discussed].freeze
36
+
37
+ APPROVAL_TYPE = %w[affirmative negative].freeze
38
+
39
+ APPROVAL_DEGREE = %w[unanimous majority minority].freeze
40
+
41
+ URL_KIND = %w[access report].freeze
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # Base class for any Edoxen-level error. Reserved for raise-on-failure paths;
5
+ # schema validation errors live under SchemaValidator::ValidationError.
6
+ class Error < StandardError; end
7
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # A monolingual rendering of a Resolution. Mirrors the glossarist
5
+ # LocalizedConcept pattern: language-agnostic fields live on the
6
+ # parent Resolution; per-language content lives here.
7
+ class Localization < Lutaml::Model::Serializable
8
+ attribute :language_code, :string
9
+ attribute :script, :string
10
+ attribute :title, :string
11
+ attribute :subject, :string
12
+ attribute :message, :string
13
+ attribute :considering, :string
14
+ attribute :considerations, Consideration, collection: true
15
+ attribute :approvals, Approval, collection: true
16
+ attribute :actions, Action, collection: true
17
+
18
+ key_value do
19
+ map "language_code", to: :language_code
20
+ map "script", to: :script
21
+ map "title", to: :title
22
+ map "subject", to: :subject
23
+ map "message", to: :message
24
+ map "considering", to: :considering
25
+ map "considerations", to: :considerations
26
+ map "approvals", to: :approvals
27
+ map "actions", to: :actions
28
+ end
29
+ end
30
+ end
@@ -1,12 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # MeetingIdentifier {
4
- # venue: String
5
- # date: DateTime
6
- # }
7
- require "lutaml/model"
8
-
9
3
  module Edoxen
4
+ # Identifier of a meeting (venue + date). Singular — the meeting a
5
+ # particular Resolution belongs to.
10
6
  class MeetingIdentifier < Lutaml::Model::Serializable
11
7
  attribute :venue, :string
12
8
  attribute :date, :date
@@ -1,47 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
4
- require_relative "resolution_date"
5
- require_relative "consideration"
6
- require_relative "approval"
7
- require_relative "action"
8
- require_relative "meeting_identifier"
9
- require_relative "resolution_relation"
10
-
11
3
  module Edoxen
4
+ # A formal Resolution. Language-agnostic admin fields live here; every
5
+ # translatable field is wrapped inside `localizations[]` (one entry per
6
+ # available language; at least one is required by the schema).
12
7
  class Resolution < Lutaml::Model::Serializable
13
- RESOLUTION_TYPE_ENUM = %w[resolution recommendation decision declaration].freeze
14
-
8
+ attribute :identifier, StructuredIdentifier, collection: true
9
+ attribute :type, :string, values: Enums::RESOLUTION_TYPE
10
+ attribute :doi, :string
11
+ attribute :urn, :string
12
+ attribute :agenda_item, :string
15
13
  attribute :dates, ResolutionDate, collection: true
16
- attribute :subject, :string
17
- attribute :title, :string
18
- attribute :type, :string, values: RESOLUTION_TYPE_ENUM
19
- attribute :identifier, :string
20
- attribute :message, :string
21
- attribute :considering, :string
22
- attribute :considerations, Consideration, collection: true
23
- attribute :approvals, Approval, collection: true
24
- attribute :actions, Action, collection: true
25
- attribute :meeting_identifier, MeetingIdentifier
26
- attribute :relations, ResolutionRelation, collection: true
27
14
  attribute :categories, :string, collection: true
15
+ attribute :meeting, MeetingIdentifier
16
+ attribute :relations, ResolutionRelation, collection: true
28
17
  attribute :urls, Url, collection: true
18
+ attribute :localizations, Localization, collection: true
29
19
 
30
20
  key_value do
31
- map "dates", to: :dates
32
- map "subject", to: :subject
33
- map "title", to: :title
34
- map "type", to: :type
35
21
  map "identifier", to: :identifier
36
- map "message", to: :message
37
- map "considering", to: :considering
38
- map "considerations", to: :considerations
39
- map "approvals", to: :approvals
40
- map "actions", to: :actions
41
- map "meeting_identifier", to: :meeting_identifier
42
- map "relations", to: :relations
22
+ map "type", to: :type
23
+ map "doi", to: :doi
24
+ map "urn", to: :urn
25
+ map "agenda_item", to: :agenda_item
26
+ map "dates", to: :dates
43
27
  map "categories", to: :categories
28
+ map "meeting", to: :meeting
29
+ map "relations", to: :relations
44
30
  map "urls", to: :urls
31
+ map "localizations", to: :localizations
45
32
  end
46
33
  end
47
34
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # Top-level container for a published resolution collection: metadata
5
+ # plus the list of resolutions.
6
+ class ResolutionCollection < Lutaml::Model::Serializable
7
+ attribute :metadata, ResolutionMetadata
8
+ attribute :resolutions, Resolution, collection: true
9
+
10
+ key_value do
11
+ map "metadata", to: :metadata
12
+ map "resolutions", to: :resolutions
13
+ end
14
+ end
15
+ end
@@ -1,17 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "lutaml/model"
4
-
5
3
  module Edoxen
4
+ # Date with semantic kind (adoption, drafted, discussed).
5
+ # ResolutionDate is the only carrier of a *typed* date in the model —
6
+ # plain `Date` in lutaml-model has no semantic context.
6
7
  class ResolutionDate < Lutaml::Model::Serializable
7
- attribute :start, :date
8
- attribute :end, :date
9
- attribute :kind, :string, values: %w[ballot enactment effective decision meeting]
8
+ attribute :date, :date
9
+ attribute :type, :string, values: Enums::RESOLUTION_DATE_TYPE
10
10
 
11
11
  key_value do
12
- map "start", to: :start
13
- map "end", to: :end
14
- map "kind", to: :kind
12
+ map "date", to: :date
13
+ map "type", to: :type
15
14
  end
16
15
  end
17
16
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # Collection-level metadata: the title (string for default / single-language
5
+ # collections, or `title_localized[]` for multilingual), the meeting date,
6
+ # the source secretariat, per-language source PDFs, and the host venue.
7
+ class ResolutionMetadata < Lutaml::Model::Serializable
8
+ attribute :title, :string
9
+ attribute :title_localized, Localization, collection: true
10
+ attribute :date, :date
11
+ attribute :source, :string
12
+ attribute :source_urls, SourceUrl, collection: true
13
+ attribute :city, :string
14
+ attribute :country_code, :string
15
+
16
+ key_value do
17
+ map "title", to: :title
18
+ map "title_localized", to: :title_localized
19
+ map "date", to: :date
20
+ map "source", to: :source
21
+ map "source_urls", to: :source_urls
22
+ map "city", to: :city
23
+ map "country_code", to: :country_code
24
+ end
25
+ end
26
+ end
@@ -1,46 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # class ResolutionRelation {
4
- # source: StructuredIdentifier (Resolution)
5
- # destination: StructuredIdentifier (Resolution)
6
- # type: ResolutionRelationType
7
- # }
8
-
9
- # enum ResolutionRelationType {
10
- # annexOf {
11
- # This resolution is an annex to the target resolution.
12
- # }
13
-
14
- # hasAnnex {
15
- # The target resolution is an annex of the source resolution.
16
- # }
17
-
18
- # updates {
19
- # This resolution updates the target resolution.
20
- # }
21
-
22
- # refines {
23
- # This resolution refines the target resolution.
24
- # }
25
-
26
- # replaces/obsoletes {
27
- # This resolution replaces/obsoletes the target resolution.
28
- # }
29
-
30
- # considers {
31
- # This resolution is made in consideration of the target resolution.
32
- # }
33
- # }
34
-
35
- require "lutaml/model"
36
-
37
3
  module Edoxen
4
+ # Directed relation between two resolutions, identified by their
5
+ # StructuredIdentifier (prefix + number).
38
6
  class ResolutionRelation < Lutaml::Model::Serializable
39
- RESOLUTION_RELATIONSHIP_ENUM = %w[annexOf hasAnnex updates refines replaces obsoletes considers].freeze
40
-
41
- attribute :source, :string
42
- attribute :destination, :string
43
- attribute :type, :string, values: RESOLUTION_RELATIONSHIP_ENUM
7
+ attribute :source, StructuredIdentifier
8
+ attribute :destination, StructuredIdentifier
9
+ attribute :type, :string, values: Enums::RESOLUTION_RELATION_TYPE
44
10
 
45
11
  key_value do
46
12
  map "source", to: :source