unitsdb 2.1.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +8 -1
  3. data/.gitignore +2 -0
  4. data/.gitmodules +4 -3
  5. data/.rubocop.yml +13 -8
  6. data/.rubocop_todo.yml +217 -100
  7. data/CLAUDE.md +55 -0
  8. data/Gemfile +4 -1
  9. data/README.adoc +283 -16
  10. data/data/dimensions.yaml +1864 -0
  11. data/data/prefixes.yaml +874 -0
  12. data/data/quantities.yaml +3715 -0
  13. data/data/scales.yaml +97 -0
  14. data/data/schemas/dimensions-schema.yaml +153 -0
  15. data/data/schemas/prefixes-schema.yaml +155 -0
  16. data/data/schemas/quantities-schema.yaml +117 -0
  17. data/data/schemas/scales-schema.yaml +106 -0
  18. data/data/schemas/unit_systems-schema.yaml +116 -0
  19. data/data/schemas/units-schema.yaml +215 -0
  20. data/data/unit_systems.yaml +78 -0
  21. data/data/units.yaml +14052 -0
  22. data/exe/unitsdb +7 -1
  23. data/lib/unitsdb/cli.rb +42 -15
  24. data/lib/unitsdb/commands/_modify.rb +40 -4
  25. data/lib/unitsdb/commands/base.rb +6 -2
  26. data/lib/unitsdb/commands/check_si/si_formatter.rb +488 -0
  27. data/lib/unitsdb/commands/check_si/si_matcher.rb +487 -0
  28. data/lib/unitsdb/commands/check_si/si_ttl_parser.rb +103 -0
  29. data/lib/unitsdb/commands/check_si/si_updater.rb +254 -0
  30. data/lib/unitsdb/commands/check_si.rb +54 -35
  31. data/lib/unitsdb/commands/get.rb +11 -10
  32. data/lib/unitsdb/commands/normalize.rb +21 -7
  33. data/lib/unitsdb/commands/qudt/check.rb +150 -0
  34. data/lib/unitsdb/commands/qudt/formatter.rb +194 -0
  35. data/lib/unitsdb/commands/qudt/matcher.rb +746 -0
  36. data/lib/unitsdb/commands/qudt/ttl_parser.rb +403 -0
  37. data/lib/unitsdb/commands/qudt/update.rb +126 -0
  38. data/lib/unitsdb/commands/qudt/updater.rb +189 -0
  39. data/lib/unitsdb/commands/qudt.rb +82 -0
  40. data/lib/unitsdb/commands/release.rb +12 -9
  41. data/lib/unitsdb/commands/search.rb +12 -11
  42. data/lib/unitsdb/commands/ucum/check.rb +42 -29
  43. data/lib/unitsdb/commands/ucum/formatter.rb +2 -1
  44. data/lib/unitsdb/commands/ucum/matcher.rb +23 -9
  45. data/lib/unitsdb/commands/ucum/update.rb +14 -13
  46. data/lib/unitsdb/commands/ucum/updater.rb +40 -6
  47. data/lib/unitsdb/commands/ucum/xml_parser.rb +0 -2
  48. data/lib/unitsdb/commands/ucum.rb +44 -4
  49. data/lib/unitsdb/commands/validate/identifiers.rb +2 -4
  50. data/lib/unitsdb/commands/validate/qudt_references.rb +111 -0
  51. data/lib/unitsdb/commands/validate/references.rb +36 -19
  52. data/lib/unitsdb/commands/validate/si_references.rb +3 -5
  53. data/lib/unitsdb/commands/validate/ucum_references.rb +105 -0
  54. data/lib/unitsdb/commands/validate.rb +67 -11
  55. data/lib/unitsdb/commands.rb +20 -0
  56. data/lib/unitsdb/config.rb +114 -2
  57. data/lib/unitsdb/database.rb +160 -123
  58. data/lib/unitsdb/dimension.rb +3 -4
  59. data/lib/unitsdb/dimension_details.rb +2 -1
  60. data/lib/unitsdb/dimension_reference.rb +2 -0
  61. data/lib/unitsdb/dimensions.rb +2 -2
  62. data/lib/unitsdb/errors.rb +7 -0
  63. data/lib/unitsdb/external_reference.rb +2 -0
  64. data/lib/unitsdb/identifier.rb +2 -0
  65. data/lib/unitsdb/localized_string.rb +2 -0
  66. data/lib/unitsdb/prefix.rb +2 -4
  67. data/lib/unitsdb/prefix_reference.rb +2 -2
  68. data/lib/unitsdb/prefixes.rb +2 -1
  69. data/lib/unitsdb/quantities.rb +2 -2
  70. data/lib/unitsdb/quantity.rb +2 -6
  71. data/lib/unitsdb/quantity_reference.rb +2 -0
  72. data/lib/unitsdb/qudt.rb +105 -0
  73. data/lib/unitsdb/root_unit_reference.rb +2 -3
  74. data/lib/unitsdb/scale.rb +2 -4
  75. data/lib/unitsdb/scale_properties.rb +2 -0
  76. data/lib/unitsdb/scale_reference.rb +2 -2
  77. data/lib/unitsdb/scales.rb +2 -2
  78. data/lib/unitsdb/si_derived_base.rb +2 -2
  79. data/lib/unitsdb/symbol_presentations.rb +2 -0
  80. data/lib/unitsdb/ucum.rb +21 -10
  81. data/lib/unitsdb/unit.rb +2 -10
  82. data/lib/unitsdb/unit_reference.rb +2 -2
  83. data/lib/unitsdb/unit_system.rb +3 -3
  84. data/lib/unitsdb/unit_system_reference.rb +2 -2
  85. data/lib/unitsdb/unit_systems.rb +2 -2
  86. data/lib/unitsdb/units.rb +2 -2
  87. data/lib/unitsdb/utils.rb +32 -21
  88. data/lib/unitsdb/version.rb +5 -1
  89. data/lib/unitsdb.rb +62 -14
  90. data/unitsdb.gemspec +6 -3
  91. metadata +52 -13
  92. data/lib/unitsdb/commands/si_formatter.rb +0 -485
  93. data/lib/unitsdb/commands/si_matcher.rb +0 -470
  94. data/lib/unitsdb/commands/si_ttl_parser.rb +0 -100
  95. data/lib/unitsdb/commands/si_updater.rb +0 -212
