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
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require_relative "validator"
5
+ require_relative "converter"
6
+ require_relative "code_lister"
7
+ require_relative "code_lookup"
8
+
9
+ module Genericode
10
+ module Cli
11
+ class Commands < Thor
12
+ desc "convert INPUT OUTPUT", "Convert between Genericode XML and JSON formats"
13
+
14
+ def convert(input, output)
15
+ puts "Conversion successful." if Converter.convert(input, output)
16
+ rescue Error => e
17
+ puts "Conversion failed: #{e.message}"
18
+ end
19
+
20
+ desc "validate FILE", "Validate a Genericode file"
21
+ option :verbose, type: :boolean, desc: "Show detailed validation results"
22
+
23
+ def validate(file)
24
+ code_list = CodeList.from_file(file)
25
+ if options[:verbose]
26
+ results = code_list.validate_verbose
27
+ if results.empty?
28
+ puts "File is valid."
29
+ else
30
+ puts "File is invalid. Issues found:"
31
+ results.each do |error|
32
+ puts " [#{error[:code]}] #{error[:message]}"
33
+ end
34
+ end
35
+ elsif code_list.valid?
36
+ puts "File is valid."
37
+ else
38
+ puts "File is invalid."
39
+ end
40
+ rescue Error => e
41
+ puts "Validation failed: #{e.message}"
42
+ end
43
+
44
+ desc "list_codes FILE", "List all codes and their associated data in a Genericode file"
45
+ option :format, type: :string, default: "tsv", enum: %w[tsv table], desc: "Output format (tsv or table)"
46
+ option :output, type: :string, desc: "Output file path (default: stdout)"
47
+
48
+ def list_codes(file)
49
+ format = (options[:format] || "tsv").to_sym
50
+ result = CodeLister.list_codes(file, format: format)
51
+
52
+ if options[:output]
53
+ File.write(options[:output], result)
54
+ puts "Codes listed in #{options[:output]}"
55
+ else
56
+ puts result
57
+ end
58
+ rescue Error => e
59
+ puts "Listing codes failed: #{e.message}"
60
+ end
61
+
62
+ desc "lookup FILE PATH", "Look up a particular code using Genericode path"
63
+
64
+ def lookup(file, path)
65
+ result = CodeLookup.lookup(file, path)
66
+ if result.is_a?(Hash)
67
+ result.each { |k, v| puts "#{k}: #{v}" }
68
+ else
69
+ puts result
70
+ end
71
+ rescue Error => e
72
+ puts "Lookup failed: #{e.message}"
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Genericode
4
+ module Cli
5
+ class Converter
6
+ def self.convert(input_path, output_path)
7
+ input_format = File.extname(input_path)
8
+ output_format = File.extname(output_path)
9
+
10
+ raise Error, "Invalid input format" unless [".gc", ".gcj"].include?(input_format)
11
+ raise Error, "Invalid output format" unless [".gc", ".gcj"].include?(output_format)
12
+ raise Error, "Input and output formats are the same" if input_format == output_format
13
+
14
+ # begin
15
+ code_list = CodeList.from_file(input_path)
16
+
17
+ result = if output_format == ".gcj"
18
+ code_list.to_json
19
+ else
20
+ code_list.to_xml
21
+ end
22
+
23
+ File.write(output_path, result)
24
+ true
25
+ # rescue JSON::ParserError => e
26
+ # raise Error, "Invalid JSON in input file: #{e.message}"
27
+ # rescue Shale::ParseError => e
28
+ # raise Error, "Invalid XML in input file: #{e.message}"
29
+ # rescue StandardError => e
30
+ # raise Error, "Conversion error: #{e.message}"
31
+ # end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Genericode
4
+ module Cli
5
+ class Validator
6
+ def self.validate(file_path)
7
+ raise Error, "File does not exist" unless File.exist?(file_path)
8
+ raise Error, "Invalid file format" unless file_path.end_with?(".gc", ".gcj")
9
+
10
+ code_list = CodeList.from_file(file_path)
11
+
12
+ raise Error, "No columns defined" if code_list.column_set.nil? || code_list.column_set.column.empty?
13
+ raise Error, "No rows defined" if code_list.simple_code_list.nil? || code_list.simple_code_list.row.empty?
14
+
15
+ raise Error, "Invalid Genericode structure" unless code_list.valid?
16
+
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Genericode
4
+ module Cli
5
+ end
6
+ end
7
+
8
+ require_relative "cli/commands"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
+ require "uri"
4
5
 
