diff --git a/.rubocop.yml b/.rubocop.yml index bb8409c..666c599 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,34 +1,14 @@ -inherit_from: .rubocop_todo.yml +inherit_from: + - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml + - .rubocop_todo.yml + +plugins: +- rubocop-performance +- rubocop-rake +- rubocop-rspec AllCops: + TargetRubyVersion: 3.0 NewCops: enable - SuggestExtensions: false - TargetRubyVersion: 2.7 - -Gemspec/DevelopmentDependencies: - Enabled: false - -Gemspec/RequireMFA: - Enabled: false - -Metrics/BlockLength: - AllowedMethods: - - describe - -Style/Documentation: - Enabled: false - -Style/StringLiterals: - EnforcedStyle: double_quotes - -Style/StringLiteralsInInterpolation: - EnforcedStyle: double_quotes - -Style/TrailingCommaInArguments: - EnforcedStyleForMultiline: consistent_comma - -Style/TrailingCommaInArrayLiteral: - EnforcedStyleForMultiline: consistent_comma - -Style/TrailingCommaInHashLiteral: - EnforcedStyleForMultiline: consistent_comma + Exclude: + - 'vendor/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 321f8d6..5e4bf1d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,66 +1,321 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-01-11 09:46:54 UTC using RuboCop version 1.70.0. +# on 2025-12-01 12:15:10 UTC using RuboCop version 1.81.7. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 4 +# Offense count: 2 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. -# Include: **/*.gemfile, **/Gemfile, **/gems.rb +# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation. Bundler/OrderedGems: Exclude: - 'Gemfile' # Offense count: 1 -# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. -Lint/DuplicateBranch: +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Severity. +Gemspec/RequireMFA: + Exclude: + - 'genericode.gemspec' + +# Offense count: 1 +# Configuration parameters: Severity. +Gemspec/RequiredRubyVersion: + Exclude: + - 'genericode.gemspec' + +# Offense count: 36 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/ArgumentAlignment: + Exclude: + - 'lib/genericode/cli/code_lister.rb' + - 'lib/genericode/cli/commands.rb' + - 'lib/genericode/cli/converter.rb' + - 'lib/genericode/cli/validator.rb' + - 'lib/genericode/identification.rb' + - 'spec/genericode/cli/code_lookup_spec.rb' + - 'spec/genericode/cli/commands_spec.rb' + - 'spec/genericode/cli/converter_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + - 'spec/genericode/code_list_spec.rb' + +# Offense count: 3 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_element, with_fixed_indentation +Layout/ArrayAlignment: + Exclude: + - 'genericode.gemspec' + - 'lib/genericode/cli/converter.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: IndentationWidth. +Layout/AssignmentIndentation: + Exclude: + - 'lib/genericode/agency.rb' + - 'lib/genericode/identification.rb' + +# Offense count: 18 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleAlignWith. +# SupportedStylesAlignWith: either, start_of_block, start_of_line +Layout/BlockAlignment: Exclude: - 'lib/genericode/code_list.rb' + - 'lib/genericode/key.rb' + - 'spec/genericode/cli/commands_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + - 'spec/genericode/code_list_spec.rb' + - 'spec/genericode_spec.rb' + +# Offense count: 18 +# This cop supports safe autocorrection (--autocorrect). +Layout/BlockEndNewline: + Exclude: + - 'lib/genericode/code_list.rb' + - 'lib/genericode/key.rb' + - 'spec/genericode/cli/commands_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + - 'spec/genericode/code_list_spec.rb' + - 'spec/genericode_spec.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +Layout/ClosingParenthesisIndentation: + Exclude: + - 'spec/genericode/cli/commands_spec.rb' # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects. -Lint/Void: +Layout/EmptyLineAfterGuardClause: + Exclude: + - 'lib/genericode/cli/converter.rb' + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/FirstArgumentIndentation: + Exclude: + - 'spec/genericode/cli/commands_spec.rb' + +# Offense count: 42 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/HashAlignment: Exclude: + - 'genericode.gemspec' + - 'lib/genericode/agency.rb' + - 'lib/genericode/cli/commands.rb' - 'lib/genericode/code_list.rb' + - 'lib/genericode/code_list_ref.rb' + - 'lib/genericode/code_list_set.rb' + - 'lib/genericode/code_list_set_ref.rb' + - 'lib/genericode/column.rb' + - 'lib/genericode/datatype_facet.rb' + - 'lib/genericode/identification.rb' + - 'lib/genericode/key.rb' + - 'spec/genericode/code_list_spec.rb' -# Offense count: 8 -# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. -Metrics/AbcSize: - Max: 182 +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: normal, indented_internal_methods +Layout/IndentationConsistency: + Exclude: + - 'genericode.gemspec' -# Offense count: 2 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. -# AllowedMethods: refine -Metrics/BlockLength: - Max: 34 +# Offense count: 36 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Width, AllowedPatterns. +Layout/IndentationWidth: + Exclude: + - 'lib/genericode/code_list.rb' + - 'lib/genericode/key.rb' + - 'spec/genericode/cli/commands_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + - 'spec/genericode/code_list_spec.rb' + - 'spec/genericode_spec.rb' + +# Offense count: 153 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Enabled: false + +# Offense count: 4 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Exclude: + - 'spec/genericode/cli/commands_spec.rb' + +# Offense count: 60 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Enabled: false # Offense count: 1 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ClassLength: - Max: 216 +# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. +Lint/DuplicateBranch: + Exclude: + - 'lib/genericode/code_list.rb' + +# Offense count: 11 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. +Metrics/AbcSize: + Exclude: + - 'lib/genericode/cli/code_lister.rb' + - 'lib/genericode/cli/commands.rb' + - 'lib/genericode/cli/validator.rb' + - 'lib/genericode/code_list.rb' + - 'lib/genericode/code_list_set.rb' + - 'spec/genericode_spec.rb' -# Offense count: 5 -# Configuration parameters: AllowedMethods, AllowedPatterns. +# Offense count: 7 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/CyclomaticComplexity: - Max: 92 + Exclude: + - 'lib/genericode/cli/code_lister.rb' + - 'lib/genericode/cli/validator.rb' + - 'lib/genericode/code_list.rb' + - 'lib/genericode/code_list_set.rb' # Offense count: 10 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: - Max: 86 + Max: 112 -# Offense count: 3 -# Configuration parameters: AllowedMethods, AllowedPatterns. +# Offense count: 8 +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/PerceivedComplexity: - Max: 92 + Exclude: + - 'lib/genericode/cli/code_lister.rb' + - 'lib/genericode/cli/commands.rb' + - 'lib/genericode/cli/validator.rb' + - 'lib/genericode/code_list.rb' + - 'lib/genericode/code_list_set.rb' + +# Offense count: 2 +# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates. +# AllowedMethods: call +# WaywardPredicates: nonzero? +Naming/PredicateMethod: + Exclude: + - 'lib/genericode/cli/converter.rb' + - 'lib/genericode/cli/validator.rb' + +# Offense count: 2 +RSpec/AnyInstance: + Exclude: + - 'spec/genericode/cli/converter_spec.rb' + +# Offense count: 17 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Exclude: + - 'spec/genericode/cli/code_lister_spec.rb' + - 'spec/genericode/cli/code_lookup_spec.rb' + - 'spec/genericode/cli/converter_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + +# Offense count: 17 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 23 + +# Offense count: 2 +RSpec/LeakyLocalVariable: + Exclude: + - 'spec/genericode/code_list_spec.rb' + +# Offense count: 1 +RSpec/MultipleExpectations: + Max: 3 # Offense count: 2 +# Configuration parameters: AllowedPatterns. +# AllowedPatterns: ^expect_, ^assert_ +RSpec/NoExpectationExample: + Exclude: + - 'spec/genericode_spec.rb' + +# Offense count: 6 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/ReceiveMessages: + Exclude: + - 'spec/genericode/cli/validator_spec.rb' + +# Offense count: 30 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. -# URISchemes: http, https -Layout/LineLength: - Max: 121 +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. +# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces +# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object +# FunctionalMethods: let, let!, subject, watch +# AllowedMethods: lambda, proc, it +Style/BlockDelimiters: + Exclude: + - 'lib/genericode/code_list.rb' + - 'lib/genericode/key.rb' + - 'spec/genericode/cli/commands_spec.rb' + - 'spec/genericode/cli/validator_spec.rb' + - 'spec/genericode/code_list_spec.rb' + - 'spec/genericode_spec.rb' + +# Offense count: 7 +# This cop supports safe autocorrection (--autocorrect). +Style/MultilineIfModifier: + Exclude: + - 'lib/genericode/cli/code_lister.rb' + - 'lib/genericode/cli/converter.rb' + - 'lib/genericode/cli/validator.rb' + - 'lib/genericode/code_list_set.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'genericode.gemspec' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInArguments: + Exclude: + - 'spec/genericode/cli/code_lister_spec.rb' + - 'spec/genericode/cli/code_lookup_spec.rb' + - 'spec/genericode/cli/converter_spec.rb' + - 'spec/genericode/code_list_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInArrayLiteral: + Exclude: + - 'lib/genericode/code_list.rb' + +# Offense count: 11 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma +Style/TrailingCommaInHashLiteral: + Exclude: + - 'lib/genericode/code_list.rb' + - 'lib/genericode/identification.rb' diff --git a/Gemfile b/Gemfile index 93dfa2a..62b56d6 100644 --- a/Gemfile +++ b/Gemfile @@ -5,9 +5,13 @@ source "https://rubygems.org" # Specify your gem's dependencies in genericode.gemspec gemspec +gem "canon" +gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main" gem "nokogiri" -gem "rake", "~> 13.0" -gem "rspec", "~> 3.0" +gem "openssl" +gem "rake" +gem "rspec" gem "rubocop" gem "rubocop-performance" -gem "xml-c14n" +gem "rubocop-rake" +gem "rubocop-rspec" diff --git a/genericode.gemspec b/genericode.gemspec index db15151..74af68c 100644 --- a/genericode.gemspec +++ b/genericode.gemspec @@ -16,12 +16,15 @@ Gem::Specification.new do |spec| spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage spec.metadata["changelog_uri"] = "https://github.com/lutaml/genericode/releases" + spec.metadata["rubygems_mfa_required"] = "true" gemspec = File.basename(__FILE__) - spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, + err: IO::NULL) do |ls| ls.readlines("\x0", chomp: true).reject do |f| (f == gemspec) || - f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile]) + f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor + Gemfile]) end end spec.bindir = "exe" @@ -29,7 +32,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "csv" - spec.add_dependency "lutaml-model", "~> 0.7" - spec.add_dependency "tabulo" + spec.add_dependency "lutaml-model", "~>0.8.0" + spec.add_dependency "table_tennis", "~>0.0.7" spec.add_dependency "thor" end diff --git a/lib/genericode.rb b/lib/genericode.rb index ec96a81..ee93eb2 100644 --- a/lib/genericode.rb +++ b/lib/genericode.rb @@ -3,11 +3,12 @@ require "lutaml/model" Lutaml::Model::Config.configure do |config| - require "lutaml/model/xml_adapter/nokogiri_adapter" - config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter + require "lutaml/model/xml/nokogiri_adapter" + config.xml_adapter = Lutaml::Model::Xml::NokogiriAdapter end require_relative "genericode/version" +require_relative "genericode/namespace" require_relative "genericode/code_list" module Genericode diff --git a/lib/genericode/agency.rb b/lib/genericode/agency.rb index f051d9c..c7aa3af 100644 --- a/lib/genericode/agency.rb +++ b/lib/genericode/agency.rb @@ -17,9 +17,12 @@ class Agency < Lutaml::Model::Serializable attribute :identifier, GeneralIdentifier, collection: true json do - map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json } - map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json } - map "Identifier", to: :identifier, with: { from: :identifier_from_json, to: :identifier_to_json } + map "ShortName", to: :short_name, + with: { from: :short_name_from_json, to: :short_name_to_json } + map "LongName", to: :long_name, + with: { from: :long_name_from_json, to: :long_name_to_json } + map "Identifier", to: :identifier, + with: { from: :identifier_from_json, to: :identifier_to_json } end def long_name_from_json(model, value) @@ -35,16 +38,17 @@ def identifier_from_json(model, value) end def identifier_to_json(model, doc) - doc["Identifier"] = GeneralIdentifier.as_json(Utils.one_or_all(model.identifier)) + doc["Identifier"] = + GeneralIdentifier.as_json(Utils.one_or_all(model.identifier)) end xml do - root "Agency" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Agency" + namespace Namespace - map_element "ShortName", to: :short_name, prefix: nil, namespace: nil - map_element "LongName", to: :long_name, prefix: nil, namespace: nil - map_element "Identifier", to: :identifier, prefix: nil, namespace: nil + map_element "ShortName", to: :short_name + map_element "LongName", to: :long_name + map_element "Identifier", to: :identifier end end end diff --git a/lib/genericode/annotation.rb b/lib/genericode/annotation.rb index 754226d..21756b5 100644 --- a/lib/genericode/annotation.rb +++ b/lib/genericode/annotation.rb @@ -22,11 +22,11 @@ def self.of_json(hash, **) end xml do - root "Annotation" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Annotation" + namespace Namespace - map_element "Description", to: :description, prefix: nil, namespace: nil - map_element "AppInfo", to: :app_info, prefix: nil, namespace: nil, value_map: { to: { nil: :empty } } + map_element "Description", to: :description + map_element "AppInfo", to: :app_info, value_map: { to: { nil: :empty } } end end end diff --git a/lib/genericode/annotation.rb.orig b/lib/genericode/annotation.rb.orig new file mode 100644 index 0000000..eebb4b4 --- /dev/null +++ b/lib/genericode/annotation.rb.orig @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "lutaml/model" + +require_relative "any_other_content" +require_relative "any_other_language_content" + +module Genericode + class Annotation < Lutaml::Model::Serializable + attribute :description, AnyOtherLanguageContent, collection: true + attribute :app_info, AnyOtherContent + + json do + map "Description", to: :description + map "AppInfo", to: :app_info, render_nil: true + end + + def self.of_json(hash, **) + hash = { "AppInfo" => hash } if hash.any? + + super + end + + xml do + element "Annotation" + namespace Namespace + +<<<<<<< Updated upstream + map_element "Description", to: :description, prefix: nil, namespace: nil + map_element "AppInfo", to: :app_info, prefix: nil, namespace: nil, value_map: { to: { nil: :empty } } +======= + map_element "Description", to: :description + map_element "AppInfo", to: :app_info, render_nil: true +>>>>>>> Stashed changes + end + end +end diff --git a/lib/genericode/any_other_content.rb b/lib/genericode/any_other_content.rb index 2e1630d..bfc7e27 100644 --- a/lib/genericode/any_other_content.rb +++ b/lib/genericode/any_other_content.rb @@ -5,8 +5,8 @@ module Genericode class AnyOtherContent < Lutaml::Model::Serializable xml do - root "AnyOtherContent" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "AnyOtherContent" + namespace Namespace end end end diff --git a/lib/genericode/any_other_language_content.rb b/lib/genericode/any_other_language_content.rb index 5971f66..e20ed3e 100644 --- a/lib/genericode/any_other_language_content.rb +++ b/lib/genericode/any_other_language_content.rb @@ -11,8 +11,8 @@ class AnyOtherLanguageContent < Lutaml::Model::Serializable end xml do - root "AnyOtherLanguageContent" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "AnyOtherLanguageContent" + namespace Namespace map_attribute "lang", to: :lang, prefix: "xml", namespace: "http://www.w3.org/XML/1998/namespace" end diff --git a/lib/genericode/canonical_uri.rb b/lib/genericode/canonical_uri.rb index 8e393fd..001eec9 100644 --- a/lib/genericode/canonical_uri.rb +++ b/lib/genericode/canonical_uri.rb @@ -9,7 +9,7 @@ class CanonicalUri < Lutaml::Model::Serializable attribute :content, :string xml do - root "CanonicalUri" + element "CanonicalUri" map_content to: :content end diff --git a/lib/genericode/cli/code_lister.rb b/lib/genericode/cli/code_lister.rb index 5769731..7c79a55 100644 --- a/lib/genericode/cli/code_lister.rb +++ b/lib/genericode/cli/code_lister.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require_relative "../code_list" -require "tabulo" +require "table_tennis" require "csv" module Genericode @@ -13,10 +13,16 @@ def list_codes(file_path, format: :tsv) # Validate data types code_list.validate_verbose.each do |error| - raise Error, "#{error[:code]}: #{error[:message]}" if error[:code] == "INVALID_DATA_TYPE" + if error[:code] == "INVALID_DATA_TYPE" + raise Error, + "#{error[:code]}: #{error[:message]}" + end # Ensure valid ColumnRefs - raise Error, "#{error[:code]}: #{error[:message]}" if error[:code] == "INVALID_COLUMN_REF" + if error[:code] == "INVALID_COLUMN_REF" + raise Error, + "#{error[:code]}: #{error[:message]}" + end end case format @@ -50,16 +56,15 @@ def list_table(code_list) columns = code_list.column_set.column rows = code_list.simple_code_list.row - table = Tabulo::Table.new(rows) do |t| - columns.each do |column| - t.add_column(column.short_name.content) do |row| - value = row.value.find { |v| v.column_ref == column.id } - value&.simple_value&.content || "" - end + headers = columns.map { |col| col.short_name.content } + data = rows.map do |row| + columns.map do |col| + value = row.value.find { |v| v.column_ref == col.id } + value&.simple_value&.content || "" end end - table.to_s + TableTennis.render([headers] + data) end end end diff --git a/lib/genericode/cli/commands.rb b/lib/genericode/cli/commands.rb index 269b31b..ff266dd 100644 --- a/lib/genericode/cli/commands.rb +++ b/lib/genericode/cli/commands.rb @@ -9,7 +9,8 @@ module Genericode module Cli class Commands < Thor - desc "convert INPUT OUTPUT", "Convert between Genericode XML and JSON formats" + desc "convert INPUT OUTPUT", + "Convert between Genericode XML and JSON formats" def convert(input, output) puts "Conversion successful." if Converter.convert(input, output) @@ -41,8 +42,10 @@ def validate(file) puts "Validation failed: #{e.message}" end - desc "list_codes FILE", "List all codes and their associated data in a Genericode file" - option :format, type: :string, default: "tsv", enum: %w[tsv table], desc: "Output format (tsv or table)" + desc "list_codes FILE", + "List all codes and their associated data in a Genericode file" + option :format, type: :string, default: "tsv", enum: %w[tsv table], + desc: "Output format (tsv or table)" option :output, type: :string, desc: "Output file path (default: stdout)" def list_codes(file) diff --git a/lib/genericode/cli/converter.rb b/lib/genericode/cli/converter.rb index 33d315e..6f0c7f3 100644 --- a/lib/genericode/cli/converter.rb +++ b/lib/genericode/cli/converter.rb @@ -7,9 +7,15 @@ def self.convert(input_path, output_path) input_format = File.extname(input_path) output_format = File.extname(output_path) - raise Error, "Invalid input format" unless [".gc", ".gcj"].include?(input_format) - raise Error, "Invalid output format" unless [".gc", ".gcj"].include?(output_format) - raise Error, "Input and output formats are the same" if input_format == output_format + raise Error, "Invalid input format" unless [".gc", + ".gcj"].include?(input_format) + raise Error, "Invalid output format" unless [".gc", + ".gcj"].include?(output_format) + + if input_format == output_format + raise Error, + "Input and output formats are the same" + end # begin code_list = CodeList.from_file(input_path) diff --git a/lib/genericode/cli/validator.rb b/lib/genericode/cli/validator.rb index fe731d2..6d78fdc 100644 --- a/lib/genericode/cli/validator.rb +++ b/lib/genericode/cli/validator.rb @@ -5,12 +5,19 @@ module Cli class Validator def self.validate(file_path) raise Error, "File does not exist" unless File.exist?(file_path) - raise Error, "Invalid file format" unless file_path.end_with?(".gc", ".gcj") + raise Error, "Invalid file format" unless file_path.end_with?(".gc", + ".gcj") code_list = CodeList.from_file(file_path) - raise Error, "No columns defined" if code_list.column_set.nil? || code_list.column_set.column.empty? - raise Error, "No rows defined" if code_list.simple_code_list.nil? || code_list.simple_code_list.row.empty? + if code_list.column_set.nil? || code_list.column_set.column.empty? + raise Error, + "No columns defined" + end + if code_list.simple_code_list.nil? || code_list.simple_code_list.row.empty? + raise Error, + "No rows defined" + end raise Error, "Invalid Genericode structure" unless code_list.valid? diff --git a/lib/genericode/code_list.rb b/lib/genericode/code_list.rb index da22c2f..3e8ec7b 100644 --- a/lib/genericode/code_list.rb +++ b/lib/genericode/code_list.rb @@ -31,10 +31,13 @@ def self.from_file(file_path) json do map "Annotation", to: :annotation map "Identification", to: :identification - map "Columns", to: :column_set, with: { from: :column_set_from_json, to: :column_set_to_json } + map "Columns", to: :column_set, + with: { from: :column_set_from_json, to: :column_set_to_json } map "ColumnSetRef", to: :column_set_ref - map "Keys", to: :key, delegate: :column_set, with: { from: :key_from_json, to: :key_to_json } - map "Codes", to: :simple_code_list, with: { from: :simple_code_list_from_json, to: :simple_code_list_to_json } + map "Keys", to: :key, delegate: :column_set, + with: { from: :key_from_json, to: :key_to_json } + map "Codes", to: :simple_code_list, + with: { from: :simple_code_list_from_json, to: :simple_code_list_to_json } end def column_set_from_json(model, value) @@ -72,14 +75,14 @@ def simple_code_list_to_json(model, doc) end xml do - root "CodeList" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" - - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Identification", to: :identification, prefix: nil, namespace: nil - map_element "ColumnSet", to: :column_set, prefix: nil, namespace: nil - map_element "ColumnSetRef", to: :column_set_ref, prefix: nil, namespace: nil - map_element "SimpleCodeList", to: :simple_code_list, prefix: nil, namespace: nil + element "CodeList" + namespace Namespace + + map_element "Annotation", to: :annotation + map_element "Identification", to: :identification + map_element "ColumnSet", to: :column_set + map_element "ColumnSetRef", to: :column_set_ref + map_element "SimpleCodeList", to: :simple_code_list end def lookup(path) @@ -89,10 +92,14 @@ def lookup(path) 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 } + column = column_set.column.find do |c| + c.short_name.content.downcase == col.downcase + end raise Error, "Column not found: #{col}" unless column - row_value = row.value.find { |v| v.column_ref == column.id }&.simple_value&.content + row_value = row.value.find do |v| + v.column_ref == column.id + end&.simple_value&.content row_value == value end end @@ -100,15 +107,19 @@ def lookup(path) 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 } + column = column_set.column.find do |c| + c.short_name.content.downcase == target_column.downcase + end raise Error, "Target column not found: #{target_column}" unless column - result.value.find { |v| v.column_ref == column.id }&.simple_value&.content + result.value.find do |v| + v.column_ref == column.id + end&.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.short_name.content, v.simple_value.content] end end end @@ -123,19 +134,20 @@ def validate_verbose # Rule 1: ColumnSet presence if column_set.nil? || column_set.column.empty? errors << { code: "MISSING_COLUMN_SET", - message: "ColumnSet is missing or empty", } + 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", } + 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" } + errors << { code: "DUPLICATE_COLUMN_IDS", + message: "Duplicate column IDs found" } end # Rule 4: Verify ColumnRef values @@ -143,7 +155,7 @@ def validate_verbose 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}", } + message: "Invalid ColumnRef '#{value.column_ref}' in row #{index + 1}" } end end end @@ -155,17 +167,22 @@ def validate_verbose end || []).compact if column_values.uniq.length != column_values.length - errors << { code: "DUPLICATE_VALUES", message: "Duplicate values found in column '#{col.id}'" } + 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" } || [] + required_columns = column_set&.column&.select do |col| + col.use == "required" + end || [] 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 } + unless row.value.any? do |v| + v.column_ref == col.id && v.simple_value&.content + end errors << { code: "MISSING_REQUIRED_VALUE", - message: "Missing value for required column '#{col.short_name&.content}' in row #{index + 1}", } + message: "Missing value for required column '#{col.short_name&.content}' in row #{index + 1}" } end end end @@ -174,43 +191,47 @@ def validate_verbose 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 + value = row.value.find do |v| + v.column_ref == col.id + end&.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}", } + 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" } + 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}'", } + 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}'", } + 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}'", } + 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}" } + errors << { code: "MISSING_COLUMN_REF", + message: "Missing explicit column reference in row #{index + 1}" } end end @@ -218,7 +239,7 @@ def validate_verbose 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", } + message: "ShortName '#{col.short_name&.content}' contains whitespace" } end end @@ -227,9 +248,11 @@ def validate_verbose 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 }) + unless valid_complex_value?(value.complex_value, column_set&.column&.find do |c| + c.id == value.column_ref + end) errors << { code: "INVALID_COMPLEX_VALUE", - message: "Invalid ComplexValue in row #{index + 1}, column '#{value.column_ref}'", } + message: "Invalid ComplexValue in row #{index + 1}, column '#{value.column_ref}'" } end end end diff --git a/lib/genericode/code_list_ref.rb b/lib/genericode/code_list_ref.rb index 631e122..d5fd6c2 100644 --- a/lib/genericode/code_list_ref.rb +++ b/lib/genericode/code_list_ref.rb @@ -16,19 +16,20 @@ class CodeListRef < Lutaml::Model::Serializable json do map "Annotation", to: :annotation - map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } + map "CanonicalUri", to: :canonical_uri, + with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } map "CanonicalVersionUri", to: :canonical_version_uri map "LocationUri", to: :location_uri end xml do - root "CodeListRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "CodeListRef" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "CanonicalUri", to: :canonical_uri, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "CanonicalUri", to: :canonical_uri + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri end end end diff --git a/lib/genericode/code_list_set.rb b/lib/genericode/code_list_set.rb index 3c5c912..dbb31b0 100644 --- a/lib/genericode/code_list_set.rb +++ b/lib/genericode/code_list_set.rb @@ -24,14 +24,14 @@ class CodeListSet < Lutaml::Model::Serializable end xml do - root "CodeListSet" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "CodeListSet" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Identification", to: :identification, prefix: nil, namespace: nil - map_element "CodeListRef", to: :code_list_ref, prefix: nil, namespace: nil - map_element "CodeListSet", to: :code_list_set, prefix: nil, namespace: nil - map_element "CodeListSetRef", to: :code_list_set_ref, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "Identification", to: :identification + map_element "CodeListRef", to: :code_list_ref + map_element "CodeListSet", to: :code_list_set + map_element "CodeListSetRef", to: :code_list_set_ref end def validate_verbose @@ -40,19 +40,24 @@ def validate_verbose # Rule 47: CodeListSet reference validation code_list_set_ref&.each do |ref| unless valid_uri?(ref.canonical_uri) && valid_uri?(ref.canonical_version_uri) - errors << { code: "INVALID_CODELIST_SET_REF", message: "Invalid CodeListSet reference URI" } + errors << { code: "INVALID_CODELIST_SET_REF", + message: "Invalid CodeListSet reference URI" } end end # Rule 48-51: URI validations [canonical_uri, canonical_version_uri].each do |uri| - errors << { code: "INVALID_URI", message: "Invalid URI: #{uri}" } unless valid_uri?(uri) + unless valid_uri?(uri) + errors << { code: "INVALID_URI", + message: "Invalid URI: #{uri}" } + end end # Rule 52-53: LocationUri validation location_uri&.each do |uri| unless valid_genericode_uri?(uri) - errors << { code: "INVALID_LOCATION_URI", message: "Invalid LocationUri: #{uri}" } + errors << { code: "INVALID_LOCATION_URI", + message: "Invalid LocationUri: #{uri}" } end end diff --git a/lib/genericode/code_list_set_ref.rb b/lib/genericode/code_list_set_ref.rb index a73ec41..76abe5b 100644 --- a/lib/genericode/code_list_set_ref.rb +++ b/lib/genericode/code_list_set_ref.rb @@ -9,6 +9,7 @@ module Genericode class CodeListSetRef < Lutaml::Model::Serializable include Json::CanonicalUriMixin + attribute :annotation, Annotation attribute :canonical_uri, CanonicalUri attribute :canonical_version_uri, :string @@ -16,19 +17,20 @@ class CodeListSetRef < Lutaml::Model::Serializable json do map "Annotation", to: :annotation - map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } + map "CanonicalUri", to: :canonical_uri, + with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } map "CanonicalVersionUri", to: :canonical_version_uri map "LocationUri", to: :location_uri end xml do - root "CodeListSetRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "CodeListSetRef" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "CanonicalUri", to: :canonical_uri, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "CanonicalUri", to: :canonical_uri + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri end end end diff --git a/lib/genericode/column.rb b/lib/genericode/column.rb index b09f014..239b62a 100644 --- a/lib/genericode/column.rb +++ b/lib/genericode/column.rb @@ -29,9 +29,12 @@ class Column < Lutaml::Model::Serializable map "Required", to: :use, with: { from: :use_from_json, to: :use_to_json } map "Id", to: :id map "Annotation", to: :annotation - map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json } - map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json } - map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } + map "ShortName", to: :short_name, + with: { from: :short_name_from_json, to: :short_name_to_json } + map "LongName", to: :long_name, + with: { from: :long_name_from_json, to: :long_name_to_json } + map "CanonicalUri", to: :canonical_uri, + with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } map "CanonicalVersionUri", to: :canonical_version_uri map "DataType", to: :type, delegate: :data map "DataLanguage", to: :lang, delegate: :data @@ -56,17 +59,17 @@ def long_name_to_json(model, doc) end xml do - root "Column" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Column" + namespace Namespace map_attribute "Id", to: :id map_attribute "Use", to: :use - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "ShortName", to: :short_name, prefix: nil, namespace: nil - map_element "LongName", to: :long_name, prefix: nil, namespace: nil - map_element "CanonicalUri", to: :canonical_uri, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "Data", to: :data, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "ShortName", to: :short_name + map_element "LongName", to: :long_name + map_element "CanonicalUri", to: :canonical_uri + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "Data", to: :data end end end diff --git a/lib/genericode/column_ref.rb b/lib/genericode/column_ref.rb index 63bc0ec..3b34dcc 100644 --- a/lib/genericode/column_ref.rb +++ b/lib/genericode/column_ref.rb @@ -26,16 +26,16 @@ class ColumnRef < Lutaml::Model::Serializable end xml do - root "ColumnRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "ColumnRef" + namespace Namespace map_attribute "Id", to: :id map_attribute "ExternalRef", to: :external_ref map_attribute "Use", to: :use - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil - map_element "Data", to: :data, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri + map_element "Data", to: :data end end end diff --git a/lib/genericode/column_set.rb b/lib/genericode/column_set.rb index be71f49..6dded1c 100644 --- a/lib/genericode/column_set.rb +++ b/lib/genericode/column_set.rb @@ -30,16 +30,16 @@ class ColumnSet < Lutaml::Model::Serializable end xml do - root "ColumnSet" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "ColumnSet" + namespace Namespace map_attribute "DatatypeLibrary", to: :datatype_library - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Identification", to: :identification, prefix: nil, namespace: nil - map_element "Column", to: :column, prefix: nil, namespace: nil - map_element "ColumnRef", to: :column_ref, prefix: nil, namespace: nil - map_element "Key", to: :key, prefix: nil, namespace: nil - map_element "KeyRef", to: :key_ref, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "Identification", to: :identification + map_element "Column", to: :column + map_element "ColumnRef", to: :column_ref + map_element "Key", to: :key + map_element "KeyRef", to: :key_ref end end end diff --git a/lib/genericode/column_set_ref.rb b/lib/genericode/column_set_ref.rb index 01054dd..0e28d03 100644 --- a/lib/genericode/column_set_ref.rb +++ b/lib/genericode/column_set_ref.rb @@ -17,12 +17,12 @@ class ColumnSetRef < Lutaml::Model::Serializable end xml do - root "ColumnSetRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "ColumnSetRef" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri end end end diff --git a/lib/genericode/data.rb b/lib/genericode/data.rb index 7ed4c69..7cb89d6 100644 --- a/lib/genericode/data.rb +++ b/lib/genericode/data.rb @@ -22,14 +22,14 @@ class Data < Lutaml::Model::Serializable end xml do - root "Data" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Data" + namespace Namespace map_attribute "Type", to: :type map_attribute "DatatypeLibrary", to: :datatype_library map_attribute "Lang", to: :lang - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Parameter", to: :parameter, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "Parameter", to: :parameter end end end diff --git a/lib/genericode/data_restrictions.rb b/lib/genericode/data_restrictions.rb index 2146b8f..49693c8 100644 --- a/lib/genericode/data_restrictions.rb +++ b/lib/genericode/data_restrictions.rb @@ -15,11 +15,11 @@ class DataRestrictions < Lutaml::Model::Serializable end xml do - root "DataRestrictions" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "DataRestrictions" + namespace Namespace map_attribute "Lang", to: :lang - map_element "Parameter", to: :parameter, prefix: nil, namespace: nil + map_element "Parameter", to: :parameter end end end diff --git a/lib/genericode/datatype_facet.rb b/lib/genericode/datatype_facet.rb index 12299ba..e2d60ad 100644 --- a/lib/genericode/datatype_facet.rb +++ b/lib/genericode/datatype_facet.rb @@ -12,14 +12,15 @@ class DatatypeFacet < Lutaml::Model::Serializable attribute :long_name, :string json do - map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json } + map "ShortName", to: :short_name, + with: { from: :short_name_from_json, to: :short_name_to_json } map "LongName", to: :long_name map "_", to: :content end xml do - root "DatatypeFacet" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "DatatypeFacet" + namespace Namespace map_content to: :content map_attribute "ShortName", to: :short_name diff --git a/lib/genericode/general_identifier.rb b/lib/genericode/general_identifier.rb index 0028e48..469a250 100644 --- a/lib/genericode/general_identifier.rb +++ b/lib/genericode/general_identifier.rb @@ -15,8 +15,8 @@ class GeneralIdentifier < Lutaml::Model::Serializable end xml do - root "GeneralIdentifier" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "GeneralIdentifier" + namespace Namespace map_content to: :content map_attribute "Identifier", to: :identifier diff --git a/lib/genericode/identification.rb b/lib/genericode/identification.rb index aac32fe..c7531b9 100644 --- a/lib/genericode/identification.rb +++ b/lib/genericode/identification.rb @@ -22,19 +22,24 @@ class Identification < Lutaml::Model::Serializable attribute :canonical_uri, CanonicalUri attribute :canonical_version_uri, :string attribute :location_uri, :string, collection: true, initialize_empty: true - attribute :alternate_format_location_uri, MimeTypedUri, collection: true, initialize_empty: true + attribute :alternate_format_location_uri, MimeTypedUri, collection: true, + initialize_empty: true attribute :agency, Agency json do - map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json } - map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json } + map "ShortName", to: :short_name, + with: { from: :short_name_from_json, to: :short_name_to_json } + map "LongName", to: :long_name, + with: { from: :long_name_from_json, to: :long_name_to_json } map "Version", to: :version - map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } + map "CanonicalUri", to: :canonical_uri, + with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } map "CanonicalVersionUri", to: :canonical_version_uri - map "LocationUri", to: :location_uri, with: { from: :location_uri_from_json, to: :location_uri_to_json } + map "LocationUri", to: :location_uri, + with: { from: :location_uri_from_json, to: :location_uri_to_json } map "AlternateFormatLocationUri", to: :alternate_format_location_uri, with: { from: :alternate_format_location_uri_from_json, - to: :alternate_format_location_uri_to_json, } + to: :alternate_format_location_uri_to_json } map "Agency", to: :agency end @@ -67,21 +72,23 @@ def alternate_format_location_uri_from_json(model, value) def alternate_format_location_uri_to_json(model, doc) return if model.alternate_format_location_uri.nil? || model.alternate_format_location_uri.empty? - doc["AlternateFormatLocationUri"] = MimeTypedUri.as_json(Utils.one_or_all(model.alternate_format_location_uri)) + doc["AlternateFormatLocationUri"] = + MimeTypedUri.as_json(Utils.one_or_all(model.alternate_format_location_uri)) end xml do - root "Identification" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" - - map_element "ShortName", to: :short_name, prefix: nil, namespace: nil - map_element "LongName", to: :long_name, prefix: nil, namespace: nil - map_element "Version", to: :version, prefix: nil, namespace: nil - map_element "CanonicalUri", to: :canonical_uri, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil - map_element "AlternateFormatLocationUri", to: :alternate_format_location_uri, prefix: nil, namespace: nil - map_element "Agency", to: :agency, prefix: nil, namespace: nil + element "Identification" + namespace Namespace + + map_element "ShortName", to: :short_name + map_element "LongName", to: :long_name + map_element "Version", to: :version + map_element "CanonicalUri", to: :canonical_uri + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri + map_element "AlternateFormatLocationUri", + to: :alternate_format_location_uri + map_element "Agency", to: :agency end end end diff --git a/lib/genericode/key.rb b/lib/genericode/key.rb index 303dab1..c137e8a 100644 --- a/lib/genericode/key.rb +++ b/lib/genericode/key.rb @@ -27,11 +27,15 @@ class Key < Lutaml::Model::Serializable json do map "Id", to: :id map "Annotation", to: :annotation - map "ShortName", to: :short_name, with: { from: :short_name_from_json, to: :short_name_to_json } - map "LongName", to: :long_name, with: { from: :long_name_from_json, to: :long_name_to_json } - map "CanonicalUri", to: :canonical_uri, with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } + map "ShortName", to: :short_name, + with: { from: :short_name_from_json, to: :short_name_to_json } + map "LongName", to: :long_name, + with: { from: :long_name_from_json, to: :long_name_to_json } + map "CanonicalUri", to: :canonical_uri, + with: { from: :canonical_uri_from_json, to: :canonical_uri_to_json } map "CanonicalVersionUri", to: :canonical_version_uri - map "ColumnRef", to: :column_ref, with: { from: :column_ref_from_json, to: :column_ref_to_json } + map "ColumnRef", to: :column_ref, + with: { from: :column_ref_from_json, to: :column_ref_to_json } end def long_name_from_json(model, value) @@ -45,7 +49,9 @@ def long_name_to_json(model, doc) end def column_ref_from_json(model, value) - model.column_ref = Utils.array_wrap(value).map { |n| KeyColumnRef.new(ref: n) } + model.column_ref = Utils.array_wrap(value).map do |n| + KeyColumnRef.new(ref: n) + end end def column_ref_to_json(model, doc) @@ -53,16 +59,16 @@ def column_ref_to_json(model, doc) end xml do - root "Key" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Key" + namespace Namespace map_attribute "Id", to: :id - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "ShortName", to: :short_name, prefix: nil, namespace: nil - map_element "LongName", to: :long_name, prefix: nil, namespace: nil - map_element "CanonicalUri", to: :canonical_uri, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "ColumnRef", to: :column_ref, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "ShortName", to: :short_name + map_element "LongName", to: :long_name + map_element "CanonicalUri", to: :canonical_uri + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "ColumnRef", to: :column_ref end end end diff --git a/lib/genericode/key_column_ref.rb b/lib/genericode/key_column_ref.rb index 852bf25..c7d0c85 100644 --- a/lib/genericode/key_column_ref.rb +++ b/lib/genericode/key_column_ref.rb @@ -15,11 +15,11 @@ class KeyColumnRef < Lutaml::Model::Serializable end xml do - root "KeyColumnRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "KeyColumnRef" + namespace Namespace map_attribute "Ref", to: :ref - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation end end end diff --git a/lib/genericode/key_ref.rb b/lib/genericode/key_ref.rb index c6fccb1..66917cf 100644 --- a/lib/genericode/key_ref.rb +++ b/lib/genericode/key_ref.rb @@ -21,14 +21,14 @@ class KeyRef < Lutaml::Model::Serializable end xml do - root "KeyRef" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "KeyRef" + namespace Namespace map_attribute "Id", to: :id map_attribute "ExternalRef", to: :external_ref - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "CanonicalVersionUri", to: :canonical_version_uri, prefix: nil, namespace: nil - map_element "LocationUri", to: :location_uri, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "CanonicalVersionUri", to: :canonical_version_uri + map_element "LocationUri", to: :location_uri end end end diff --git a/lib/genericode/long_name.rb b/lib/genericode/long_name.rb index 95ef406..98439d4 100644 --- a/lib/genericode/long_name.rb +++ b/lib/genericode/long_name.rb @@ -25,8 +25,8 @@ def lang_to_json(model, doc) end xml do - root "LongName" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "LongName" + namespace Namespace map_content to: :content map_attribute "Identifier", to: :identifier diff --git a/lib/genericode/mime_typed_uri.rb b/lib/genericode/mime_typed_uri.rb index d611816..24a5e64 100644 --- a/lib/genericode/mime_typed_uri.rb +++ b/lib/genericode/mime_typed_uri.rb @@ -13,8 +13,8 @@ class MimeTypedUri < Lutaml::Model::Serializable end xml do - root "MimeTypedUri" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "MimeTypedUri" + namespace Namespace map_content to: :content map_attribute "MimeType", to: :mime_type diff --git a/lib/genericode/namespace.rb b/lib/genericode/namespace.rb new file mode 100644 index 0000000..00fbeca --- /dev/null +++ b/lib/genericode/namespace.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require "lutaml/model" + +module Genericode + class Namespace < Lutaml::Model::XmlNamespace + uri "http://docs.oasis-open.org/codelist/ns/genericode/1.0/" + prefix_default "gc" + end +end diff --git a/lib/genericode/row.rb b/lib/genericode/row.rb index 64702e3..181d2fa 100644 --- a/lib/genericode/row.rb +++ b/lib/genericode/row.rb @@ -16,11 +16,11 @@ class Row < Lutaml::Model::Serializable end xml do - root "Row" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Row" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Value", to: :value, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "Value", to: :value end end end diff --git a/lib/genericode/short_name.rb b/lib/genericode/short_name.rb index c223ffe..48540cb 100644 --- a/lib/genericode/short_name.rb +++ b/lib/genericode/short_name.rb @@ -13,8 +13,8 @@ class ShortName < Lutaml::Model::Serializable end xml do - root "ShortName" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "ShortName" + namespace Namespace map_content to: :content map_attribute "lang", to: :lang, prefix: "xml", namespace: "http://www.w3.org/XML/1998/namespace" diff --git a/lib/genericode/simple_code_list.rb b/lib/genericode/simple_code_list.rb index 71046fa..b4e8b65 100644 --- a/lib/genericode/simple_code_list.rb +++ b/lib/genericode/simple_code_list.rb @@ -16,11 +16,11 @@ class SimpleCodeList < Lutaml::Model::Serializable end xml do - root "SimpleCodeList" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "SimpleCodeList" + namespace Namespace - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "Row", to: :row, prefix: nil, namespace: nil + map_element "Annotation", to: :annotation + map_element "Row", to: :row end end end diff --git a/lib/genericode/simple_value.rb b/lib/genericode/simple_value.rb index 36fbae0..7bd2a26 100644 --- a/lib/genericode/simple_value.rb +++ b/lib/genericode/simple_value.rb @@ -11,8 +11,8 @@ class SimpleValue < Lutaml::Model::Serializable end xml do - root "SimpleValue" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "SimpleValue" + namespace Namespace map_content to: :content end diff --git a/lib/genericode/value.rb b/lib/genericode/value.rb index 9eaf60c..3de4aea 100644 --- a/lib/genericode/value.rb +++ b/lib/genericode/value.rb @@ -22,13 +22,13 @@ class Value < Lutaml::Model::Serializable end xml do - root "Value" - namespace "http://docs.oasis-open.org/codelist/ns/genericode/1.0/", "gc" + element "Value" + namespace Namespace - map_attribute "ColumnRef", to: :column_ref, prefix: nil, namespace: nil - map_element "Annotation", to: :annotation, prefix: nil, namespace: nil - map_element "SimpleValue", to: :simple_value, prefix: nil, namespace: nil - map_element "ComplexValue", to: :complex_value, prefix: nil, namespace: nil + map_attribute "ColumnRef", to: :column_ref + map_element "Annotation", to: :annotation + map_element "SimpleValue", to: :simple_value + map_element "ComplexValue", to: :complex_value end end end diff --git a/spec/genericode/cli/code_lister_spec.rb b/spec/genericode/cli/code_lister_spec.rb index e5b0253..f3caf13 100644 --- a/spec/genericode/cli/code_lister_spec.rb +++ b/spec/genericode/cli/code_lister_spec.rb @@ -33,7 +33,7 @@ allow(File).to receive(:read).and_return(valid_xml) allow(Genericode::CodeList).to receive(:from_xml).and_call_original - expect(Genericode::Cli::CodeLister.list_codes("valid.gc")).to eq("Code\nCode1\nCode2") + expect(described_class.list_codes("valid.gc")).to eq("Code\nCode1\nCode2") end it "raises an error when an invalid ColumnRef is found" do @@ -57,9 +57,9 @@ allow(Genericode::CodeList).to receive(:from_xml).and_call_original expect do - Genericode::Cli::CodeLister.list_codes("invalid.gc") + described_class.list_codes("invalid.gc") end.to raise_error(Genericode::Error, - /INVALID_COLUMN_REF: Invalid ColumnRef 'invalid' in row 1/,) + /INVALID_COLUMN_REF: Invalid ColumnRef 'invalid' in row 1/) end it "raises an error when a code value is invalid" do @@ -84,9 +84,9 @@ allow(Genericode::CodeList).to receive(:from_xml).and_call_original expect do - Genericode::Cli::CodeLister.list_codes("invalid.gc") + described_class.list_codes("invalid.gc") end.to raise_error(Genericode::Error, - /INVALID_DATA_TYPE: Invalid data type for column 'Code' in row 1/,) + /INVALID_DATA_TYPE: Invalid data type for column 'Code' in row 1/) end end end diff --git a/spec/genericode/cli/code_lookup_spec.rb b/spec/genericode/cli/code_lookup_spec.rb index fe81db6..484d745 100644 --- a/spec/genericode/cli/code_lookup_spec.rb +++ b/spec/genericode/cli/code_lookup_spec.rb @@ -38,21 +38,24 @@ end it "looks up a code successfully" do - expect(Genericode::Cli::CodeLookup.lookup("file.gc", "code:Code1")).to eq("Code: Code1\nName: Name1") + expect(described_class.lookup("file.gc", + "code:Code1")).to eq("Code: Code1\nName: Name1") end it "looks up a name successfully" do - expect(Genericode::Cli::CodeLookup.lookup("file.gc", "name:Name1")).to eq("Code: Code1\nName: Name1") + expect(described_class.lookup("file.gc", + "name:Name1")).to eq("Code: Code1\nName: Name1") end it "looks up a simple value successfully" do - expect(Genericode::Cli::CodeLookup.lookup("file.gc", "code:Code1>name")).to eq("Name1") + expect(described_class.lookup("file.gc", + "code:Code1>name")).to eq("Name1") end it "raises an error for invalid path" do expect do - Genericode::Cli::CodeLookup.lookup("file.gc", - "invalid:path",) + described_class.lookup("file.gc", + "invalid:path") end.to raise_error(Genericode::Error, "Column not found: invalid") end end diff --git a/spec/genericode/cli/commands_spec.rb b/spec/genericode/cli/commands_spec.rb index 8dbaeae..ee130c9 100644 --- a/spec/genericode/cli/commands_spec.rb +++ b/spec/genericode/cli/commands_spec.rb @@ -11,12 +11,20 @@ describe "#convert" do it "converts XML to JSON successfully" do allow(Genericode::Cli::Converter).to receive(:convert).and_return(true) - expect { cli.convert("input.gc", "output.gcj") }.to output("Conversion successful.\n").to_stdout + expect do + cli.convert("input.gc", + "output.gcj") + end.to output("Conversion successful.\n").to_stdout end it "handles conversion errors" do - allow(Genericode::Cli::Converter).to receive(:convert).and_raise(Genericode::Error, "Conversion failed") - expect { cli.convert("input.gc", "output.gcj") }.to output("Conversion failed: Conversion failed\n").to_stdout + allow(Genericode::Cli::Converter).to receive(:convert).and_raise( + Genericode::Error, "Conversion failed" + ) + expect do + cli.convert("input.gc", + "output.gcj") + end.to output("Conversion failed: Conversion failed\n").to_stdout end end @@ -27,8 +35,12 @@ end it "handles validation errors" do - allow(Genericode::CodeList).to receive(:from_file).and_raise(Genericode::Error, "Invalid file") - expect { cli.validate("file.gc") }.to output("Validation failed: Invalid file\n").to_stdout + allow(Genericode::CodeList).to receive(:from_file).and_raise( + Genericode::Error, "Invalid file" + ) + expect do + cli.validate("file.gc") + end.to output("Validation failed: Invalid file\n").to_stdout end end @@ -39,8 +51,12 @@ end it "handles listing errors" do - allow(Genericode::Cli::CodeLister).to receive(:list_codes).and_raise(Genericode::Error, "Listing failed") - expect { cli.list_codes("file.gc") }.to output("Listing codes failed: Listing failed\n").to_stdout + allow(Genericode::Cli::CodeLister).to receive(:list_codes).and_raise( + Genericode::Error, "Listing failed" + ) + expect do + cli.list_codes("file.gc") + end.to output("Listing codes failed: Listing failed\n").to_stdout end end @@ -51,8 +67,13 @@ end it "handles lookup errors" do - allow(Genericode::Cli::CodeLookup).to receive(:lookup).and_raise(Genericode::Error, "Lookup failed") - expect { cli.lookup("file.gc", "path") }.to output("Lookup failed: Lookup failed\n").to_stdout + allow(Genericode::Cli::CodeLookup).to receive(:lookup).and_raise( + Genericode::Error, "Lookup failed" + ) + expect do + cli.lookup("file.gc", + "path") + end.to output("Lookup failed: Lookup failed\n").to_stdout end end end diff --git a/spec/genericode/cli/converter_spec.rb b/spec/genericode/cli/converter_spec.rb index dd8c5ef..ec702e3 100644 --- a/spec/genericode/cli/converter_spec.rb +++ b/spec/genericode/cli/converter_spec.rb @@ -12,7 +12,8 @@ allow(File).to receive(:write) allow_any_instance_of(Genericode::CodeList).to receive(:to_json).and_return("{}") - expect(Genericode::Cli::Converter.convert("input.gc", "output.gcj")).to be true + expect(described_class.convert("input.gc", + "output.gcj")).to be true end it "converts JSON to XML" do @@ -20,28 +21,30 @@ allow(File).to receive(:write) allow_any_instance_of(Genericode::CodeList).to receive(:to_xml).and_return("") - expect(Genericode::Cli::Converter.convert("input.gcj", "output.gc")).to be true + expect(described_class.convert("input.gcj", + "output.gc")).to be true end it "raises an error for invalid input format" do expect do - Genericode::Cli::Converter.convert("input.txt", - "output.gcj",) + described_class.convert("input.txt", + "output.gcj") end.to raise_error(Genericode::Error, "Invalid input format") end it "raises an error for invalid output format" do expect do - Genericode::Cli::Converter.convert("input.gc", - "output.txt",) + described_class.convert("input.gc", + "output.txt") end.to raise_error(Genericode::Error, "Invalid output format") end it "raises an error when input and output formats are the same" do expect do - Genericode::Cli::Converter.convert("input.gc", - "output.gc",) - end.to raise_error(Genericode::Error, "Input and output formats are the same") + described_class.convert("input.gc", + "output.gc") + end.to raise_error(Genericode::Error, + "Input and output formats are the same") end end end diff --git a/spec/genericode/cli/validator_spec.rb b/spec/genericode/cli/validator_spec.rb index 185b963..9450109 100644 --- a/spec/genericode/cli/validator_spec.rb +++ b/spec/genericode/cli/validator_spec.rb @@ -27,17 +27,16 @@ end it "validates a valid XML file" do - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:read).and_return(valid_xml) + allow(File).to receive_messages(exist?: true, read: valid_xml) - expect(Genericode::Cli::Validator.validate("valid.gc")).to be true + expect(described_class.validate("valid.gc")).to be true end it "raises an error for non-existent file" do allow(File).to receive(:exist?).and_return(false) expect do - Genericode::Cli::Validator.validate("nonexistent.gc") + described_class.validate("nonexistent.gc") end.to raise_error(Genericode::Error, "File does not exist") end @@ -45,26 +44,27 @@ allow(File).to receive(:exist?).and_return(true) expect do - Genericode::Cli::Validator.validate("invalid.txt") + described_class.validate("invalid.txt") end.to raise_error(Genericode::Error, "Invalid file format") end it "raises an error for empty column set" do xml_without_columns = "" - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:read).and_return(xml_without_columns) + allow(File).to receive_messages(exist?: true, read: xml_without_columns) expect do - Genericode::Cli::Validator.validate("invalid.gc") + described_class.validate("invalid.gc") end.to raise_error(Genericode::Error, "No columns defined") end it "raises an error for empty row set" do xml_without_rows = "" - allow(File).to receive(:exist?).and_return(true) - allow(File).to receive(:read).and_return(xml_without_rows) + allow(File).to receive_messages(exist?: true, read: xml_without_rows) - expect { Genericode::Cli::Validator.validate("invalid.gc") }.to raise_error(Genericode::Error, "No rows defined") + expect do + described_class.validate("invalid.gc") + end.to raise_error(Genericode::Error, + "No rows defined") end end end diff --git a/spec/genericode/code_list_spec.rb b/spec/genericode/code_list_spec.rb index a0a89e1..514f49b 100644 --- a/spec/genericode/code_list_spec.rb +++ b/spec/genericode/code_list_spec.rb @@ -31,14 +31,18 @@ row: [ Genericode::Row.new( value: [ - Genericode::Value.new(column_ref: "code", simple_value: Genericode::SimpleValue.new(content: "CODE1")), - Genericode::Value.new(column_ref: "name", simple_value: Genericode::SimpleValue.new(content: "Name 1")), + Genericode::Value.new(column_ref: "code", + simple_value: Genericode::SimpleValue.new(content: "CODE1")), + Genericode::Value.new(column_ref: "name", + simple_value: Genericode::SimpleValue.new(content: "Name 1")), ], ), Genericode::Row.new( value: [ - Genericode::Value.new(column_ref: "code", simple_value: Genericode::SimpleValue.new(content: "CODE2")), - Genericode::Value.new(column_ref: "name", simple_value: Genericode::SimpleValue.new(content: "Name 2")), + Genericode::Value.new(column_ref: "code", + simple_value: Genericode::SimpleValue.new(content: "CODE2")), + Genericode::Value.new(column_ref: "name", + simple_value: Genericode::SimpleValue.new(content: "Name 2")), ], ), ], @@ -92,7 +96,7 @@ it "reports duplicate column IDs" do invalid_column_set = valid_column_set.dup invalid_column_set.column << Genericode::Column.new(id: "code", - short_name: Genericode::ShortName.new(content: "Duplicate"),) + short_name: Genericode::ShortName.new(content: "Duplicate")) invalid_code_list = valid_code_list.dup invalid_code_list.column_set = invalid_column_set result = invalid_code_list.validate_verbose @@ -101,7 +105,8 @@ it "reports missing Code column" do invalid_column_set = Genericode::ColumnSet.new( - column: [Genericode::Column.new(id: "name", short_name: Genericode::ShortName.new(content: "Name"))], + column: [Genericode::Column.new(id: "name", + short_name: Genericode::ShortName.new(content: "Name"))], ) invalid_code_list = valid_code_list.dup invalid_code_list.column_set = invalid_column_set @@ -112,7 +117,8 @@ it "reports duplicate code values" do invalid_simple_code_list = valid_simple_code_list.dup invalid_simple_code_list.row << Genericode::Row.new( - value: [Genericode::Value.new(column_ref: "code", simple_value: Genericode::SimpleValue.new(content: "CODE1"))], + value: [Genericode::Value.new(column_ref: "code", + simple_value: Genericode::SimpleValue.new(content: "CODE1"))], ) invalid_code_list = valid_code_list.dup invalid_code_list.simple_code_list = invalid_simple_code_list @@ -124,7 +130,7 @@ invalid_simple_code_list = valid_simple_code_list.dup invalid_simple_code_list.row << Genericode::Row.new( value: [Genericode::Value.new(column_ref: "name", - simple_value: Genericode::SimpleValue.new(content: "Name 3"),)], + simple_value: Genericode::SimpleValue.new(content: "Name 3"))], ) invalid_code_list = valid_code_list.dup invalid_code_list.simple_code_list = invalid_simple_code_list @@ -158,7 +164,9 @@ it "converts XML to JSON correctly" do code_list = described_class.from_xml(xml_content) generated_json = JSON.parse(code_list.to_json(except: [:annotation])) - expected_json = JSON.parse(json_content).tap { |n| n.delete("Annotation") } + expected_json = JSON.parse(json_content).tap do |n| + n.delete("Annotation") + end expect(generated_json).to eq(expected_json) end diff --git a/spec/genericode_spec.rb b/spec/genericode_spec.rb index 4d9d787..61b6117 100644 --- a/spec/genericode_spec.rb +++ b/spec/genericode_spec.rb @@ -2,6 +2,7 @@ require "spec_helper" require "pathname" +require "nokogiri" require_relative "../lib/genericode/code_list" RSpec.describe Genericode do @@ -13,6 +14,24 @@ def check_parsed_content(parsed, reparsed) expect(reparsed.simple_code_list.row.size).to eq(parsed.simple_code_list.row.size) end + def strip_xml_comments(xml_string) + doc = Nokogiri::XML(xml_string) + doc.xpath("//comment()").remove + doc.to_xml + end + + def normalize_empty_elements(xml_string) + doc = Nokogiri::XML(xml_string) + # Find all elements and normalize their text content + doc.xpath("//*").each do |node| + # If element has no children and only whitespace text, set to empty + node.content = "" if node.children.all? do |child| + child.text? && child.text.strip.empty? + end + end + doc.to_xml + end + describe "XML round-trip conversion" do xml_files = Dir[fixtures_dir.join("xml", "*", "*.gc")] @@ -40,7 +59,11 @@ def check_parsed_content(parsed, reparsed) encoding: "utf-8", ) - expect(generated).to be_analogous_with(xml_string) + # Strip XML comments and normalize empty elements before comparison + xml_string_cleaned = normalize_empty_elements(strip_xml_comments(xml_string)) + generated_cleaned = normalize_empty_elements(strip_xml_comments(generated)) + + expect(generated_cleaned).to be_xml_equivalent_to(xml_string_cleaned) end end end @@ -62,7 +85,9 @@ def check_parsed_content(parsed, reparsed) end it "performs lossless round-trip conversion" do - original_to_test = JSON.parse(json_string).tap { |n| n.delete("Annotation") }.to_json + original_to_test = JSON.parse(json_string).tap do |n| + n.delete("Annotation") + end.to_json parsed = Genericode::CodeList.from_json(original_to_test) generated = Genericode::CodeList.to_json(parsed) reparsed = Genericode::CodeList.from_json(generated) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6d92c5a..46534b5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ require "genericode" require "nokogiri" -require "xml-c14n" +require "canon" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure