genericode 0.1.0 → 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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +27 -1
  3. data/.rubocop_todo.yml +66 -0
  4. data/README.adoc +282 -0
  5. data/exe/genericode +7 -0
  6. data/lib/genericode/agency.rb +25 -5
  7. data/lib/genericode/annotation.rb +10 -4
  8. data/lib/genericode/any_other_content.rb +2 -2
  9. data/lib/genericode/any_other_language_content.rb +3 -3
  10. data/lib/genericode/canonical_uri.rb +32 -0
  11. data/lib/genericode/cli/code_lister.rb +67 -0
  12. data/lib/genericode/cli/code_lookup.rb +22 -0
  13. data/lib/genericode/cli/commands.rb +76 -0
  14. data/lib/genericode/cli/converter.rb +35 -0
  15. data/lib/genericode/cli/validator.rb +21 -0
  16. data/lib/genericode/cli.rb +8 -0
  17. data/lib/genericode/code_list.rb +257 -9
  18. data/lib/genericode/code_list_ref.rb +9 -6
  19. data/lib/genericode/code_list_set.rb +39 -2
  20. data/lib/genericode/code_list_set_ref.rb +9 -6
  21. data/lib/genericode/column.rb +37 -11
  22. data/lib/genericode/column_ref.rb +7 -7
  23. data/lib/genericode/column_set.rb +3 -3
  24. data/lib/genericode/column_set_ref.rb +4 -4
  25. data/lib/genericode/data.rb +5 -5
  26. data/lib/genericode/data_restrictions.rb +3 -3
  27. data/lib/genericode/datatype_facet.rb +10 -7
  28. data/lib/genericode/general_identifier.rb +6 -6
  29. data/lib/genericode/identification.rb +53 -11
  30. data/lib/genericode/json/canonical_uri_mixin.rb +17 -0
  31. data/lib/genericode/json/short_name_mixin.rb +17 -0
  32. data/lib/genericode/key.rb +34 -9
  33. data/lib/genericode/key_column_ref.rb +3 -3
  34. data/lib/genericode/key_ref.rb +6 -6
  35. data/lib/genericode/long_name.rb +17 -7
  36. data/lib/genericode/mime_typed_uri.rb +5 -5
  37. data/lib/genericode/row.rb +2 -2
  38. data/lib/genericode/short_name.rb +10 -5
  39. data/lib/genericode/simple_code_list.rb +2 -2
  40. data/lib/genericode/simple_value.rb +3 -3
  41. data/lib/genericode/utils.rb +50 -0
  42. data/lib/genericode/value.rb +5 -4
  43. data/lib/genericode/version.rb +1 -1
  44. data/lib/genericode.rb +12 -0
  45. data/oasis-reqs-implemented.md +31 -0
  46. data/oasis-requirements-1.0.md +56 -0
  47. metadata +28 -54
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36468da54f60d5e0013f2e4e16941051f0b09288d48f6560ceafa3f7b1599af1
4
- data.tar.gz: fceaa075b3b6b994659952046e4ebc92eeada80e67d8a2dcce548460223c8912
3
+ metadata.gz: 45525e09245553fcda6cb3606ec8f7b17c0fde0daf6507c41e47008d5e8dce43
4
+ data.tar.gz: 66c0b7b056d47c096d736176d9c575df8fba8c830f539d14e076b43a6f07cda8
5
5
  SHA512:
