unitsdb 2.2.1 → 2.2.4

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/opal.yml +36 -0
  3. data/.gitignore +3 -0
  4. data/Gemfile +1 -0
  5. data/Rakefile +3 -1
  6. data/lib/unitsdb/cli.rb +5 -41
  7. data/lib/unitsdb/commands/_modify.rb +1 -34
  8. data/lib/unitsdb/commands/check_si/si_formatter.rb +6 -6
  9. data/lib/unitsdb/commands/check_si/si_matcher.rb +202 -292
  10. data/lib/unitsdb/commands/check_si/si_updater.rb +16 -36
  11. data/lib/unitsdb/commands/entity_presenter.rb +98 -0
  12. data/lib/unitsdb/commands/get.rb +16 -113
  13. data/lib/unitsdb/commands/qudt/formatter.rb +16 -27
  14. data/lib/unitsdb/commands/qudt/matcher.rb +18 -28
  15. data/lib/unitsdb/commands/qudt/updater.rb +8 -11
  16. data/lib/unitsdb/commands/qudt.rb +1 -34
  17. data/lib/unitsdb/commands/search.rb +33 -188
  18. data/lib/unitsdb/commands/thor.rb +41 -0
  19. data/lib/unitsdb/commands/ucum/formatter.rb +9 -18
  20. data/lib/unitsdb/commands/ucum/matcher.rb +4 -4
  21. data/lib/unitsdb/commands/ucum/updater.rb +3 -5
  22. data/lib/unitsdb/commands/ucum.rb +1 -34
  23. data/lib/unitsdb/commands/validate/qudt_references.rb +29 -70
  24. data/lib/unitsdb/commands/validate/references.rb +5 -303
  25. data/lib/unitsdb/commands/validate/si_references.rb +30 -66
  26. data/lib/unitsdb/commands/validate/ucum_references.rb +30 -64
  27. data/lib/unitsdb/commands/validate.rb +1 -36
  28. data/lib/unitsdb/commands.rb +2 -0
  29. data/lib/unitsdb/config.rb +170 -4
  30. data/lib/unitsdb/database/loader.rb +135 -0
  31. data/lib/unitsdb/database/reference_validator.rb +227 -0
  32. data/lib/unitsdb/database/uniqueness_validator.rb +80 -0
  33. data/lib/unitsdb/database.rb +127 -588
  34. data/lib/unitsdb/dimension.rb +2 -27
  35. data/lib/unitsdb/dimension_details.rb +2 -0
  36. data/lib/unitsdb/dimension_reference.rb +2 -0
  37. data/lib/unitsdb/dimensions.rb +2 -2
  38. data/lib/unitsdb/external_reference.rb +2 -0
  39. data/lib/unitsdb/identifier.rb +2 -0
  40. data/lib/unitsdb/localized_string.rb +2 -0
  41. data/lib/unitsdb/opal.rb +43 -0
  42. data/lib/unitsdb/prefix.rb +2 -13
  43. data/lib/unitsdb/prefix_reference.rb +2 -0
  44. data/lib/unitsdb/prefixes.rb +2 -2
  45. data/lib/unitsdb/quantities.rb +2 -1
  46. data/lib/unitsdb/quantity.rb +2 -2
  47. data/lib/unitsdb/quantity_reference.rb +2 -2
  48. data/lib/unitsdb/qudt.rb +5 -0
  49. data/lib/unitsdb/root_unit_reference.rb +2 -2
  50. data/lib/unitsdb/scale.rb +2 -2
  51. data/lib/unitsdb/scale_properties.rb +2 -1
  52. data/lib/unitsdb/scale_reference.rb +2 -0
  53. data/lib/unitsdb/scales.rb +2 -1
  54. data/lib/unitsdb/si_derived_base.rb +2 -1
  55. data/lib/unitsdb/symbol_presentations.rb +2 -2
  56. data/lib/unitsdb/ucum.rb +7 -0
  57. data/lib/unitsdb/unit.rb +2 -34
  58. data/lib/unitsdb/unit_reference.rb +2 -0
  59. data/lib/unitsdb/unit_system.rb +2 -2
  60. data/lib/unitsdb/unit_system_reference.rb +2 -0
  61. data/lib/unitsdb/unit_systems.rb +2 -2
  62. data/lib/unitsdb/units.rb +2 -2
  63. data/lib/unitsdb/version.rb +1 -1
  64. data/lib/unitsdb.rb +134 -27
  65. data/unitsdb.gemspec +1 -0
  66. metadata +23 -2