5
6
  require_relative "annotation"
6
7
  require_relative "column_set"
@@ -9,35 +10,282 @@ require_relative "identification"
9
10
  require_relative "simple_code_list"
10
11
 
11
12
  module Genericode
12
- class CodeList < Shale::Mapper
13
+ class CodeList < Lutaml::Model::Serializable
13
14
  attribute :annotation, Annotation
14
15
  attribute :identification, Identification
15
16
  attribute :column_set, ColumnSet
16
17
  attribute :column_set_ref, ColumnSetRef
17
18
  attribute :simple_code_list, SimpleCodeList
18
- attribute :schema_location, Shale::Type::String
19
+
20
+ def self.from_file(file_path)
21
+ content = File.read(file_path)
22
+ if file_path.end_with?(".gc")
23
+ from_xml(content)
24
+ elsif file_path.end_with?(".gcj")
25
+ from_json(content)
26
+ else
27
+ raise Error, "Unsupported file format. Expected .gc or .gcj file."
28
+ end
29
+ end
19
30
 
20
31
  json do
21
32
  map "Annotation", to: :annotation
22
33
  map "Identification", to: :identification
23
- map "ColumnSet", to: :column_set
34
+ map "Columns", to: :column_set, with: { from: :column_set_from_json, to: :column_set_to_json }
24
35
  map "ColumnSetRef", to: :column_set_ref
25
- map "SimpleCodeList", to: :simple_code_list
36
+ map "Keys", to: :key, delegate: :column_set, with: { from: :key_from_json, to: :key_to_json }
37
+ map "Codes", to: :simple_code_list, with: { from: :simple_code_list_from_json, to: :simple_code_list_to_json }
38
+ end
39
+
40
+ def column_set_from_json(model, value)
41
+ model.column_set = ColumnSet.of_json({ "Column" => value })
42
+ end
43
+
44
+ def column_set_to_json(model, doc)
45
+ doc["Columns"] = Column.as_json(model.column_set.column)
46
+ end
47
+
48
+ def key_from_json(model, value)
49
+ model.column_set.key = Key.of_json(value)
50
+ end
51
+
52
+ def key_to_json(model, doc)
53
+ doc["Keys"] = Key.as_json(model.column_set.key)
54
+ end
55
+
56
+ def simple_code_list_from_json(model, value)
57
+ rows = value.map do |x|
58
+ values = x.map do |k, v|
59
+ Value.new(column_ref: k, simple_value: SimpleValue.new(content: v))
60
+ end
61
+
62
+ Row.new(value: values)
63
+ end
64
+
65
+ model.simple_code_list = SimpleCodeList.new(row: rows)
66
+ end
67
+
68
+ def simple_code_list_to_json(model, doc)
69
+ doc["Codes"] = model.simple_code_list.row.map do |row|
70
+ row.value.to_h { |v| [v.column_ref, v.simple_value.content] }
71
+ end
26
72
  end
27
73
 
28
74
  xml do
29
75
  root "CodeList"
30
76
  namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc"
31
77
 
32
- map_attribute "schemaLocation", to: :schema_location,
33
- namespace: "http://www.w3.org/2001/XMLSchema-instance",
34
- prefix: "xsi"
35
-
36
78
  map_element "Annotation", to: :annotation, prefix: nil, namespace: nil
37
79
  map_element "Identification", to: :identification, prefix: nil, namespace: nil
38
80
  map_element "ColumnSet", to: :column_set, prefix: nil, namespace: nil
39
81
  map_element "ColumnSetRef", to: :column_set_ref, prefix: nil, namespace: nil
40
82
  map_element "SimpleCodeList", to: :simple_code_list, prefix: nil, namespace: nil
41
83
  end