6
- metadata.gz: aefac994fc63be20cb0c436b5411efb25585bb6013885b0cbc7641f6f79a11c36c93eaf5acd34debd11dc8b42b4f59970e28a1e63ec014196e8927d777996651
7
- data.tar.gz: 6eb782a0098930abbc8ed164b9a9218506783416ad69deeddfb47a97b6279600784d85f50d7e6ebcc78dfff680469f5f94d915de8a6c10d5646d00974202884a
6
+ metadata.gz: d6dd2ebe439fde9ab7584ef67e149b3d79b026ae980b8beec200c5fd99a45e95c35b70663bfd8d27566095778394ce6a65ad2f4e7ff72b4706b7028c94ba55a5
7
+ data.tar.gz: 59951937dd0cd26496fe64e13f9ba70c12ed7d2757609ebfa10633e4d21a233fae230fc234b7da559f4bc12e1c121b029bec786af445f74d6fd5a703dcff83b0
data/.rubocop.yml CHANGED
@@ -1,8 +1,34 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  AllCops:
2
- TargetRubyVersion: 3.0
4
+ NewCops: enable
5
+ SuggestExtensions: false
6
+ TargetRubyVersion: 2.7
7
+
8
+ Gemspec/DevelopmentDependencies:
9
+ Enabled: false
10
+
11
+ Gemspec/RequireMFA:
12
+ Enabled: false
13
+
14
+ Metrics/BlockLength:
15
+ AllowedMethods:
16
+ - describe
17
+
18
+ Style/Documentation:
19
+ Enabled: false
3
20
 
4
21
  Style/StringLiterals:
5
22
  EnforcedStyle: double_quotes
6
23
 
7
24
  Style/StringLiteralsInInterpolation:
8
25
  EnforcedStyle: double_quotes