@@ -1,219 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
-
5
3
  module Unitsdb
6
4
  module Commands
7
5
  class Search < Base
8
6
  def run(query)
9
- # Database path is guaranteed by Thor's global option
10
-
11
7
  type = @options[:type]
12
8
  id = @options[:id]
13
9
  id_type = @options[:id_type]
14
10
  format = @options[:format] || "text"
15
11
 
16
- begin
17
- database = load_database(@options[:database])
18
-
19
- # Search by ID (early return)
20
- if id
21
- entity = database.get_by_id(id: id, type: id_type)
22
-
23
- unless entity
24
- puts "No entity found with ID: '#{id}'"
25
- return
26
- end
27
-
28
- # Use the same output logic as the Get command
29
- if %w[json yaml].include?(format.downcase)
30
- begin
31
- puts entity.send("to_#{format.downcase}")
32
- return
33
- rescue NoMethodError
34
- raise Unitsdb::Errors::InvalidFormatError,
35
- "Unable to convert entity to #{format.upcase} format: output format not supported for this entity type"
36
- end
37
- end
38
-
39
- print_entity_details(entity)
40
- return
41
- end
42
-
43
- # Regular text search
44
- results = database.search(text: query, type: type)
45
-
46
- # Early return for empty results
47
- if results.empty?
48
- puts "No results found for '#{query}'"
49
- return
50
- end
51
-
52
- # Format-specific output
53
- if %w[json yaml].include?(format.downcase)
54
- temp_db = create_temporary_database(results)
55
- puts temp_db.send("to_#{format.downcase}")
56
- return
57
- end
12
+ database = load_database(@options[:database])
58
13
 
59
- # Default text output
60
- puts "Found #{results.size} result(s) for '#{query}':"
61
- results.each do |entity|
62
- print_entity_with_ids(entity)
63
- end
64
- rescue Unitsdb::Errors::DatabaseError => e
65
- raise Unitsdb::Errors::DatabaseLoadError,
66
- "Failed to load database: #{e.message}"
67
- rescue StandardError => e
68
- raise Unitsdb::Errors::CLIRuntimeError, "Search failed: #{e.message}"
14
+ if id
15
+ lookup_by_id(database, id, id_type, format)
16
+ return
69
17
  end
70
- end
71
-
72
- private
73
-
74
- def print_entity_with_ids(entity)
75
- # Determine entity type
76
- entity_type = get_entity_type(entity)
77
-
78
- # Get name
79
- name = get_entity_name(entity)
80
18
 
81
- # Get all identifiers
82
- identifiers = entity.identifiers || []
83
-
84
- # Print entity information
85
- puts " - #{entity_type}: #{name}"
86
-
87
- # Print each identifier on its own line for better readability
88
- if identifiers.empty?
89
- puts " ID: None"
90
- else
91
- puts " IDs:"
92
- identifiers.each do |id|
93
- puts " - #{id.id} (Type: #{id.type || 'N/A'})"
94
- end
19
+ results = database.search(text: query, type: type)
20
+ if results.empty?
21
+ puts "No results found for '#{query}'"
22
+ return
95
23
  end
96
24
 
97
- # If entity has a short description, print it
98
- puts " Description: #{entity.short}" if entity.respond_to?(:short) && entity.short && entity.short != name
99
-
100
- # Add a blank line for readability
101
- puts ""
25
+ print_results(results, query, format)
26
+ rescue Unitsdb::Errors::DatabaseError => e
27
+ raise Unitsdb::Errors::DatabaseLoadError,
28
+ "Failed to load database: #{e.message}"
29
+ rescue StandardError => e
30
+ raise Unitsdb::Errors::CLIRuntimeError, "Search failed: #{e.message}"
102
31
  end
103
32
 