84
+
85
+ def lookup(path)
86
+ parts = path.split(">")
87
+ conditions = parts[0].split(",").to_h { |c| c.split(":") }
88
+ target_column = parts[1]
89
+
90
+ result = simple_code_list.row.find do |row|
91
+ conditions.all? do |col, value|
92
+ column = column_set.column.find { |c| c.short_name.content.downcase == col.downcase }
93
+ raise Error, "Column not found: #{col}" unless column
94
+
95
+ row_value = row.value.find { |v| v.column_ref == column.id }&.simple_value&.content
96
+ row_value == value
97
+ end
98
+ end
99
+
100
+ raise Error, "No matching row found for path: #{path}" unless result
101
+
102
+ if target_column
103
+ column = column_set.column.find { |c| c.short_name.content.downcase == target_column.downcase }
104
+ raise Error, "Target column not found: #{target_column}" unless column
105
+
106
+ result.value.find { |v| v.column_ref == column.id }&.simple_value&.content
107
+ else
108
+ result.value.to_h do |v|
109
+ [column_set.column.find do |c|
110
+ c.id == v.column_ref
111
+ end.short_name.content, v.simple_value.content,]
112
+ end
113
+ end
114
+ end
115
+
116
+ def valid?
117
+ validate_verbose.empty?
118
+ end
119
+
120
+ def validate_verbose
121
+ errors = []
122
+
123
+ # Rule 1: ColumnSet presence
124
+ if column_set.nil? || column_set.column.empty?
125
+ errors << { code: "MISSING_COLUMN_SET",
126
+ message: "ColumnSet is missing or empty", }
127
+ end
128
+
129
+ # Rule 2: SimpleCodeList presence
130
+ if simple_code_list.nil? || simple_code_list.row.empty?
131
+ errors << { code: "MISSING_SIMPLE_CODE_LIST",
132
+ message: "SimpleCodeList is missing or empty", }
133
+ end
134
+
135
+ # Rule 3: Unique column IDs
136
+ column_ids = column_set&.column&.map(&:id) || []
137
+ if column_ids.uniq.length != column_ids.length
138
+ errors << { code: "DUPLICATE_COLUMN_IDS", message: "Duplicate column IDs found" }
139
+ end
140
+
141
+ # Rule 4: Verify ColumnRef values
142
+ simple_code_list&.row&.each_with_index do |row, index|
143
+ row.value.each do |value|
144
+ unless column_ids.include?(value.column_ref)
145
+ errors << { code: "INVALID_COLUMN_REF",
146
+ message: "Invalid ColumnRef '#{value.column_ref}' in row #{index + 1}", }
147
+ end
148
+ end
149
+ end
150
+
151
+ # Rule 5: Unique values in columns
152
+ column_set&.column&.each do |col|
153
+ column_values = (simple_code_list&.row&.map do |row|
154
+ row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
155
+ end || []).compact
156
+
157
+ if column_values.uniq.length != column_values.length
158
+ errors << { code: "DUPLICATE_VALUES", message: "Duplicate values found in column '#{col.id}'" }
159
+ end
160
+ end
161
+
162
+ # Rule 6: Required column values
163
+ required_columns = column_set&.column&.select { |col| col.use == "required" } || []
164
+ simple_code_list&.row&.each_with_index do |row, index|
165
+ required_columns.each do |col|
166
+ unless row.value.any? { |v| v.column_ref == col.id && v.simple_value&.content }
167
+ errors << { code: "MISSING_REQUIRED_VALUE",
168
+ message: "Missing value for required column '#{col.short_name&.content}' in row #{index + 1}", }
169
+ end
170
+ end
171
+ end
172
+
173
+ # Rule 7: Data type consistency
174
+ column_set&.column&.each do |col|
175
+ data_type = col.data&.type
176
+ simple_code_list&.row&.each_with_index do |row, index|
177
+ value = row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
178
+ unless value_matches_type?(value, data_type)
179
+ errors << { code: "INVALID_DATA_TYPE",
180
+ message: "Invalid data type for column '#{col.short_name&.content}' in row #{index + 1}", }
181
+ end
182
+ end
183
+ end
184
+
185
+ # Rule 8: Valid canonical URIs
186
+ if identification&.canonical_uri && !valid_uri?(identification.canonical_uri)
187
+ errors << { code: "INVALID_CANONICAL_URI", message: "Invalid canonical URI" }
188
+ end
189
+
190
+ # Rule 19: Datatype ID validation
191
+ column_set&.column&.each do |col|
192
+ if col.data&.type && !valid_datatype_id?(col.data.type)
193
+ errors << { code: "INVALID_DATATYPE_ID",
194
+ message: "Invalid datatype ID for column '#{col.short_name&.content}'", }
195
+ end
196
+
197
+ # Rule 20 and 22: Complex data validation
198
+ if col.data&.type == "*" && col.data&.datatype_library != "*"
199
+ errors << { code: "INVALID_COMPLEX_DATA",
200
+ message: "Invalid complex data configuration for column '#{col.short_name&.content}'", }
201
+ end
202
+
203
+ # Rule 23: Language attribute validation
204
+ if col.data&.lang && col.data_restrictions&.lang
205
+ errors << { code: "DUPLICATE_LANG_ATTRIBUTE",
206
+ message: "Duplicate lang attribute for column '#{col.short_name&.content}'", }
207
+ end
208
+ end
209
+
210
+ # Rule 38: Implicit column reference
211
+ simple_code_list&.row&.each_with_index do |row, index|
212
+ unless row.value.all?(&:column_ref)
213
+ errors << { code: "MISSING_COLUMN_REF", message: "Missing explicit column reference in row #{index + 1}" }
214
+ end
215
+ end
216
+
217
+ # Rule 39: ShortName whitespace check
218
+ column_set&.column&.each do |col|
219
+ if col.short_name&.content&.match?(/\s/)
220
+ errors << { code: "INVALID_SHORT_NAME",
221
+ message: "ShortName '#{col.short_name&.content}' contains whitespace", }
222
+ end
223
+ end
224
+
225
+ # Rule 42 and 43: ComplexValue validation
226
+ simple_code_list&.row&.each_with_index do |row, index|
227
+ row.value.each do |value|
228
+ next unless value.complex_value
229
+
230
+ unless valid_complex_value?(value.complex_value, column_set&.column&.find { |c| c.id == value.column_ref })
231
+ errors << { code: "INVALID_COMPLEX_VALUE",
232
+ message: "Invalid ComplexValue in row #{index + 1}, column '#{value.column_ref}'", }
233
+ end
234
+ end
235
+ end
236
+
237
+ errors
238
+ end
239
+
240
+ private
241
+
242
+ def value_matches_type?(value, type)
243
+ case type
244
+ when "string"
245
+ true # All values can be considered strings
246
+ when "integer"
247
+ value.to_i.to_s == value
248
+ when "decimal"
249
+ begin
250
+ Float(value)
251
+ rescue StandardError
252
+ false
253
+ end
254
+ when "date"
255
+ begin
256
+ Date.parse(value)
257
+ rescue StandardError
258
+ false
259
+ end
260
+ else
261
+ true # If type is unknown, consider it valid
262
+ end
263
+ end
264
+
265
+ def valid_uri?(uri)
266
+ uri =~ URI::DEFAULT_PARSER.make_regexp
267
+ end
268
+
269
+ def valid_datatype_id?(id)
270
+ %w[string token boolean decimal integer date].include?(id)
271
+ end
272
+
273
+ def valid_complex_value?(complex_value, column)
274
+ return true unless complex_value && column&.data
275
+
276
+ if column.data.type == "*"
277
+ true # Any element is allowed
278
+ else
279
+ # Check if the root element name matches the datatype ID
280
+ complex_value.name == column.data.type
281
+ end
282
+
283
+ if column.data.datatype_library == "*"
284
+ true # Any namespace is allowed
285
+ else
286
+ # Check if the namespace matches the datatype library
287
+ complex_value.namespace == column.data.datatype_library
288
+ end
289
+ end
42
290
  end