26
+
27
+ Style/TrailingCommaInArguments:
28
+ EnforcedStyleForMultiline: consistent_comma
29
+
30
+ Style/TrailingCommaInArrayLiteral:
31
+ EnforcedStyleForMultiline: consistent_comma
32
+
33
+ Style/TrailingCommaInHashLiteral:
34
+ EnforcedStyleForMultiline: consistent_comma
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,66 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-01-11 09:46:54 UTC using RuboCop version 1.70.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 4
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
12
+ # Include: **/*.gemfile, **/Gemfile, **/gems.rb
13
+ Bundler/OrderedGems:
14
+ Exclude:
15
+ - 'Gemfile'
16
+
17
+ # Offense count: 1
18
+ # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
19
+ Lint/DuplicateBranch:
20
+ Exclude:
21
+ - 'lib/genericode/code_list.rb'
22
+
23
+ # Offense count: 1
24
+ # This cop supports safe autocorrection (--autocorrect).
25
+ # Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects.
26
+ Lint/Void:
27
+ Exclude:
28
+ - 'lib/genericode/code_list.rb'
29
+
30
+ # Offense count: 8
31
+ # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
32
+ Metrics/AbcSize:
33
+ Max: 182
34
+
35
+ # Offense count: 2
36
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
37
+ # AllowedMethods: refine
38
+ Metrics/BlockLength:
39
+ Max: 34
40
+
41
+ # Offense count: 1
42
+ # Configuration parameters: CountComments, CountAsOne.
43
+ Metrics/ClassLength:
44
+ Max: 216
45
+
46
+ # Offense count: 5
47
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
48
+ Metrics/CyclomaticComplexity:
49
+ Max: 92
50
+
51
+ # Offense count: 10
52
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
53
+ Metrics/MethodLength:
54
+ Max: 86
55
+
56
+ # Offense count: 3
57
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
58
+ Metrics/PerceivedComplexity:
59
+ Max: 92
60
+
61
+ # Offense count: 2
62
+ # This cop supports safe autocorrection (--autocorrect).
63
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
64
+ # URISchemes: http, https
65
+ Layout/LineLength:
66
+ Max: 121
data/README.adoc ADDED
@@ -0,0 +1,282 @@
1
+ = Genericode
2
+
3
+ image:https://img.shields.io/gem/v/genericode.svg["Gem Version", link="https://rubygems.org/gems/genericode"]
4
+ image:https://github.com/lutaml/genericode/workflows/rake/badge.svg["Build Status", link="https://github.com/lutaml/genericode/actions?workflow=rake"]
5
+ image:https://codeclimate.com/github/lutaml/genericode/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/lutaml/genericode"]
6
+
7
+ == Purpose
8
+
9
+ This Ruby gem implements the
10
+ http://docs.oasis-open.org/codelist/genericode/doc/oasis-code-list-representation-genericode.html[OASIS Genericode 1.0 standard].
11
+
12
+ It can be used to parse and generate code lists in the Genericode format,
13
+ including:
14
+
15
+ * Genericode XML: `*.gc`
16
+ * Genericode JSON: `*.gcj`
17
+
18
+
19
+ == Installation
20
+
21
+ To install the genericode gem, use one of the following methods.
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ [source,ruby]
26
+ ----
27
+ gem 'genericode'
28
+ ----
29
+
30
+ Then execute:
31
+
32
+ [source,sh]
33
+ ----
34
+ $ bundle install
35
+ ----
36
+
37
+ Or install it directly using:
38
+
39
+ [source,sh]
40
+ ----
41
+ $ gem install genericode
42
+ ----
43
+
44
+ After installation, you can start using the genericode gem in your Ruby projects
45
+ or via the command-line interface.
46
+
47
+
48
+ == Genericode path syntax
49
+
50
+ The "Genericode path" is a simplified syntax used to navigate through and
51
+ extract data from Genericode structures. It's designed to be intuitive and
52
+ flexible.
53
+
54
+ === Basic syntax
55
+
56
+ The basic syntax uses a combination of column names and values to uniquely
57
+ identify a row and retrieve associated data:
58
+
59
+ ----
60
+ column_name:value
61
+ ----
62
+
63
+ This will return all data associated with the row where `column_name` has the
64
+ specified `value`.
65
+
66
+ === Multiple column lookup
67
+
68
+ To narrow down the search using multiple columns:
69
+
70
+ ----
71
+ column1:value1,column2:value2
72
+ ----
73
+
74
+ This will return data for the row where `column1` has `value1` AND `column2` has
75
+ `value2`.
76
+
77
+ === Specific column retrieval
78
+
79
+ To retrieve the value of a specific column for a given row:
80
+
81
+ ----
82
+ column1:value1>target_column
83
+ ----
84
+
85
+ This will return the value of `target_column` for the row where `column1` has
86
+ `value1`.
87
+
88
+
89
+ == CLI commands
90
+
91
+ The Genericode gem provides a command-line interface (CLI) with the following
92
+ commands:
93
+
94
+ === Convert
95
+
96
+ Converts between Genericode XML and JSON formats.
97
+
98
+ Usage:
99
+ [source,sh]
100
+ ----
101
+ $ genericode convert INPUT_FILE OUTPUT_FILE
102
+ ----
103
+
104
+ Example:
105
+ [source,sh]
106
+ ----
107
+ $ genericode convert input.gc output.gcj
108
+ ----
109
+
110
+ This command is useful when you need to convert between XML and JSON
111
+ representations of your code lists.
112
+
113
+ === Validate
114
+
115
+ Validates a Genericode file.
116
+
117
+ Usage:
118
+ [source,sh]
119
+ ----
120
+ $ genericode validate FILE
121
+ ----
122
+
123
+ Example:
124
+ [source,sh]
125
+ ----
126
+ $ genericode validate codelist.gc
127
+ ----
128
+
129
+ Use this command to check if your Genericode file is valid according to the
130
+ specification.
131
+
132
+ === List codes
133
+
134
+ Lists all codes and their associated data in a Genericode file.
135
+
136
+ Usage:
137
+ [source,sh]
138
+ ----
139
+ $ genericode list_codes FILE [--format=FORMAT] [--output=FILE]
140
+ ----
141
+
142
+ Example:
143
+ [source,sh]
144
+ ----
145
+ $ genericode list_codes country_codes.gc
146
+ $ genericode list_codes country_codes.gc --format=table
147
+ $ genericode list_codes country_codes.gc --format=tsv --output=codes.tsv
148
+ ----
149
+
150
+ This command displays all the data in the Genericode file. By default, it
151
+ outputs in TSV (Tab-Separated Values) format. You can specify the `--format`
152
+ option to choose between `tsv` (default) and `table` formats. Use the `--output`
153
+ option to save the result to a file.
154
+
155
+ === Lookup
156
+
157
+ Looks up specific data in a Genericode file using the simplified Genericode path
158
+ syntax.
159
+
160
+ Usage:
161
+ [source,sh]
162
+ ----
163
+ $ genericode lookup FILE PATH
164
+ ----
165
+
166
+ Examples:
167
+ [source,sh]
168
+ ----
169
+ $ genericode lookup country_codes.gc "code:US"
170
+ $ genericode lookup country_codes.gc "code:FR>name"
171
+ $ genericode lookup currency_codes.gc "alpha_code:USD,numeric_code:840>name"
172
+ ----
173
+
174
+ Use this command to extract specific information from a Genericode file using
175
+ the simplified path syntax. The syntax allows you to:
176
+
177
+ * Retrieve all data for a specific code: `column:value`
178
+ * Get a specific column value for a given code: `column:value>target_column`
179
+ * Use multiple columns to narrow down the search: `column1:value1,column2:value2`
180
+
181
+ To use these CLI commands, ensure that the Genericode gem is installed and
182
+ available in your system's PATH.
183
+
184
+ == Ruby API
185
+
186
+ [source,ruby]
187
+ ----
188
+ require 'genericode'
189
+
190
+ # Load a Genericode file
191
+ gc = Genericode::CodeList.from_file("country_codes.gc")
192
+
193
+ # Lookup examples
194
+ gc.lookup("code:US")
195
+ # => {"code"=>"US", "name"=>"United States", ...}
196
+
197
+ gc.lookup("code:FR>name")
198
+ # => "France"
199
+
200
+ gc.lookup("alpha_code:USD,numeric_code:840>name")
201
+ # => "US Dollar"
202
+
203
+ # Other API usage remains the same
204
+ gc.identification.short_name.content
205
+ # => "CountryCodes"
206
+
207
+ gc.simple_code_list.row.map(&:value).flatten.map(&:simple_value).map(&:content)
208
+ # => ["US", "United States", "FR", "France", ...]
209
+ ----
210
+
211
+ [source,ruby]
212
+ ----
213
+ require 'genericode'
214
+
215
+ # XML element root of `<<gc:CodeList>`
216
+ gc = Genericode::CodeList.from_xml(File.read("spec/fixtures/xml/CaseTypeCode.gc"))
217
+ # Or:
218
+ # JSON element root of `<<gc:CodeList>`
219
+ # gc = Genericode.from_json(File.read("spec/fixtures/json/CaseTypeCode.gcj"))
220
+
221
+ gc.identification.short_name.content
222
+ # => "CaseTypeCode"
223
+ gc.identification.short_name.version
224
+ # => "5.0"
225
+
226
+ gc.simple_code_list.row.map(&:value).flatten.map(&:simple_value).map(&:content)
227
+ # => ["appellate", "bankruptcy", "citation", "civil", "criminal", "domestic", "juvenile"]
228
+ ----
229
+
230
+ [source,ruby]
231
+ ----
232
+ require 'genericode'
233
+
234
+ # XML element root of `<<gc:CodeList>`
235
+ gc = Genericode::CodeList.from_json(File.read("spec/fixtures/xml/CaseTypeCode.gcj"))
236
+
237
+ gc.identification.short_name.content
238
+ # => "CaseTypeCode"
239
+ gc.identification.short_name.version
240
+ # => "5.0"
241
+
242
+ gc.simple_code_list.row.map(&:value).flatten.map(&:simple_value).map(&:content)
243
+ # => ["appellate", "bankruptcy", "citation", "civil", "criminal", "domestic", "juvenile"]
244
+ ----
245
+
246
+ == Tests
247
+
248
+ The `spec/fixtures` folder tests the library against known examples of Genericode.
249
+
250
+ Including:
251
+
252
+ `spec/fixtures/json`:: JSON examples.
253
+
254
+ `spec/fixtures/json/standard/*.gcj`:: Genericode JSON examples from the OASIS
255
+ genericode standard 1.0
256
+ https://github.com/oasis-tcs/codelist-genericode/tree/genericode-v1.0-os/json-example[(GitHub)].
257
+
258
+ `spec/fixtures/xml`:: XML examples.
259
+
260
+ `spec/fixtures/xml/standard/*.gc`:: Genericode XML examples from the OASIS
261
+ genericode standard 1.0
262
+ https://github.com/oasis-tcs/codelist-genericode/tree/genericode-v1.0-os/xml[(GitHub)].
263
+
264
+ `spec/fixtures/xml/niem/*.gc`:: Genericode XML examples from the
265
+ https://reference.niem.gov/niem/specification/code-lists/1.0beta1/niem-code-lists-1.0beta1-2016-03-15.html[NIEM Code Lists Specification, Version 1.0beta1].
266
+ https://github.com/NIEM/NIEM-Code-Lists-Spec/blob/master/example/make-model/make-model.gc[(GitHub)]
267
+ +
268
+ NOTE: A modification was made to remove unrelated namespaces to allow tests to
269
+ pass.
270
+
271
+ `spec/fixtures/xml/ubl/*.gc`:: Genericode XML examples from the
272
+ https://github.com/oasis-tcs/ubl[OASIS UBL 2.3 repository] (branch `ubl-2.5-csd01`),
273
+ https://github.com/oasis-tcs/ubl/tree/ubl-2.5-csd01/os-UBL-2.3/cl/gc/default[`os-UBL-2.3/cl/gc/default`].
274
+
275
+ NOTE: The "OASIS genericode standard 1.0" refers to the
276
+ https://docs.oasis-open.org/codelist/genericode/v1.0/os/genericode-v1.0-os.html#S-LONGNAME-COMPLEX-TYPE["OASIS Code List Representation (genericode) Version 1.0"]
277
+
278
+ == License
279
+
280
+ Copyright Ribose.
281
+
282
+ BSD-3 license.
data/exe/genericode ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/genericode"
5
+ require_relative "../lib/genericode/cli"
6
+
7
+ Genericode::Cli::Commands.start(ARGV)
@@ -1,21 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "general_identifier"
6
6
  require_relative "long_name"