@@ -4,16 +4,28 @@ require "thor"
4
4
 
5
5
  module Unitsdb
6
6
  module Commands
7
+ module Validate
8
+ autoload :Identifiers, "unitsdb/commands/validate/identifiers"
9
+ autoload :QudtReferences, "unitsdb/commands/validate/qudt_references"
10
+ autoload :References, "unitsdb/commands/validate/references"
11
+ autoload :SiReferences, "unitsdb/commands/validate/si_references"
12
+ autoload :UcumReferences, "unitsdb/commands/validate/ucum_references"
13
+ end
14
+
7
15
  class ValidateCommand < Thor
16
+ # Inherit trace option from parent CLI
17
+ class_option :trace, type: :boolean, default: false,
18
+ desc: "Show full backtrace on error"
19
+
8
20
  desc "references", "Validate that all references exist"
9
- option :debug_registry, type: :boolean, desc: "Show registry contents for debugging"
21
+ option :debug_registry, type: :boolean,
22
+ desc: "Show registry contents for debugging"
10
23
  option :database, type: :string, required: true, aliases: "-d",
11
24
  desc: "Path to UnitsDB database (required)"
12
- option :print_valid, type: :boolean, default: false, desc: "Print valid references too"
25
+ option :print_valid, type: :boolean, default: false,
26
+ desc: "Print valid references too"
13
27
  def references
14
- require_relative "validate/references"
15
-
16
- Commands::Validate::References.new(options).run
28
+ run_command(Commands::Validate::References, options)
17
29
  end
18
30
 
19
31
  desc "identifiers", "Check for uniqueness of identifier fields"