43
291
  end
@@ -1,19 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
+ require_relative "json/canonical_uri_mixin"
6
7
 
7
8
  module Genericode
8
- class CodeListRef < Shale::Mapper
9
+ class CodeListRef < Lutaml::Model::Serializable
10
+ include Json::CanonicalUriMixin
11
+
9
12
  attribute :annotation, Annotation
10
- attribute :canonical_uri, Shale::Type::String
11
- attribute :canonical_version_uri, Shale::Type::String
12
- attribute :location_uri, Shale::Type::String, collection: true
13
+ attribute :canonical_uri, CanonicalUri
14
+ attribute :canonical_version_uri, :string
15
+ attribute :location_uri, :string, collection: true
13
16
 
14
17
  json do
15
18
  map "Annotation", to: :annotation
16
- map "CanonicalUri", to: :canonical_uri
19
+ map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json }
17
20
  map "CanonicalVersionUri", to: :canonical_version_uri
18
21
  map "LocationUri", to: :location_uri
19
22
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
6
  require_relative "code_list_ref"
@@ -8,7 +8,7 @@ require_relative "code_list_set_ref"
8
8
  require_relative "identification"
9
9
 
10
10
  module Genericode
11
- class CodeListSet < Shale::Mapper
11
+ class CodeListSet < Lutaml::Model::Serializable
12
12
  attribute :annotation, Annotation