104
- def print_entity_details(entity)
105
- # Determine entity type
106
- entity_type = get_entity_type(entity)
107
-
108
- # Get name
109
- name = get_entity_name(entity)
110
-
111
- puts "Entity details:"
112
- puts " - Type: #{entity_type}"
113
- puts " - Name: #{name}"
114
-
115
- # Print description if available
116
- puts " - Description: #{entity.short}" if entity.respond_to?(:short) && entity.short && entity.short != name
117
-
118
- # Print all identifiers
119
- if entity.identifiers&.any?
120
- puts " - Identifiers:"
121
- entity.identifiers.each do |id|
122
- puts " - #{id.id} (Type: #{id.type || 'N/A'})"
123
- end
124
- else
125
- puts " - Identifiers: None"
126
- end
127
-
128
- # Print additional properties based on entity type
129
- case entity
130
- when Unitsdb::Unit
131
- puts " - Symbols:" if entity.respond_to?(:symbols) && entity.symbols&.any?
132
- if entity.respond_to?(:symbols) && entity.symbols&.any?
133
- entity.symbols.each do |s|
134
- puts " - #{s}"
135
- end
136
- end
137
-
138
- puts " - Definition: #{entity.definition}" if entity.respond_to?(:definition) && entity.definition
139
-
140
- if entity.respond_to?(:dimensions) && entity.dimensions&.any?
141
- puts " - Dimensions:"
142
- entity.dimensions.each { |d| puts " - #{d}" }
143
- end
144
- when Unitsdb::Quantity
145
- puts " - Dimensions: #{entity.dimension}" if entity.respond_to?(:dimension) && entity.dimension
146
- when Unitsdb::Prefix
147
- puts " - Value: #{entity.value}" if entity.respond_to?(:value) && entity.value
148
- puts " - Symbol: #{entity.symbol}" if entity.respond_to?(:symbol) && entity.symbol
149
- when Unitsdb::Dimension
150
- # Any dimension-specific properties
151
- when Unitsdb::UnitSystem
152
- puts " - Organization: #{entity.organization}" if entity.respond_to?(:organization) && entity.organization
153
- end
154
-
155
- # Print references if available
156
- return unless entity.respond_to?(:references) && entity.references&.any?
33
+ private
157
34
 
158
- puts " - References:"
159
- entity.references.each do |ref|
160
- puts " - #{ref.type}: #{ref.id}"
35
+ def lookup_by_id(database, id, id_type, format)
36
+ entity = database.get_by_id(id: id, type: id_type)
37
+ if entity.nil?
38
+ puts "No entity found with ID: '#{id}'"
39
+ return
161
40
  end
162
- end
163
41
 
164
- def get_entity_type(entity)
165
- case entity
166
- when Unitsdb::Unit
167
- "Unit"
168
- when Unitsdb::Prefix
169
- "Prefix"
170
- when Unitsdb::Quantity
171
- "Quantity"
172
- when Unitsdb::Dimension
173
- "Dimension"
174
- when Unitsdb::UnitSystem
175
- "UnitSystem"
42
+ if %w[json yaml].include?(format.downcase)
43
+ print_serialized(entity, format.downcase)
176
44
  else
177
- "Unknown"
45
+ EntityPresenter.new(entity).print_details
178
46
  end
179
47
  end
180
48
 
181
- def get_entity_name(entity)
182
- # Using early returns is still preferable for simple conditions
183
- return entity.names.first if entity.respond_to?(:names) && entity.names&.any?
184
- return entity.name if entity.respond_to?(:name) && entity.name
185
- return entity.short if entity.respond_to?(:short) && entity.short
49
+ def print_results(results, query, format)
50
+ if %w[json yaml].include?(format.downcase)
51
+ temp_db = Database.empty_for_results(results)
52
+ print_serialized(temp_db, format.downcase)
53
+ return
54
+ end
186
55
 
187
- "N/A" # Default if no name found
56
+ puts "Found #{results.size} result(s) for '#{query}':"
57
+ results.each { |entity| EntityPresenter.new(entity).print_summary }
188
58
  end
189
59
 
190
- def create_temporary_database(results)
191
- temp_db = Unitsdb::Database.new
192
-
193
- # Initialize collections
194
- temp_db.units = []
195
- temp_db.prefixes = []
196
- temp_db.quantities = []
197
- temp_db.dimensions = []
198
- temp_db.unit_systems = []
199
-
200
- # Add results to appropriate collection based on type using case statement
201
- results.each do |entity|
202
- case entity
203
- when Unitsdb::Unit
204
- temp_db.units << entity
205
- when Unitsdb::Prefix
206
- temp_db.prefixes << entity
207
- when Unitsdb::Quantity
208
- temp_db.quantities << entity
209
- when Unitsdb::Dimension
210
- temp_db.dimensions << entity
211
- when Unitsdb::UnitSystem
212
- temp_db.unit_systems << entity
213
- end
214
- end
215
-
216
- temp_db
60
+ def print_serialized(entity, format)
61
+ puts entity.public_send("to_#{format}")
217
62
  end
