Class: Genericode::CodeList

Inherits:
Lutaml::Model::Serializable
  • Object
show all
Defined in:
lib/genericode/code_list.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_file(file_path) ⇒ Object



21
22
23
24
25
26
27
28
29
30
# File 'lib/genericode/code_list.rb', line 21

def self.from_file(file_path)
  content = File.read(file_path)
  if file_path.end_with?('.gc')
    from_xml(content)
  elsif file_path.end_with?('.gcj')
    from_json(content)
  else
    raise Error, 'Unsupported file format. Expected .gc or .gcj file.'
  end
end

Instance Method Details

#column_set_from_json(model, value) ⇒ Object



41
42
43
# File 'lib/genericode/code_list.rb', line 41

def column_set_from_json(model, value)
  model.column_set = ColumnSet.of_json({ 'Column' => value })
end

#column_set_to_json(model, doc) ⇒ Object



45
46
47
# File 'lib/genericode/code_list.rb', line 45

def column_set_to_json(model, doc)
  doc['Columns'] = Column.as_json(model.column_set.column)
end

#key_from_json(model, value) ⇒ Object



49
50
51
# File 'lib/genericode/code_list.rb', line 49

def key_from_json(model, value)
  model.column_set.key = Key.of_json(value)
end

#key_to_json(model, doc) ⇒ Object



53
54
55
# File 'lib/genericode/code_list.rb', line 53

def key_to_json(model, doc)
  doc['Keys'] = Key.as_json(model.column_set.key)
end

#lookup(path) ⇒ Object

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/genericode/code_list.rb', line 86

def lookup(path)
  parts = path.split('>')
  conditions = parts[0].split(',').to_h { |c| c.split(':') }
  target_column = parts[1]

  result = simple_code_list.row.find do |row|
    conditions.all? do |col, value|
      column = column_set.column.find { |c| c.short_name.content.downcase == col.downcase }
      raise Error, "Column not found: #{col}" unless column

      row_value = row.value.find { |v| v.column_ref == column.id }&.simple_value&.content
      row_value == value
    end
  end

  raise Error, "No matching row found for path: #{path}" unless result

  if target_column
    column = column_set.column.find { |c| c.short_name.content.downcase == target_column.downcase }
    raise Error, "Target column not found: #{target_column}" unless column

    result.value.find { |v| v.column_ref == column.id }&.simple_value&.content
  else
    result.value.to_h do |v|
      [column_set.column.find do |c|
        c.id == v.column_ref
      end.short_name.content, v.simple_value.content]
    end
  end
end

#simple_code_list_from_json(model, value) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/genericode/code_list.rb', line 57

def simple_code_list_from_json(model, value)
  rows = value.map do |x|
    values = x.map do |k, v|
      Value.new(column_ref: k, simple_value: SimpleValue.new(content: v))
    end

    Row.new(value: values)
  end

  model.simple_code_list = SimpleCodeList.new(row: rows)
end

#simple_code_list_to_json(model, doc) ⇒ Object



69
70
71
72
73
# File 'lib/genericode/code_list.rb', line 69

def simple_code_list_to_json(model, doc)
  doc['Codes'] = model.simple_code_list.row.map do |row|
    row.value.to_h { |v| [v.column_ref, v.simple_value.content] }
  end
end

#valid?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/genericode/code_list.rb', line 117

def valid?
  validate_verbose.empty?
end

#validate_verboseObject



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/genericode/code_list.rb', line 121