13
13
  attribute :identification, Identification
14
14
  attribute :code_list_ref, CodeListRef, collection: true
@@ -33,5 +33,42 @@ module Genericode
33
33
  map_element "CodeListSet", to: :code_list_set, prefix: nil, namespace: nil
34
34
  map_element "CodeListSetRef", to: :code_list_set_ref, prefix: nil, namespace: nil
35
35
  end
36
+
37
+ def validate_verbose
38
+ errors = []
39
+
40
+ # Rule 47: CodeListSet reference validation
41
+ code_list_set_ref&.each do |ref|
42
+ unless valid_uri?(ref.canonical_uri) && valid_uri?(ref.canonical_version_uri)
43
+ errors << { code: "INVALID_CODELIST_SET_REF", message: "Invalid CodeListSet reference URI" }
44
+ end
45
+ end
46
+
47
+ # Rule 48-51: URI validations
48
+ [canonical_uri, canonical_version_uri].each do |uri|
49
+ errors << { code: "INVALID_URI", message: "Invalid URI: #{uri}" } unless valid_uri?(uri)
50
+ end
51
+
52
+ # Rule 52-53: LocationUri validation
53
+ location_uri&.each do |uri|
54
+ unless valid_genericode_uri?(uri)
55
+ errors << { code: "INVALID_LOCATION_URI", message: "Invalid LocationUri: #{uri}" }
56
+ end
57
+ end
58
+
59
+ errors
60
+ end
61
+
62
+ private
63
+
64
+ def valid_uri?(uri)
65
+ uri =~ URI::DEFAULT_PARSER.make_regexp
66
+ end
67
+
68
+ def valid_genericode_uri?(uri)
69
+ # Add logic to check if the URI points to a valid genericode document
70
+ # This might involve making an HTTP request or checking file extensions
71
+ uri.end_with?(".gc", ".gcj")
72
+ end
36
73
  end
37
74
  end
@@ -1,19 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
+ require_relative "canonical_uri"
7
+ require_relative "json/canonical_uri_mixin"
6
8
 
7
9
  module Genericode
8
- class CodeListSetRef < Shale::Mapper
10
+ class CodeListSetRef < Lutaml::Model::Serializable
11
+ include Json::CanonicalUriMixin
9
12
  attribute :annotation, Annotation
10
- attribute :canonical_uri, Shale::Type::String
11
- attribute :canonical_version_uri, Shale::Type::String
12
- attribute :location_uri, Shale::Type::String, collection: true
13
+ attribute :canonical_uri, CanonicalUri
14
+ attribute :canonical_version_uri, :string
15
+ attribute :location_uri, :string, collection: true
13
16
 
14
17
  json do
15
18
  map "Annotation", to: :annotation
