genericode 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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