edoxen 0.1.1 → 0.1.2

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.
data/lib/edoxen/action.rb CHANGED
@@ -1,65 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
+ require_relative "resolution_date"
4
5
 
5
6
  module Edoxen
6
7
  class Action < Lutaml::Model::Serializable
7
8
  ACTION_TYPE_ENUM = %w[
8
- adopts thanks approves decides declares asks invites resolves confirms
9
- welcomes recommends requests congratulates instructs urges appoints
10
- resolves further instructs calls-upon encourages affirms elects
11
- authorizes charges states remarks judges sanctions abrogates empowers
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
12
16
  ].freeze
13
17
 
14
18
  attribute :type, :string, values: ACTION_TYPE_ENUM
15
- attribute :date_effective, :date
19
+ attribute :dates, ResolutionDate, collection: true
16
20
  attribute :message, :string
17
21
  attribute :subject, :string
22
+ attribute :degree, :string
18
23
 
19
24
  key_value do
20
25
  map "type", to: :type
21
- map "date_effective", to: :date_effective
22
26
  map "message", to: :message
23
27
  map "subject", to: :subject
28
+ map "degree", to: :degree
29
+ map "dates", to: :dates
24
30
  end
25
31
  end
26
32
  end
27
-
28
- # Action {
29
- # type: ActionType
30
- # dateEffective: Date
31
- # message: Text
32
- # }
33
-
34
- # enum ActionType {
35
- # adopts
36
- # thanks / expresses-appreciation (subjects)
37
- # approves
38
- # decides
39
- # declares
40
- # asks (subjects)
41
- # invites / further invites (subjects)
42
- # resolves
43
- # confirms
44
- # welcomes (subjects)
45
- # recommends
46
- # requests (subjects)
47
- # congratulates (subjects)
48
- # instructs (subjects)
49
- # urges (subjects)
50
- # appoints (subjects)
51
- # resolves further
52
- # instructs (subjects)
53
- # calls upon (subjects)
54
- # encourages (subjects)
55
- # affirms / reaffirming (subjects)
56
- # elects
57
- # authorizes
58
- # charges
59
- # states
60
- # remarks
61
- # judges
62
- # sanctions
63
- # abrogates
64
- # empowers
65
- # }
@@ -1,23 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Approval {
4
- # type: ApprovalType
5
- # degree: ApprovalDegree
6
- # date: Date
7
- # message: Text
8
- # }
9
-
10
- # enum ApprovalType {
11
- # affirmative
12
- # negative
13
- # }
14
-
15
- # enum ApprovalDegree {
16
- # unanimous
17
- # majority
18
- # minority
19
- # }
20
3
  require "lutaml/model"
4
+ require_relative "resolution_date"
21
5
 
22
6
  module Edoxen
23
7
  class Approval < Lutaml::Model::Serializable
@@ -26,13 +10,13 @@ module Edoxen
26
10
 
27
11
  attribute :type, :string, values: APPROVAL_TYPE_ENUM
28
12
  attribute :degree, :string, values: APPROVAL_DEGREE_ENUM
29
- attribute :date, :date
13
+ attribute :dates, ResolutionDate, collection: true
30
14
  attribute :message, :string
31
15
 
32
16
  key_value do
33
17
  map "type", to: :type
34
18
  map "degree", to: :degree
35
- map "date", to: :date
19
+ map "dates", to: :dates
36
20
  map "message", to: :message
37
21
  end
38
22
  end
data/lib/edoxen/cli.rb ADDED
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "fileutils"
5
+ require_relative "schema_validator"
6
+
7
+ module Edoxen
8
+ class Cli < Thor
9
+ desc "validate YAML_FILE_PATTERN", "Validate YAML files against Edoxen schema"
10
+ def validate(pattern)
11
+ files = expand_pattern(pattern)
12
+
13
+ if files.empty?
14
+ say "No files found matching pattern: #{pattern}", :red
15
+ exit 1
16
+ end
17
+
18
+ say "🔍 Validating #{files.size} file(s)...", :blue
19
+
20
+ validator = SchemaValidator.new
21
+ valid_count = 0
22
+ invalid_count = 0
23
+
24
+ files.each do |file|
25
+ print " #{File.basename(file)}... "
26
+
27
+ # Schema validation
28
+ 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
38
+
39
+ if schema_errors.empty? && model_errors.empty?
40
+ say "✅ VALID", :green
41
+ valid_count += 1
42
+ else
43
+ say "❌ INVALID", :red
44
+ 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
61
+ end
62
+ end
63
+
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
+
69
+ exit(invalid_count.positive? ? 1 : 0)
70
+ end
71
+
72
+ desc "normalize YAML_FILE_PATTERN", "Normalize YAML files using Edoxen schema"
73
+ option :output, type: :string, desc: "Output directory for normalized files"
74
+ option :inplace, type: :boolean, desc: "Modify files in place (no backup)"
75
+ def normalize(pattern)
76
+ if options[:output] && options[:inplace]
77
+ say "Error: Cannot use both --output and --inplace options", :red
78
+ exit 1
79
+ end
80
+
81
+ unless options[:output] || options[:inplace]
82
+ say "Error: Must specify either --output or --inplace option", :red
83
+ exit 1
84
+ end
85
+
86
+ files = expand_pattern(pattern)
87
+
88
+ if files.empty?
89
+ say "No files found matching pattern: #{pattern}", :red
90
+ exit 1
91
+ end
92
+
93
+ say "🔄 Normalizing #{files.size} file(s)...", :blue
94
+
95
+ success_count = 0
96
+ error_count = 0
97
+
98
+ files.each do |file|
99
+ print " #{File.basename(file)}... "
100
+
101
+ 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
115
+
116
+ if options[:inplace]
117
+ # Write directly to the original file
118
+ File.write(file, normalized_yaml)
119
+ say "✅ NORMALIZED", :green
120
+ 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
126
+ end
127
+
128
+ success_count += 1
129
+ rescue StandardError => e
130
+ say "❌ FAILED", :red
131
+ say " Error: #{e.message}", :red
132
+ error_count += 1
133
+ end
134
+ end
135
+
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
+
147
+ exit(error_count.positive? ? 1 : 0)
148
+ end
149
+
150
+ private
151
+
152
+ def expand_pattern(pattern)
153
+ # Handle glob patterns
154
+ files = Dir.glob(pattern).select { |f| File.file?(f) }
155
+
156
+ # Filter for YAML files
157
+ files.select { |f| f.match?(/\.(ya?ml)$/i) }.sort
158
+ end
159
+
160
+ 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
169
+
170
+ nil
171
+ end
172
+ end
173
+ end
@@ -1,48 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Consideration {
4
- # type: ConsiderationType
5
- # dateEffective: Date
6
- # message: Text
7
- # }
8
-
9
- # enum ConsiderationType {
10
- # having / having regard
11
- # noting
12
- # recognizing
13
- # acknowledging
14
- # recalling / further recalling
15
- # reaffirming
16
- # considering
17
- # taking into account
18
- # pursuant to
19
- # bearing in mind
20
- # emphasizing
21
- # concerned
22
- # accepts
23
- # observing
24
- # referring
25
- # acting
26
- # empowers
27
- # reaffirming
28
- # }
29
-
30
3
  require "lutaml/model"
4
+ require_relative "resolution_date"
31
5
 
32
6
  module Edoxen
33
7
  class Consideration < Lutaml::Model::Serializable
34
- CONSIDERATION_TYPE_ENUM = %w[having noting recognizing acknowledging recalling reaffirming considering
35
- taking-into-account pursuant-to bearing-in-mind emphasizing concerned accepts observing
36
- referring acting empowers reaffirming].freeze
8
+ CONSIDERATION_TYPE_ENUM = %w[
9
+ acknowledging basing considering identifying noting recalling recognises according following
10
+ recognising recognizing
11
+ ].freeze
37
12
 
38
13
  attribute :type, :string, values: CONSIDERATION_TYPE_ENUM
39
- attribute :date, :date
14
+ attribute :dates, ResolutionDate, collection: true
40
15
  attribute :message, :string
16
+ attribute :subject, :string
41
17
 
42
18
  key_value do
43
19
  map "type", to: :type
44
- map "date", to: :date
20
+ map "dates", to: :dates
45
21
  map "message", to: :message
22
+ map "subject", to: :subject
46
23
  end
47
24
  end
48
25
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "resolution_date"
5
+ require_relative "url"
6
+
7
+ module Edoxen
8
+ class Metadata < Lutaml::Model::Serializable
9
+ attribute :title, :string
10
+ attribute :identifier, :string
11
+ attribute :dates, ResolutionDate, collection: true
12
+ attribute :source, :string
13
+ attribute :venue, :string
14
+ attribute :chair, :string
15
+ attribute :urls, Url, collection: true
16
+
17
+ key_value do
18
+ map "title", to: :title
19
+ map "identifier", to: :identifier
20
+ map "dates", to: :dates
21
+ map "source", to: :source
22
+ map "venue", to: :venue
23
+ map "chair", to: :chair
24
+ map "urls", to: :urls
25
+ end
26
+ end
27
+ end
@@ -1,61 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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"
4
10
 
5
11
  module Edoxen
6
12
  class Resolution < Lutaml::Model::Serializable
7
13
  RESOLUTION_TYPE_ENUM = %w[resolution recommendation decision declaration].freeze
8
14
 
9
- attribute :category, :string
10
- attribute :dates, :date, collection: true
15
+ attribute :dates, ResolutionDate, collection: true
11
16
  attribute :subject, :string
12
17
  attribute :title, :string
13
18
  attribute :type, :string, values: RESOLUTION_TYPE_ENUM
14
19
  attribute :identifier, :string
20
+ attribute :message, :string
21
+ attribute :considering, :string
15
22
  attribute :considerations, Consideration, collection: true
16
23
  attribute :approvals, Approval, collection: true
17
24
  attribute :actions, Action, collection: true
25
+ attribute :meeting_identifier, MeetingIdentifier
26
+ attribute :relations, ResolutionRelation, collection: true
27
+ attribute :categories, :string, collection: true
28
+ attribute :urls, Url, collection: true
18
29
 
19
30
  key_value do
20
- map "category", to: :category
21
31
  map "dates", to: :dates
22
32
  map "subject", to: :subject
23
33
  map "title", to: :title
24
34
  map "type", to: :type
25
35
  map "identifier", to: :identifier
36
+ map "message", to: :message
37
+ map "considering", to: :considering
26
38
  map "considerations", to: :considerations
27
39
  map "approvals", to: :approvals
28
40
  map "actions", to: :actions
41
+ map "meeting_identifier", to: :meeting_identifier
42
+ map "relations", to: :relations
43
+ map "categories", to: :categories
44
+ map "urls", to: :urls
29
45
  end
30
-
31
- # Example of a Resolution
32
- # category: Resolutions related to JWG 1
33
- # dates:
34
- # - 2019-10-17
35
- # subject: ISO/TC 154
36
- # title: "Adoption of NWIP ballot for ISO/PWI 9735-11 "Electronic data..."
37
- # identifier: 2019-01
38
- # considerations:
39
- # - type: considering
40
- # date_effective: 2019-10-17
41
- # message: considering the voting result ...
42
-
43
- # - type: considering
44
- # date_effective: 2019-10-17
45
- # message: considering the importance of ...
46
-
47
- # - type: considering
48
- # date_effective: 2019-10-17
49
- # message: considering the request from JWG1...
50
-
51
- # approvals:
52
- # - type: affirmative
53
- # degree: unanimous
54
- # message: The resolution was taken by unanimity.
55
-
56
- # actions:
57
- # - type: resolves
58
- # date_effective: 2019-10-17
59
- # message: resolves to submit ISO 9735-11...
60
46
  end
61
47
  end
@@ -1,33 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # class ResolutionRelation {
4
- # source: StructuredIdentifier (Resolution)
5
- # destination: StructuredIdentifier (Resolution)
6
- # type: ResolutionRelationType
7
- # }
3
+ require "lutaml/model"
8
4
 
9
- # enum ResolutionRelationType {
10
- # annexOf {
11
- # This resolution is an annex to the target resolution.
12
- # }
5
+ module Edoxen
6
+ class ResolutionDate < Lutaml::Model::Serializable
7
+ attribute :start, :date
8
+ attribute :end, :date
9
+ attribute :kind, :string, values: %w[ballot enactment effective decision meeting]
13
10
 
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
- # }
11
+ key_value do
12
+ map "start", to: :start
13
+ map "end", to: :end
14
+ map "kind", to: :kind
15
+ end
16
+ end
17
+ end
@@ -35,7 +35,7 @@
35
35
  require "lutaml/model"
36
36
 
37
37
  module Edoxen
38
- class ResolutionRelationship < Lutaml::Model::Serializable
38
+ class ResolutionRelation < Lutaml::Model::Serializable
39
39
  RESOLUTION_RELATIONSHIP_ENUM = %w[annexOf hasAnnex updates refines replaces obsoletes considers].freeze
40
40
 
41
41
  attribute :source, :string
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "metadata"
5
+ require_relative "resolution"
6
+
7
+ module Edoxen
8
+ class ResolutionSet < Lutaml::Model::Serializable
9
+ attribute :metadata, Metadata
10
+ attribute :resolutions, Resolution, collection: true
11
+
12
+ key_value do
13
+ map "metadata", to: :metadata
14
+ map "resolutions", to: :resolutions
15
+ end
16
+ end
17
+ end