7
7
  require_relative "short_name"
8
+ require_relative "json/short_name_mixin"
9
+ require_relative "utils"
8
10
 
9
11
  module Genericode
10
- class Agency < Shale::Mapper
12
+ class Agency < Lutaml::Model::Serializable
13
+ include Json::ShortNameMixin
14
+
11
15
  attribute :short_name, ShortName
12
16
  attribute :long_name, LongName, collection: true
13
17
  attribute :identifier, GeneralIdentifier, collection: true
14
18
 
15
19
  json do
16
- map "ShortName", to: :short_name
17
- map "LongName", to: :long_name
18
- map "Identifier", to: :identifier
20
+ map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json }
21
+ map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json }
22
+ map "Identifier", to: :identifier, with: { from: :identifier_from_json, to: :identifier_to_json }
23
+ end
24
+
25
+ def long_name_from_json(model, value)
26
+ model.long_name = LongName.of_json(Utils.array_wrap(value))
27
+ end
28
+
29
+ def long_name_to_json(model, doc)
30
+ doc["LongName"] = LongName.as_json(Utils.one_or_all(model.long_name))
31
+ end
32
+
33
+ def identifier_from_json(model, value)
34
+ model.identifier = GeneralIdentifier.of_json(Utils.array_wrap(value))
35
+ end
36
+
37
+ def identifier_to_json(model, doc)
38
+ doc["Identifier"] = GeneralIdentifier.as_json(Utils.one_or_all(model.identifier))
19
39
  end