16
- map "CanonicalUri", to: :canonical_uri
19
+ map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json }
17
20
  map "CanonicalVersionUri", to: :canonical_version_uri
18
21
  map "LocationUri", to: :location_uri
19
22
  end
@@ -1,32 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
6
  require_relative "data"
7
+ require_relative "canonical_uri"
7
8
  require_relative "long_name"
8
9
  require_relative "short_name"
10
+ require_relative "json/short_name_mixin"
11
+ require_relative "json/canonical_uri_mixin"
12
+ require_relative "utils"
9
13
 
10
14
  module Genericode
11
- class Column < Shale::Mapper
12
- attribute :id, Shale::Type::String
13
- attribute :use, Shale::Type::String
15
+ class Column < Lutaml::Model::Serializable
16
+ include Json::CanonicalUriMixin
17
+ include Json::ShortNameMixin
18
+
19
+ attribute :id, :string
20
+ attribute :use, :string, default: -> { "optional" }
14
21
  attribute :annotation, Annotation
15
22
  attribute :short_name, ShortName
16
23
  attribute :long_name, LongName, collection: true
17
- attribute :canonical_uri, Shale::Type::String
18
- attribute :canonical_version_uri, Shale::Type::String
24
+ attribute :canonical_uri, CanonicalUri
25
+ attribute :canonical_version_uri, :string
19
26
  attribute :data, Data
20
27
 
21
28
  json do
29
+ map "Required", to: :use, with: { from: :use_from_json, to: :use_to_json }
22
30
  map "Id", to: :id
23
- map "Use", to: :use
24
31
  map "Annotation", to: :annotation
25
- map "ShortName", to: :short_name
26
- map "LongName", to: :long_name
27
- map "CanonicalUri", to: :canonical_uri
32
+ map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json }
33
+ map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json }
34
+ map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json }
28
35
  map "CanonicalVersionUri", to: :canonical_version_uri
29
- map "Data", to: :data
36
+ map "DataType", to: :type, delegate: :data
37
+ map "DataLanguage", to: :lang, delegate: :data
38
+ end
39
+
40
+ def use_from_json(model, value)
41
+ model.use = value == "true" ? "required" : "optional"
42
+ end
43
+
44
+ def use_to_json(model, doc)
45
+ doc["Required"] = "true" if model.use == "required"
46
+ end
47
+
48
+ def long_name_from_json(model, value)
49
+ model.long_name = LongName.of_json(Utils.array_wrap(value))
50
+ end
51
+
52
+ def long_name_to_json(model, doc)
53
+ return if model.long_name.empty?
54
+
55
+ doc["LongName"] = LongName.as_json(Utils.one_or_all(model.long_name))
30
56
  end
31
57
 
32
58
  xml do
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
6
  require_relative "data_restrictions"
7
7
 
8
8
  module Genericode
9
- class ColumnRef < Shale::Mapper
10
- attribute :id, Shale::Type::String
11
- attribute :external_ref, Shale::Type::String
12
- attribute :use, Shale::Type::String
9
+ class ColumnRef < Lutaml::Model::Serializable
10
+ attribute :id, :string
11
+ attribute :external_ref, :string
12
+ attribute :use, :string
13
13
  attribute :annotation, Annotation
14
- attribute :canonical_version_uri, Shale::Type::String
15
- attribute :location_uri, Shale::Type::String, collection: true
14
+ attribute :canonical_version_uri, :string
15
+ attribute :location_uri, :string, collection: true
16
16
  attribute :data, DataRestrictions
17
17
 
18
18
  json do
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "shale"
3
+ require "lutaml/model"
4
4
 
5
5
  require_relative "annotation"
6
6
  require_relative "column"
@@ -10,8 +10,8 @@ require_relative "key"
10
10
  require_relative "key_ref"
11
11
 
12
12
  module Genericode
13
- class ColumnSet < Shale::Mapper
14
- attribute :datatype_library, Shale::Type::String
13
+ class ColumnSet < Lutaml::Model::Serializable
14
+ attribute :datatype_library, :string
15
15
  attribute :annotation, Annotation
16
16
  attribute :identification, Identification
17
17
  attribute :column, Column, collection: true