@@ -21,19 +33,63 @@ module Unitsdb
21
33
  desc: "Path to UnitsDB database (required)"
22
34
 
23
35
  def identifiers
24
- require_relative "validate/identifiers"
25
-
26
- Commands::Validate::Identifiers.new(options).run
36
+ run_command(Commands::Validate::Identifiers, options)
27
37
  end
28
38
 
29
- desc "si_references", "Validate that each SI digital framework reference is unique per entity type"
39
+ desc "si_references",
40
+ "Validate that each SI digital framework reference is unique per entity type"
30
41
  option :database, type: :string, required: true, aliases: "-d",
31
42
  desc: "Path to UnitsDB database (required)"
32
43
 
33
44
  def si_references
34
- require_relative "validate/si_references"
45
+ run_command(Commands::Validate::SiReferences, options)
46
+ end
47
+
48
+ desc "qudt_references",
49
+ "Validate that each QUDT reference is unique per entity type"
50
+ option :database, type: :string, required: true, aliases: "-d",
51
+ desc: "Path to UnitsDB database (required)"
52
+
53
+ def qudt_references
54
+ run_command(Commands::Validate::QudtReferences, options)
55
+ end
56
+
57
+ desc "ucum_references",
58
+ "Validate that each UCUM reference is unique per entity type"
59
+ option :database, type: :string, required: true, aliases: "-d",
60
+ desc: "Path to UnitsDB database (required)"
61
+
62
+ def ucum_references
63
+ run_command(Commands::Validate::UcumReferences, options)
64
+ end
65
+
66
+ private
67
+
68
+ def run_command(command_class, options)
69
+ command = command_class.new(options)
70
+ command.run
71
+ rescue Unitsdb::Errors::CLIRuntimeError => e
72
+ handle_cli_error(e)
73
+ rescue StandardError => e
74
+ handle_error(e)
75
+ end
76
+
77
+ def handle_cli_error(error)
78
+ if options[:trace]
79
+ raise error
80
+ else
81
+ warn "Error: #{error.message}"
82
+ exit 1
83
+ end
84
+ end
35
85
 
36
- Commands::Validate::SiReferences.new(options).run
86
+ def handle_error(error)
87
+ if options[:trace]
88
+ raise error
89
+ else
90
+ warn "Error: #{error.message}"
91
+ exit 1
92
+ end
37
93
  end
38
94
  end
39
95
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unitsdb
4
+ module Commands
5
+ autoload :ModifyCommand, "unitsdb/commands/_modify"
6
+ autoload :Base, "unitsdb/commands/base"
7
+ autoload :CheckSi, "unitsdb/commands/check_si"
8
+ autoload :CheckSiCommand, "unitsdb/commands/check_si"
9
+ autoload :Get, "unitsdb/commands/get"
10
+ autoload :Normalize, "unitsdb/commands/normalize"
11
+ autoload :Qudt, "unitsdb/commands/qudt"
12
+ autoload :QudtCommand, "unitsdb/commands/qudt"
13
+ autoload :Release, "unitsdb/commands/release"
14
+ autoload :Search, "unitsdb/commands/search"
15
+ autoload :Ucum, "unitsdb/commands/ucum"
16
+ autoload :UcumCommand, "unitsdb/commands/ucum"
17
+ autoload :Validate, "unitsdb/commands/validate"
18
+ autoload :ValidateCommand, "unitsdb/commands/validate"
19
+ end
20
+ end
@@ -2,17 +2,129 @@
2
2
 
3
3
  module Unitsdb
4
4
  class Config
5
+ CONTEXT_ID = :unitsdb_v2
6
+
5
7
  class << self
8
+ def context_id
9
+ @context_id ||= CONTEXT_ID
10
+ end
11
+
12
+ def register_model(klass, id:)
13
+ registered_models[id.to_sym] = klass
14
+ klass
15
+ end
16
+
17
+ def registered_models
18
+ @registered_models ||= {}
19
+ end
20
+
6
21
  def models