218
63
  end
219
64
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+
5
+ module Unitsdb
6
+ module Commands
7
+ # Base class for every Thor-based CLI surface in the gem. Owns the
8
+ # shared `--trace` option, the `exit_on_failure?` policy, the
9
+ # `run_command` dispatch helper, and the single error handler
10
+ # (`handle_error`). Subclasses inherit these and provide only their
11
+ # own `desc`/`option`/subcommand declarations.
12
+ class Thor < ::Thor
13
+ class_option :trace, type: :boolean, default: false,
14
+ desc: "Show full backtrace on error"
15
+
16
+ def self.exit_on_failure?
17
+ true
18
+ end
19
+
20
+ private
21
+
22
+ # Instantiate `command_class` with `options` plus any extra
23
+ # positional args, then call its `run` (or other public method).
24
+ # All exceptions route through `handle_error`.
25
+ def run_command(command_class, options, *args, method: :run)
26
+ command_class.new(options).public_send(method, *args)
27
+ rescue StandardError => e
28
+ handle_error(e)
29
+ end
30
+
31
+ # Re-raise when `--trace` is set so the user sees the full
32
+ # backtrace. Otherwise warn-and-exit-1 for a clean CLI UX.
33
+ def handle_error(error)
34
+ raise error if options[:trace]
35
+
36
+ warn "Error: #{error.message}"
37
+ exit 1
38
+ end
39
+ end
40
+ end
41
+ end
@@ -48,7 +48,7 @@ unmatched_ucum)
48
48
  db_id = get_db_entity_id(db_entity)
49
49
  db_name = get_db_entity_name(db_entity)
50
50
  ucum_name = get_ucum_entity_name(ucum_entity)
51
- ucum_code = ucum_entity.respond_to?(:code_sensitive) ? ucum_entity.code_sensitive : "unknown"
51
+ ucum_code = ucum_entity.code_sensitive || "unknown"
52
52
 
53
53
  case ucum_entity
54
54
  when Unitsdb::UcumPrefix
@@ -88,7 +88,7 @@ unmatched_ucum)
88
88
  db_id = get_db_entity_id(db_entity)
89
89
  db_name = get_db_entity_name(db_entity)
90
90
  ucum_name = get_ucum_entity_name(ucum_entity)
91
- ucum_code = ucum_entity.respond_to?(:code_sensitive) ? ucum_entity.code_sensitive : "unknown"
91
+ ucum_code = ucum_entity.code_sensitive || "unknown"
92
92
 
93
93
  case ucum_entity
94
94
  when Unitsdb::UcumPrefix
@@ -103,26 +103,17 @@ unmatched_ucum)
103
103
 
104
104
  # Helper to get db entity id
105
105
  def get_db_entity_id(entity)
106
- if entity.respond_to?(:identifiers) && entity.identifiers && !entity.identifiers.empty?
107
- entity.identifiers.first.id
108
- elsif entity.respond_to?(:id)
109
- entity.id
110
- else
111
- "unknown-id"
112
- end
106
+ return entity.identifiers.first.id unless entity.identifiers.empty?
107
+
108
+ entity.short || "unknown-id"
113
109
  end
114
110
 
115
111
  # Helper to get db entity name
116
112
  def get_db_entity_name(entity)
117
- if entity.respond_to?(:names) && entity.names && !entity.names.empty?
118
- entity.names.first.value
119
- elsif entity.respond_to?(:short) && entity.short
120
- entity.short
121
- elsif entity.respond_to?(:name)
122
- entity.name
123
- else
124
- "unknown-name"
125
- end
113
+ return entity.names.first.value unless entity.names.empty?
114
+ return entity.short if entity.short
115
+
116
+ "unknown-name"
126
117
  end
127
118
 
128
119
  # Helper to get ucum entity name
@@ -70,14 +70,14 @@ module Unitsdb
70
70
 