def validate_verbose
  errors = []

  # Rule 1: ColumnSet presence
  if column_set.nil? || column_set.column.empty?
    errors << { code: 'MISSING_COLUMN_SET',
                message: 'ColumnSet is missing or empty' }
  end

  # Rule 2: SimpleCodeList presence
  if simple_code_list.nil? || simple_code_list.row.empty?
    errors << { code: 'MISSING_SIMPLE_CODE_LIST',
                message: 'SimpleCodeList is missing or empty' }
  end

  # Rule 3: Unique column IDs
  column_ids = column_set&.column&.map(&:id) || []
  if column_ids.uniq.length != column_ids.length
    errors << { code: 'DUPLICATE_COLUMN_IDS', message: 'Duplicate column IDs found' }
  end

  # Rule 4: Verify ColumnRef values
  simple_code_list&.row&.each_with_index do |row, index|
    row.value.each do |value|
      unless column_ids.include?(value.column_ref)
        errors << { code: 'INVALID_COLUMN_REF',
                    message: "Invalid ColumnRef '#{value.column_ref}' in row #{index + 1}" }
      end
    end
  end

  # Rule 5: Unique values in columns
  column_set&.column&.each do |col|
    column_values = (simple_code_list&.row&.map do |row|
      row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
    end || []).compact

    if column_values.uniq.length != column_values.length
      errors << { code: 'DUPLICATE_VALUES', message: "Duplicate values found in column '#{col.id}'" }
    end
  end

  # Rule 6: Required column values
  required_columns = column_set&.column&.select { |col| col.use == 'required' } || []
  simple_code_list&.row&.each_with_index do |row, index|
    required_columns.each do |col|
      unless row.value.any? { |v| v.column_ref == col.id && v.simple_value&.content }
        errors << { code: 'MISSING_REQUIRED_VALUE',
                    message: "Missing value for required column '#{col.short_name&.content}' in row #{index + 1}" }
      end
    end
  end

  # Rule 7: Data type consistency
  column_set&.column&.each do |col|
    data_type = col.data&.type
    simple_code_list&.row&.each_with_index do |row, index|
      value = row.value.find { |v| v.column_ref == col.id }&.simple_value&.content
      unless value_matches_type?(value, data_type)
        errors << { code: 'INVALID_DATA_TYPE',
                    message: "Invalid data type for column '#{col.short_name&.content}' in row #{index + 1}" }
      end
    end
  end

  # Rule 8: Valid canonical URIs
  if identification&.canonical_uri && !valid_uri?(identification.canonical_uri)
    errors << { code: 'INVALID_CANONICAL_URI', message: 'Invalid canonical URI' }
  end

  # Rule 19: Datatype ID validation
  column_set&.column&.each do |col|
    if col.data&.type && !valid_datatype_id?(col.data.type)
      errors << { code: 'INVALID_DATATYPE_ID',
                  message: "Invalid datatype ID for column '#{col.short_name&.content}'" }
    end

    # Rule 20 and 22: Complex data validation
    if col.data&.type == '*' && col.data&.datatype_library != '*'
      errors << { code: 'INVALID_COMPLEX_DATA',
                  message: "Invalid complex data configuration for column '#{col.short_name&.content}'" }
    end

    # Rule 23: Language attribute validation
    if col.data&.lang && col.data_restrictions&.lang
      errors << { code: 'DUPLICATE_LANG_ATTRIBUTE',
                  message: "Duplicate lang attribute for column '#{col.short_name&.content}'" }
    end
  end

  # Rule 38: Implicit column reference
  simple_code_list&.row&.each_with_index do |row, index|
    unless row.value.all?(&:column_ref)
      errors << { code: 'MISSING_COLUMN_REF', message: "Missing explicit column reference in row #{index + 1}" }
    end
  end

  # Rule 39: ShortName whitespace check
  column_set&.column&.each do |col|
    if col.short_name&.content&.match?(/\s/)
      errors << { code: 'INVALID_SHORT_NAME',
                  message: "ShortName '#{col.short_name&.content}' contains whitespace" }
    end
  end

  # Rule 42 and 43: ComplexValue validation
  simple_code_list&.row&.each_with_index do |row, index|
    row.value.each do |value|
      next unless value.complex_value

      unless valid_complex_value?(value.complex_value, column_set&.column&.find { |c| c.id == value.column_ref })
        errors << { code: 'INVALID_COMPLEX_VALUE',
                    message: "Invalid ComplexValue in row #{index + 1}, column '#{value.column_ref}'" }
      end
    end
  end

  errors
end