20
40
 
21
41
  xml do
@@ -1,18 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "any_other_content"
6
6
  require_relative "any_other_language_content"
7
7
 
8
8
  module Genericode
9
- class Annotation < Shale::Mapper
9
+ class Annotation < Lutaml::Model::Serializable
10
10
  attribute :description, AnyOtherLanguageContent, collection: true
11
11
  attribute :app_info, AnyOtherContent
12
12
 
13
13
  json do
14
14
  map "Description", to: :description
15
- map "AppInfo", to: :app_info
15
+ map "AppInfo", to: :app_info, render_nil: true
16
+ end
17
+
18
+ def self.of_json(hash, **)
19
+ hash = { "AppInfo" => hash } if hash.any?
20
+
21
+ super
16
22
  end
17
23
 
18
24
  xml do
@@ -20,7 +26,7 @@ module Genericode
20
26
  namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc"
21
27
 
22
28
  map_element "Description", to: :description, prefix: nil, namespace: nil
23
- map_element "AppInfo", to: :app_info, prefix: nil, namespace: nil
29
+ map_element "AppInfo", to: :app_info, prefix: nil, namespace: nil, render_nil: true
24
30
  end
25
31
  end
26
32
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  module Genericode
6
- class AnyOtherContent < Shale::Mapper
6
+ class AnyOtherContent < Lutaml::Model::Serializable
7
7
  xml do