71
71
  # Check if a UnitsDB entity already has a UCUM reference
72
72
  def has_ucum_reference?(entity)
73
- return false unless entity.respond_to?(:references) && entity.references
73
+ return false unless entity.references
74
74
 
75
75
  entity.references.any? { |ref| ref.authority == "ucum" }
76
76
  end
77
77
 
78
78
  # Find the referenced UCUM entity based on the reference URI
79
79
  def find_referenced_ucum_entity(db_entity, ucum_entities)
80
- return nil unless db_entity.respond_to?(:references) && db_entity.references
80
+ return nil unless db_entity.references
81
81
 
82
82
  ucum_ref = db_entity.references.find { |ref| ref.authority == "ucum" }
83
83
  return nil unless ucum_ref
@@ -87,8 +87,8 @@ module Unitsdb
87
87
  end
88
88
 
89
89
  # Get the ID of a UnitsDB entity
90
- def get_entity_id(entity)
91
- entity.respond_to?(:id) ? entity.id : nil
90
+ def get_entity_id(_entity)
91
+ nil
92
92
  end
93
93
 
94
94
  # Find a matching UnitsDB entity for a UCUM entity
@@ -120,11 +120,9 @@ include_potential = false)
120
120
 
121
121
  # Get entity ID (either from identifiers array or directly)
122
122
  def get_entity_id(entity)
123
- if entity.respond_to?(:identifiers) && entity.identifiers && !entity.identifiers.empty?
124
- entity.identifiers.first.id
125
- elsif entity.respond_to?(:id)
126
- entity.id
127
- end
123
+ return entity.identifiers.first.id unless entity.identifiers.empty?
124
+
125
+ entity.short
128
126
  end
129
127
  end
130
128
  end
@@ -13,11 +13,7 @@ module Unitsdb
13
13
  autoload :XmlParser, "unitsdb/commands/ucum/xml_parser"
14
14
  end
15
15
 
16
- class UcumCommand < Thor
17
- # Inherit trace option from parent CLI
18
- class_option :trace, type: :boolean, default: false,
19
- desc: "Show full backtrace on error"
20
-
16
+ class UcumCommand < Unitsdb::Commands::Thor
21
17
  desc "check", "Check UCUM references in UnitsDB"
22
18
  option :entity_type, type: :string, aliases: "-e",
23
19
  desc: "Entity type to check (units, prefixes). If not specified, all types are checked"
@@ -49,35 +45,6 @@ module Unitsdb
49
45
  def update
50
46
  run_command(Ucum::Update, options)
51
47
  end
52
-
53
- private
54
-
55
- def run_command(command_class, options)
56
- command = command_class.new(options)
57
- command.run
58
- rescue Unitsdb::Errors::CLIRuntimeError => e
59
- handle_cli_error(e)
60
- rescue StandardError => e
61
- handle_error(e)
62
- end
63
-
64
- def handle_cli_error(error)
65
- if options[:trace]
66
- raise error
67
- else
68
- warn "Error: #{error.message}"
69
- exit 1
70
- end
71
- end
72
-
73
- def handle_error(error)
74
- if options[:trace]
75
- raise error
76
- else
77
- warn "Error: #{error.message}"
78
- exit 1
79
- end
80
- end
81
48
  end
82
49
  end
83
50
  end
@@ -3,16 +3,15 @@
3
3
  module Unitsdb
4
4
  module Commands
5
5
  module Validate
6
+ # `unitsdb validate qudt_references`. Checks that no QUDT
7
+ # reference URI is used by more than one entity of a given type.
6
8
  class QudtReferences < Unitsdb::Commands::Base
9
+ AUTHORITY = "qudt"
10
+
7
11
  def run
8
- # Load the database
9
12
  db = load_database(@options[:database])
10
-
11
- # Check for duplicate QUDT references
12
- duplicates = check_qudt_references(db)
13
-
14
- # Display results
15
- display_duplicate_results(duplicates)
13
+ duplicates = scan(db)
14
+ display(duplicates)
16
15
  rescue Unitsdb::Errors::DatabaseError => e
17
16
  raise Unitsdb::Errors::ValidationError,
18
17
  "Failed to validate QUDT references: #{e.message}"
@@ -20,90 +19,50 @@ module Unitsdb
20
19
 
21
20
  private