7
22
  @models ||= {}
8
23
  end
9
24
 
10
25
  def models=(user_models)
11
- models.merge!(user_models)
26
+ normalized_models = user_models.each_with_object({}) do |(id, klass), result|
27
+ model_id = id.to_sym
28
+ result[model_id] = register_model(klass, id: model_id)
29
+ end
30
+
31
+ models.merge!(normalized_models)
12
32
  end
13
33
 
14
34
  def model_for(model_name)
15
- models[model_name]
35
+ model_id = model_name.to_sym
36
+ models[model_id] || registered_models[model_id]
37
+ end
38
+
39
+ def register(id = context_id)
40
+ explicit_registers[id.to_sym]
41
+ end
42
+
43
+ def populate_register(id: context_id, fallback_to: [:default], substitutions: [])
44
+ register_id = id.to_sym
45
+ context(register_id)
46
+
47
+ model_register = Lutaml::Model::Register.new(register_id, fallback: fallback_to)
48
+ resolve_substitutions(
49
+ substitutions,
50
+ registry: build_registry,
51
+ fallback_to: fallback_to,
52
+ id: "#{register_id}_register",
53
+ ).each do |substitution|
54
+ model_register.register_global_type_substitution(**substitution)
55
+ end
56
+
57
+ explicit_registers[register_id] = Lutaml::Model::GlobalRegister.register(model_register)
58
+ end
59
+
60
+ def find_context(id)
61
+ Lutaml::Model::GlobalContext.context(id.to_sym)
62
+ end
63
+
64
+ def resolve_type(type_name, context: context_id)
65
+ Lutaml::Model::GlobalContext.resolve_type(type_name, context.to_sym)
66
+ end
67
+
68
+ def context(id = context_id, force_populate: false)
69
+ existing = find_context(id)
70
+ return existing if existing && !force_populate && populated?(id)
71
+
72
+ populate_context(id: id)
73
+ end
74
+
75
+ def populate_context(id: context_id, fallback_to: [:default], substitutions: [])
76
+ Lutaml::Model::GlobalContext.unregister_context(id) if find_context(id)
77
+
78
+ opts = { registry: build_registry, fallback_to: fallback_to, id: id }
79
+ context = Lutaml::Model::GlobalContext.create_context(
80
+ substitutions: resolve_substitutions(substitutions, **opts),
81
+ **opts,
82
+ )
83
+ mark_populated!(id)
84
+ context
85
+ end
86
+
87
+ def resolve_substitutions(substitutions, registry:, fallback_to:, id:)
88
+ resolution_context = Lutaml::Model::TypeContext.derived(
89
+ id: "#{id}_substitution_resolution",
90
+ registry: registry,
91
+ fallback_to: fallback_to,
92
+ )
93
+
94
+ Array(substitutions).map do |substitution|
95
+ from_key = substitution[:from_type] || substitution[:from]
96
+ to_key = substitution[:to_type] || substitution[:to]
97
+
98
+ {
99
+ from_type: resolve_substitution_type(from_key, resolution_context),
100
+ to_type: resolve_substitution_type(to_key, resolution_context),
101
+ }
102
+ end
103
+ end
104
+
105
+ def resolve_substitution_type(value, resolution_context)
106
+ return value if value.is_a?(Class)
107
+
108
+ Lutaml::Model::TypeResolver.resolve(value, resolution_context)
109
+ end
110
+
111
+ def build_registry
112
+ registry = Lutaml::Model::TypeRegistry.new
113
+ registered_models.each { |model_id, klass| registry.register(model_id, klass) }
114
+ registry
115
+ end
116
+
117
+ def populated?(context_id)
118
+ @populated_for&.[](context_id.to_sym)
119
+ end
120
+
121
+ def mark_populated!(context_id)
122
+ @populated_for ||= {}
123
+ @populated_for[context_id.to_sym] = true
124
+ end
125
+
126
+ def explicit_registers
127
+ @explicit_registers ||= {}
16
128
  end
17
129
  end
18
130
  end