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
@@ -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