22
21
 
23
- def check_qudt_references(db)
24
- duplicates = {}
25
-
26
- # Check units
27
- check_entity_qudt_references(db.units, "units", duplicates)
28
-
29
- # Check quantities
30
- check_entity_qudt_references(db.quantities, "quantities", duplicates)
31
-
32
- # Check dimensions
33
- check_entity_qudt_references(db.dimensions, "dimensions", duplicates)
34
-
35
- # Check unit_systems
36
- check_entity_qudt_references(db.unit_systems, "unit_systems",
37
- duplicates)
38
-
39
- duplicates
22
+ def scan(db)
23
+ {}.tap do |duplicates|
24
+ Unitsdb::Database::COLLECTIONS.each do |name|
25
+ collection = db.collection(name) || []
26
+ dup = scan_collection(collection)
27
+ duplicates[name.to_s] = dup unless dup.empty?
28
+ end
29
+ end
40
30
  end
41
31
 
42
- def check_entity_qudt_references(entities, entity_type, duplicates)
43
- # Track QUDT references by URI
44
- qudt_refs = {}
45
-
32
+ def scan_collection(entities)
33
+ by_uri = {}
46
34
  entities.each_with_index do |entity, index|
47
- # Skip if no references
48
- next unless entity.respond_to?(:references) && entity.references
49
-
50
- # Check each reference
51
- entity.references.each do |ref|
52
- # Only interested in qudt references
53
- next unless ref.authority == "qudt"
54
-
55
- # Get entity info for display
56
- entity_id = if entity.respond_to?(:identifiers) && entity.identifiers&.first.respond_to?(:id)
57
- entity.identifiers.first.id
58
- else
59
- entity.short
60
- end
35
+ refs = entity.references || []
36
+ refs.each do |ref|
37
+ next unless ref.authority == AUTHORITY
61
38
 
62
- # Track this reference
63
- qudt_refs[ref.uri] ||= []
64
- qudt_refs[ref.uri] << {
65
- entity_id: entity_id,
66
- entity_name: entity.respond_to?(:names) ? entity.names.first : entity.short,
39
+ (by_uri[ref.uri] ||= []) << {
40
+ entity_id: entity.identifiers.first&.id || entity.short,
41
+ entity_name: entity.names.first&.value || entity.short,
67
42
  index: index,
68
43
  }
69
44
  end
70
45
  end
71
-
72
- # Find duplicates (URIs with more than one entity)
73
- qudt_refs.each do |uri, entities|
74
- next unless entities.size > 1
75
-
76
- # Record this duplicate
77
- duplicates[entity_type] ||= {}
78
- duplicates[entity_type][uri] = entities
79
- end
46
+ by_uri.reject { |_, refs| refs.size == 1 }
80
47
  end
81
48
 
82
- def display_duplicate_results(duplicates)
49
+ def display(duplicates)
83
50
  if duplicates.empty?
84
- puts "No duplicate QUDT references found! Each QUDT reference URI is used by at most one entity of each type."
51
+ puts "No duplicate QUDT references found! " \
52
+ "Each QUDT reference URI is used by at most one entity of each type."
85
53
  return
86
54
  end
87
55
 
88
56
  puts "Found duplicate QUDT references:"
89
-
90
57
  duplicates.each do |entity_type, uri_duplicates|
91
58
  puts "\n #{entity_type.capitalize}:"
92
-
93
- uri_duplicates.each do |uri, entities|
59
+ uri_duplicates.each do |uri, refs|
94
60
  puts " QUDT URI: #{uri}"
95
- puts " Used by #{entities.size} entities:"
96
-
97
- entities.each do |entity|
98
- puts " - #{entity[:entity_id]} (#{entity[:entity_name]}) at index #{entity[:index]}"
99
- end
61
+ puts " Used by #{refs.size} entities:"
62
+ refs.each { |r| puts " - #{r[:entity_id]} (#{r[:entity_name]}) at index #{r[:index]}" }
100
63
  puts ""
101
64
  end
102
65
  end
103
-
104
- puts "\nEach QUDT reference should be used by at most one entity of each type."
105
- puts "Please fix the duplicates by either removing the reference from all but one entity,"
106
- puts "or by updating the references to use different URIs appropriate for each entity."
107
66
  end
108
67
  end
109
68
  end