8
8
  root "AnyOtherContent"
9
9
  namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc"
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  module Genericode
6
- class AnyOtherLanguageContent < Shale::Mapper
7
- attribute :lang, Shale::Type::String
6
+ class AnyOtherLanguageContent < Lutaml::Model::Serializable
7
+ attribute :lang, :string
8
8
 
9
9
  json do
10
10
  map "lang", to: :lang
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require "uri"
5
+
6
+ module Genericode
7
+ # Rule 4: Must be an absolute URI, must not be relative
8
+ class CanonicalUri < Lutaml::Model::Serializable
9
+ attribute :content, :string
10
+
11
+ xml do
12
+ root "CanonicalUri"
13
+ map_content to: :content
14
+ end
15
+
16
+ def valid?
17
+ valid_uri?(content) && absolute_uri?(content)
18
+ end
19
+
20
+ private
21
+
22
+ def valid_uri?(uri)
23
+ uri =~ URI::DEFAULT_PARSER.make_regexp
24
+ end
25
+
26
+ def absolute_uri?(uri)
27
+ URI.parse(uri).absolute?
28
+ rescue URI::InvalidURIError
29
+ false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../code_list"
4
+ require "tabulo"
5
+ require "csv"
6
+
7
+ module Genericode
8
+ module Cli
9
+ class CodeLister
10
+ class << self
11
+ def list_codes(file_path, format: :tsv)
12
+ code_list = CodeList.from_file(file_path)
13
+
14
+ # Validate data types
15
+ code_list.validate_verbose.each do |error|
16
+ raise Error, "#{error[:code]}: #{error[:message]}" if error[:code] == "INVALID_DATA_TYPE"
17
+
18
+ # Ensure valid ColumnRefs
19
+ raise Error, "#{error[:code]}: #{error[:message]}" if error[:code] == "INVALID_COLUMN_REF"
20
+ end
21
+
22
+ case format
23
+ when :tsv
24
+ list_tsv(code_list)
25
+ when :table
26
+ list_table(code_list)
27
+ else
28
+ raise Error, "Unknown format: #{format}"
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def list_tsv(code_list)
35
+ columns = code_list.column_set.column
36
+ rows = code_list.simple_code_list.row
37
+
38
+ CSV.generate(col_sep: "\t") do |csv|
39
+ csv << columns.map { |col| col.short_name.content }
40
+ rows.each do |row|
41
+ csv << columns.map do |col|
42
+ value = row.value.find { |v| v.column_ref == col.id }
43
+ value&.simple_value&.content || ""
44
+ end
45
+ end
46
+ end.strip.encode("UTF-8")
47
+ end
48
+
49
+ def list_table(code_list)
50
+ columns = code_list.column_set.column
51
+ rows = code_list.simple_code_list.row
52
+
53
+ table = Tabulo::Table.new(rows) do |t|
54
+ columns.each do |column|
55
+ t.add_column(column.short_name.content) do |row|
56
+ value = row.value.find { |v| v.column_ref == column.id }
57
+ value&.simple_value&.content || ""
58
+ end
59
+ end
60
+ end
61
+
62
+ table.to_s
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../code_list"
4
+
5
+ module Genericode
6
+ module Cli
7
+ class CodeLookup
8
+ def self.lookup(file_path, path)
9
+ code_list = CodeList.from_file(file_path)
10
+ result = code_list.lookup(path)
11
+
12
+ if result.is_a?(Hash)
13
+ result.map { |k, v| "#{k}: #{v}" }.join("\n")
14
+ else
15
+ result.to_s
16
+ end
17
+ rescue Error => e
18
+ raise Error, e.message
19
+ end
20
+ end
21
+ end
22
+ end