unitsml 0.4.7 → 0.6.0

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +8 -0
  3. data/.github/workflows/release.yml +7 -1
  4. data/.rubocop.yml +18 -0
  5. data/.rubocop_todo.yml +498 -0
  6. data/Gemfile +16 -11
  7. data/Rakefile +5 -3
  8. data/bin/console +4 -3
  9. data/docs/README.adoc +106 -4
  10. data/lib/unitsml/dimension.rb +49 -35
  11. data/lib/unitsml/errors/base_error.rb +8 -0
  12. data/lib/unitsml/errors/plurimath_load_error.rb +1 -1
  13. data/lib/unitsml/errors.rb +8 -0
  14. data/lib/unitsml/extender.rb +21 -16
  15. data/lib/unitsml/fenced.rb +97 -0
  16. data/lib/unitsml/fenced_numeric.rb +13 -0
  17. data/lib/unitsml/formula.rb +46 -33
  18. data/lib/unitsml/intermediate_exp_rules.rb +76 -0
  19. data/lib/unitsml/model/dimension.rb +4 -14
  20. data/lib/unitsml/model/dimension_quantities/quantity.rb +2 -0
  21. data/lib/unitsml/model/dimension_quantities.rb +17 -0
  22. data/lib/unitsml/model/prefix.rb +4 -8
  23. data/lib/unitsml/model/prefixes/name.rb +5 -4
  24. data/lib/unitsml/model/prefixes/symbol.rb +3 -2
  25. data/lib/unitsml/model/prefixes.rb +10 -0
  26. data/lib/unitsml/model/quantities/name.rb +4 -3
  27. data/lib/unitsml/model/quantities.rb +9 -0
  28. data/lib/unitsml/model/quantity.rb +5 -7
  29. data/lib/unitsml/model/unit.rb +4 -9
  30. data/lib/unitsml/model/units/enumerated_root_unit.rb +1 -0
  31. data/lib/unitsml/model/units/name.rb +4 -3
  32. data/lib/unitsml/model/units/root_units.rb +3 -2
  33. data/lib/unitsml/model/units/symbol.rb +2 -1
  34. data/lib/unitsml/model/units/system.rb +4 -3
  35. data/lib/unitsml/model/units.rb +13 -0
  36. data/lib/unitsml/model.rb +15 -0
  37. data/lib/unitsml/namespace.rb +8 -0
  38. data/lib/unitsml/number.rb +79 -0
  39. data/lib/unitsml/parse.rb +30 -38
  40. data/lib/unitsml/parser.rb +27 -13
  41. data/lib/unitsml/prefix.rb +17 -17
  42. data/lib/unitsml/sqrt.rb +3 -3
  43. data/lib/unitsml/transform.rb +143 -50
  44. data/lib/unitsml/unit.rb +67 -37
  45. data/lib/unitsml/unitsdb/dimension.rb +14 -20
  46. data/lib/unitsml/unitsdb/dimension_quantity.rb +2 -6
  47. data/lib/unitsml/unitsdb/dimensions.rb +3 -9
  48. data/lib/unitsml/unitsdb/prefix_reference.rb +23 -0
  49. data/lib/unitsml/unitsdb/prefixes.rb +17 -5
  50. data/lib/unitsml/unitsdb/quantities.rb +4 -4
  51. data/lib/unitsml/unitsdb/unit.rb +21 -0
  52. data/lib/unitsml/unitsdb/units.rb +19 -18
  53. data/lib/unitsml/unitsdb.rb +14 -5
  54. data/lib/unitsml/utility.rb +133 -103
  55. data/lib/unitsml/version.rb +3 -1
  56. data/lib/unitsml.rb +71 -35
  57. data/unitsdb/Gemfile +6 -0
  58. data/unitsdb/LICENSE.md +53 -0
  59. data/unitsdb/README.adoc +1253 -0
  60. data/unitsdb/RELEASE-NOTES.adoc +269 -0
  61. data/unitsdb/dimensions.yaml +1607 -602
  62. data/unitsdb/prefixes.yaml +842 -301
  63. data/unitsdb/quantities.yaml +3706 -2458
  64. data/unitsdb/scales.yaml +97 -0
  65. data/unitsdb/schemas/README.md +159 -0
  66. data/unitsdb/schemas/dimensions-schema.yaml +153 -0
  67. data/unitsdb/schemas/prefixes-schema.yaml +155 -0
  68. data/unitsdb/schemas/quantities-schema.yaml +117 -0
  69. data/unitsdb/schemas/scales-schema.yaml +106 -0
  70. data/unitsdb/schemas/unit_systems-schema.yaml +116 -0
  71. data/unitsdb/schemas/units-schema.yaml +215 -0
  72. data/unitsdb/spec/units_spec.rb +13 -10
  73. data/unitsdb/unit_systems.yaml +77 -15
  74. data/unitsdb/units.yaml +13517 -9974
  75. data/unitsdb/validate_schemas.rb +203 -0
  76. data/unitsml.gemspec +4 -1
  77. metadata +47 -7
  78. data/lib/unitsml/error.rb +0 -8
  79. data/unitsdb/docs/README.adoc +0 -12
  80. data/unitsdb/docs/navigation.adoc +0 -7
@@ -2,12 +2,10 @@
2
2
 
3
3
  module Unitsml
4
4
  module Unitsdb
5
- class Dimensions
6
- attr_accessor :dimensions
7
-
5
+ class Dimensions < ::Unitsdb::Dimensions
8
6
  def find_by_vector(vector)
9
- vectored
10
- find(:vector, vector)
7
+ dimensions.each(&:set_vector)
8
+ dimensions.find { |dim| dim.set_vector == vector }
11
9
  end
12
10
 
13
11
  def find_by_id(d_id)
@@ -26,10 +24,6 @@ module Unitsml
26
24
 
27
25
  private
28
26
 
29
- def vectored
30
- @vectored ||= dimensions.each(&:set_vector)
31
- end
32
-
33
27
  def find(field, matching_data)
34
28
  dimensions.find { |dim| dim.send(field) == matching_data }
35
29
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unitsml
4
+ module Unitsdb
5
+ class PrefixReference < ::Unitsdb::PrefixReference
6
+ def base
7
+ prefix.base
8
+ end
9
+
10
+ def symbolid
11
+ prefix.symbols.first.ascii
12
+ end
13
+
14
+ def power
15
+ prefix.power
16
+ end
17
+
18
+ def prefix
19
+ @prefix ||= Unitsdb.prefixes.find_by_id(id)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,19 +2,31 @@
2
2
 
3
3
  module Unitsml
4
4
  module Unitsdb
5
- class Prefixes
6
- attr_accessor :prefixes
5
+ class Prefixes < ::Unitsdb::Prefixes
6
+ def find_by_id(p_id)
7
+ find(p_id, :id, :identifiers)
8
+ end
7
9
 
8
10
  def find_by_symbol_name(ascii_sym)
9
- prefixes.find { |prefix| prefix.symbol.ascii == ascii_sym }
11
+ find(ascii_sym, :ascii, :symbols)
10
12
  end
11
13
 
12
14
  def ascii_symbols
13
15
  prefixes.each_with_object([]) do |prefix, names_array|
14
- symbol = prefix.symbol.ascii
16
+ symbol = prefix.symbols.map(&:ascii)
15
17
  next if symbol.empty?
16
18
 
17
- names_array << symbol
19
+ names_array.concat(symbol)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def find(matching_data, field, prefix_method)
26
+ prefixes.find do |prefix|
27
+ prefix.public_send(prefix_method.to_sym).find do |object|
28
+ object.public_send(field) == matching_data
29
+ end
18
30
  end
19
31
  end
20
32
  end
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Unitsml
4
4
  module Unitsdb
5
- class Quantities
6
- attr_accessor :quantities
7
-
5
+ class Quantities < ::Unitsdb::Quantities
8
6
  def find_by_id(q_id)
9
- quantities.find { |quantity| quantity.id == q_id }
7
+ quantities.find do |quantity|
8
+ quantity.identifiers.find { |id| id.id == q_id }
9
+ end
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unitsml
4
+ module Unitsdb
5
+ class Unit < ::Unitsdb::Unit
6
+ def dimension_url
7
+ quantity_id = quantity_references[0].id
8
+ quantity = Unitsdb.quantities.find_by_id(quantity_id)
9
+ quantity.dimension_reference.id
10
+ end
11
+
12
+ def en_name
13
+ names.find { |name| name.lang == 'en' }&.value
14
+ end
15
+
16
+ def nist_id
17
+ identifiers.find { |id| id.type == 'nist' }&.id
18
+ end
19
+ end
20
+ end
21
+ end
@@ -2,42 +2,43 @@
2
2
 
3
3
  module Unitsml
4
4
  module Unitsdb
5
- class Units
6
- attr_accessor :units
7
-
5
+ class Units < ::Unitsdb::Units
8
6
  def find_by_id(u_id)
9
- units.find { |unit| unit.id == u_id }
7
+ find(u_id, :id, :identifiers)
10
8
  end
11
9
 
12
10
  def find_by_name(u_name)
13
- units.find do |unit|
14
- unit.unit_symbols.find { |u_sym| u_sym.id == u_name }
15
- end
11
+ find(u_name, :id, :symbols)
16
12
  end
17
13
 
18
14
  def filtered
19
- @filtered ||= units_symbol_ids.reject do |unit|
20
- ((/\*|\^|\/|^1$/).match?(unit) || find_by_symbol_id(unit).prefixed)
15
+ @filtered ||= symbols_ids.reject do |unit|
16
+ %r{\*|\^|/|^1$}.match?(unit) || find_by_symbol_id(unit).prefixed
21
17
  end
22
18
  end
23
19
 
24
20
  def find_by_symbol_id(sym_id)
25
- units_symbol_hash[sym_id]
21
+ symbols_hash[sym_id]
26
22
  end
27
23
 
28
- def units_symbol_ids
29
- units_symbol_hash.keys
24
+ def symbols_ids
25
+ symbols_hash.keys
30
26
  end
31
27
 
32
- def units_symbol_hash
33
- @symbol_ids_hash ||= {}
28
+ def symbols_hash
29
+ @symbols_hash ||= units.each_with_object({}) do |unit, object|
30
+ unit.symbols&.each { |unit_sym| object[unit_sym.id] = unit }
31
+ end
32
+ end
33
+
34
+ private
34
35
 
35
- if @symbol_ids_hash.empty?
36
- units.each_with_object(@symbol_ids_hash) do |unit, object|
37
- unit.unit_symbols.each { |unit_sym| object[unit_sym.id] = unit }
36
+ def find(matching_data, field, unit_method)
37
+ units.find do |unit|
38
+ unit.public_send(unit_method.to_sym).find do |id|
39
+ id.public_send(field) == matching_data
38
40
  end
39
41
  end
40
- @symbol_ids_hash
41
42
  end
42
43
  end
43
44
  end
@@ -2,6 +2,15 @@
2
2
 
3
3
  module Unitsml
4
4
  module Unitsdb
5
+ autoload :Unit, "#{__dir__}/unitsdb/unit"
6
+ autoload :Units, "#{__dir__}/unitsdb/units"
7
+ autoload :Prefixes, "#{__dir__}/unitsdb/prefixes"
8
+ autoload :Dimension, "#{__dir__}/unitsdb/dimension"
9
+ autoload :Dimensions, "#{__dir__}/unitsdb/dimensions"
10
+ autoload :Quantities, "#{__dir__}/unitsdb/quantities"
11
+ autoload :PrefixReference, "#{__dir__}/unitsdb/prefix_reference"
12
+ autoload :DimensionQuantity, "#{__dir__}/unitsdb/dimension_quantity"
13
+
5
14
  class << self
6
15
  def load_file(file_name)
7
16
  @@hash ||= {}
@@ -9,19 +18,19 @@ module Unitsml
9
18
  end
10
19
 
11
20
  def units
12
- @@units_file ||= ::Unitsdb::Units.from_yaml(load_file(:units))
21
+ @@units_file ||= Unitsml.get_class_from_register(:unitsdb_units).from_yaml(load_file(:units))
13
22
  end
14
23
 
15
24
  def prefixes
16
- @@prefixes ||= ::Unitsdb::Prefixes.from_yaml(load_file(:prefixes))
25
+ @@prefixes ||= Unitsml.get_class_from_register(:unitsdb_prefixes).from_yaml(load_file(:prefixes))
17
26
  end
18
27
 
19
28
  def dimensions
20
- @@dim_file ||= ::Unitsdb::Dimensions.from_yaml(load_file(:dimensions))
29
+ @@dim_file ||= Unitsml.get_class_from_register(:unitsdb_dimensions).from_yaml(load_file(:dimensions))
21
30
  end
22
31
 
23
32
  def quantities
24
- @@quantities ||= ::Unitsdb::Quantities.from_yaml(load_file(:quantities))
33
+ @@quantities ||= Unitsml.get_class_from_register(:unitsdb_quantities).from_yaml(load_file(:quantities))
25
34
  end
26
35
 
27
36
  def prefixes_array
@@ -37,7 +46,7 @@ module Unitsml
37
46
 
38
47
  def valid_path(file_name)
39
48
  File.expand_path(
40
- File.join(__dir__, "..", "..","unitsdb", "#{file_name}.yaml")
49
+ File.join(__dir__, '..', '..', 'unitsdb', "#{file_name}.yaml")
41
50
  )
42
51
  end
43
52
  end
@@ -1,37 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "unitsml/model/unit"
4
- require "unitsml/model/prefix"
5
- require "unitsml/model/quantity"
6
- require "unitsml/model/dimension"
7
-
8
3
  module Unitsml
9
4
  module Utility
10
5
  # Unit to dimension
11
6
  U2D = {
12
- "m" => { dimension: "Length", order: 1, symbol: "L" },
13
- "g" => { dimension: "Mass", order: 2, symbol: "M" },
14
- "kg" => { dimension: "Mass", order: 2, symbol: "M" },
15
- "s" => { dimension: "Time", order: 3, symbol: "T" },
16
- "A" => { dimension: "ElectricCurrent", order: 4, symbol: "I" },
17
- "K" => { dimension: "ThermodynamicTemperature", order: 5,
18
- symbol: "Theta" },
19
- "degK" => { dimension: "ThermodynamicTemperature", order: 5,
20
- symbol: "Theta" },
21
- "mol" => { dimension: "AmountOfSubstance", order: 6, symbol: "N" },
22
- "cd" => { dimension: "LuminousIntensity", order: 7, symbol: "J" },
23
- "deg" => { dimension: "PlaneAngle", order: 8, symbol: "phi" },
7
+ 'm' => { dimension: 'Length', order: 1, symbol: 'L' },
8
+ 'g' => { dimension: 'Mass', order: 2, symbol: 'M' },
9
+ 'kg' => { dimension: 'Mass', order: 2, symbol: 'M' },
10
+ 's' => { dimension: 'Time', order: 3, symbol: 'T' },
11
+ 'A' => { dimension: 'ElectricCurrent', order: 4, symbol: 'I' },
12
+ 'K' => { dimension: 'ThermodynamicTemperature', order: 5,
13
+ symbol: 'Theta' },
14
+ 'degK' => { dimension: 'ThermodynamicTemperature', order: 5,
15
+ symbol: 'Theta' },
16
+ 'mol' => { dimension: 'AmountOfSubstance', order: 6, symbol: 'N' },
17
+ 'cd' => { dimension: 'LuminousIntensity', order: 7, symbol: 'J' },
18
+ 'deg' => { dimension: 'PlaneAngle', order: 8, symbol: 'phi' }
24
19
  }.freeze
25
20
  # Dimesion for dim_(dimesion) input
26
- Dim2D = {
27
- "dim_L" => U2D["m"],
28
- "dim_M" => U2D["g"],
29
- "dim_T" => U2D["s"],
30
- "dim_I" => U2D["A"],
31
- "dim_Theta" => U2D["K"],
32
- "dim_N" => U2D["mol"],
33
- "dim_J" => U2D["cd"],
34
- "dim_phi" => U2D["deg"],
21
+ DIM2D = {
22
+ 'dim_L' => U2D['m'],
23
+ 'dim_M' => U2D['g'],
24
+ 'dim_T' => U2D['s'],
25
+ 'dim_I' => U2D['A'],
26
+ 'dim_Theta' => U2D['K'],
27
+ 'dim_N' => U2D['mol'],
28
+ 'dim_J' => U2D['cd'],
29
+ 'dim_phi' => U2D['deg']
35
30
  }.freeze
36
31
  DIMS_VECTOR = %w[
37
32
  ThermodynamicTemperature
@@ -44,6 +39,8 @@ module Unitsml
44
39
  Time
45
40
  ].freeze
46
41
 
42
+ UNKNOWN = 'unknown'
43
+
47
44
  class << self
48
45
  def unit_instance(unit)
49
46
  Unitsdb.units.find_by_symbol_id(unit)
@@ -55,7 +52,7 @@ module Unitsml
55
52
 
56
53
  def units2dimensions(units)
57
54
  norm = decompose_units_list(units)
58
- return if norm.any? { |u| u.nil? || u[:unit].unit_name == "unknown" || u[:prefix] == "unknown" }
55
+ return if norm.any? { |u| u.nil? || u[:unit]&.unknown_name? || u[:prefix] == UNKNOWN }
59
56
 
60
57
  norm.map do |u|
61
58
  unit_name = u[:unit].unit_name
@@ -63,7 +60,7 @@ module Unitsml
63
60
  dimension: U2D[unit_name][:dimension],
64
61
  unit: unit_name,
65
62
  exponent: u[:unit].power_numerator || 1,
66
- symbol: U2D[unit_name][:symbol],
63
+ symbol: U2D[unit_name][:symbol]
67
64
  }
68
65
  end.sort { |a, b| U2D[a[:unit]][:order] <=> U2D[b[:unit]][:order] }
69
66
  end
@@ -72,12 +69,20 @@ module Unitsml
72
69
  return nil if dims.nil? || dims.empty?
73
70
 
74
71
  dim_hash = dims.each_with_object({}) { |h, m| m[h[:dimension]] = h }
75
- dims_vector = DIMS_VECTOR.map { |h| dim_hash.dig(h, :exponent) }.join(":")
72
+ dims_vector = DIMS_VECTOR.map { |h| dim_hash.dig(h, :exponent) }.join(':')
76
73
  id = Unitsdb.dimensions.find_by_vector(dims_vector)&.id and return id.to_s
77
- "D_" + dims.map do |d|
78
- (U2D.dig(d[:unit], :symbol) || Dim2D.dig(d[:id], :symbol)) +
79
- (d[:exponent] == 1 ? "" : float_to_display(d[:exponent]))
80
- end.join("")
74
+
75
+ 'D_' + dims.map do |d|
76
+ (U2D.dig(d[:unit], :symbol) || DIM2D.dig(d[:id], :symbol)) +
77
+ (to_i_value(d[:exponent]) == 1 ? '' : float_to_display(d[:exponent]))
78
+ end.join('')
79
+ end
80
+
81
+ def to_i_value(object)
82
+ case object
83
+ when Integer, Float then object
84
+ when Number, Fenced then object.to_i
85
+ end
81
86
  end
82
87
 
83
88
  def decompose_units_list(units)
@@ -85,22 +90,21 @@ module Unitsml
85
90
  end
86
91
 
87
92
  def decompose_unit(u)
88
- if u&.unit_name == "g" || u.system_type == "SI_base"
93
+ if u&.unit_name == 'g' || Lutaml::Model::Utils.snake_case(u.system_type) == 'si_base'
89
94
  { unit: u, prefix: u&.prefix }
90
95
  elsif u.si_derived_bases.nil? || u.si_derived_bases.empty?
91
- { unit: Unit.new("unknown") }
96
+ { unit: Unit.new(UNKNOWN) }
92
97
  else
93
98
  u.si_derived_bases.each_with_object([]) do |k, object|
94
- prefix = if !k.prefix.nil? && !k.prefix.empty?
95
- combine_prefixes(prefix_object(k.prefix), u.prefix)
99
+ prefix = if !k.prefix_reference.nil?
100
+ combine_prefixes(prefix_object(k.prefix_reference), u.prefix)
96
101
  else
97
102
  u.prefix
98
103
  end
99
- unit_name = Unitsdb.units.find_by_id(k.id).unit_symbols.first.id
104
+ unit_name = Unitsdb.units.find_by_id(k.unit_reference.id).symbols.first.id
100
105
  exponent = (k.power&.to_i || 1) * (u.power_numerator&.to_f || 1)
101
106
  object << { prefix: prefix,
102
- unit: Unit.new(unit_name, exponent, prefix: prefix),
103
- }
107
+ unit: Unit.new(unit_name, exponent, prefix: prefix) }
104
108
  end
105
109
  end
106
110
  end
@@ -110,15 +114,24 @@ module Unitsml
110
114
  if m.empty? || m[-1][:unit]&.unit_name != k[:unit]&.unit_name
111
115
  m << k
112
116
  else
113
- m[-1][:unit]&.power_numerator = (k[:unit]&.power_numerator&.to_f || 1) + (m[-1][:unit]&.power_numerator&.to_f || 1)
117
+ m[-1][:unit]&.power_numerator = Number.new(numerator_value(k, m))
114
118
  m[-1] = {
115
119
  prefix: combine_prefixes(prefix_object(m[-1][:prefix]), prefix_object(k[:prefix])),
116
- unit: m[-1][:unit],
120
+ unit: m[-1][:unit]
117
121
  }
118
122
  end
119
123
  end
120
124
  end
121
125
 
126
+ def numerator_value(unit_hash, unit_array)
127
+ unit_numerator_float(unit_hash) +
128
+ unit_numerator_float(unit_array[-1])
129
+ end
130
+
131
+ def unit_numerator_float(object_hash)
132
+ object_hash[:unit]&.power_numerator&.to_f || 1
133
+ end
134
+
122
135
  def prefix_object(prefix)
123
136
  return prefix unless prefix.is_a?(String)
124
137
  return nil unless Unitsdb.prefixes_array.any?(prefix)
@@ -130,76 +143,68 @@ module Unitsml
130
143
  return nil if p1.nil? && p2.nil?
131
144
  return p1.symbolid if p2.nil?
132
145
  return p2.symbolid if p1.nil?
133
- return "unknown" if p1.base != p2.base
146
+ return UNKNOWN if p1.base != p2.base
134
147
 
135
148
  Unitsdb.prefixes_array.each do |prefix_name|
136
149
  p = prefix_object(prefix_name)
137
150
  return p if p.base == p1.base && p.power == p1.power + p2.power
138
151
  end
139
152
 
140
- "unknown"
153
+ UNKNOWN
141
154
  end
142
155
 
143
156
  def unit(units, formula, dims, norm_text, name, options)
144
157
  attributes = {
145
158
  id: unit_id(norm_text),
146
159
  system: unitsystem(units),
147
- name: unitname(units, norm_text, name),
160
+ name: unitname(norm_text, name),
148
161
  symbol: unitsymbols(formula, options),
149
- root_units: rootunits(units),
162
+ root_units: rootunits(units)
150
163
  }
151
164
  attributes[:dimension_url] = "##{dim_id(dims)}" if dims
152
165
  Model::Unit.new(attributes).to_xml
153
- .gsub("&lt;", "<")
154
- .gsub("&gt;", ">")
155
- .gsub("&amp;", "&")
156
- .gsub(/−/, "&#x2212;")
157
- .gsub(/⋅/, "&#x22c5;")
158
- end
159
-
160
- def unitname(units, text, name)
161
- Model::Units::Name.new(
162
- name: unit_instance(text)&.unit_name&.first || text
163
- )
164
- end
165
-
166
- def postprocess_normtext(units)
167
- units.map { |u| "#{u.prefix_name}#{u.unit_name}#{display_exp(u)}" }.join("*")
166
+ .force_encoding('UTF-8')
167
+ .gsub('&lt;', '<')
168
+ .gsub('&gt;', '>')
169
+ .gsub('&amp;', '&')
170
+ .gsub(/−/, '&#x2212;')
171
+ .gsub(/⋅/, '&#x22c5;')
168
172
  end
169
173
 
170
- def display_exp(unit)
171
- unit.power_numerator && unit.power_numerator != "1" ? "^#{unit.power_numerator}" : ""
174
+ def unitname(text, name)
175
+ name ||= unit_instance(text)&.en_name || text
176
+ Model::Units::Name.new(name: name)
172
177
  end
173
178
 
174
179
  def unitsymbols(formula, options)
175
180
  %w[HTML MathMl].map do |lang|
176
- Model::Units::Symbol.new(type: lang, content: formula.public_send(:"to_#{lang.downcase}", options))
181
+ Model::Units::Symbol.new(
182
+ type: lang,
183
+ content: formula.public_send(:"to_#{lang.downcase}", options)
184
+ )
177
185
  end
178
186
  end
179
187
 
180
188
  def unitsystem(units)
181
189
  ret = []
182
- if units.any? { |u| u.system_name != "SI" }
183
- ret << Model::Units::System.new(name: "not_SI", type: "not_SI")
184
- end
185
- if units.any? { |u| u.system_name == "SI" }
190
+ ret << Model::Units::System.new(name: 'not_SI', type: 'not_SI') if units.any? { |u| !u.si_system_type? }
191
+ if units.any?(&:si_system_type?)
186
192
  if units.size == 1
187
- base = units[0].system_type == "SI-base"
188
- base = true if units[0].unit_name == "g" && units[0]&.prefix_name == "k"
193
+ base = units[0].downcase_system_type == 'si_base'
194
+ base = true if units[0].unit_name == 'g' && units[0]&.prefix_name == 'k'
189
195
  end
190
- ret << Model::Units::System.new(name: "SI", type: (base ? 'SI_base' : 'SI_derived'))
196
+ ret << Model::Units::System.new(name: 'SI', type: (base ? 'SI_base' : 'SI_derived'))
191
197
  end
192
198
  ret
193
199
  end
194
200
 
195
201
  def dimension(norm_text)
196
- dim_url = unit_instance(norm_text)&.dimension_url
197
- return unless dim_url
202
+ dim_id = unit_instance(norm_text)&.dimension_url
203
+ return unless dim_id
198
204
 
199
- dim_id = dim_url.sub("#", '')
200
205
  dim_attrs = { id: dim_id }
201
206
  dimid2dimensions(dim_id)&.compact&.each { |u| dimension1(u, dim_attrs) }
202
- Model::Dimension.new(dim_attrs).to_xml
207
+ Model::Dimension.new(dim_attrs).to_xml.force_encoding('UTF-8')
203
208
  end
204
209
 
205
210
  def dimension1(dim, dims_hash)
@@ -212,26 +217,31 @@ module Unitsml
212
217
  end
213
218
 
214
219
  def float_to_display(float)
215
- float.to_f.round(1).to_s.sub(/\.0$/, "")
220
+ case float
221
+ when Integer, Float
222
+ float.to_f.round(1).to_s.sub(/\.0$/, '')
223
+ when Number, Fenced
224
+ float.float_to_display
225
+ end
216
226
  end
217
227
 
218
228
  def dimid2dimensions(normtext)
219
229
  dims = Unitsdb.dimensions.find_by_id(normtext)
220
230
  dims&.processed_keys&.map do |processed_key|
221
- humanized = processed_key.split("_").map(&:capitalize).join
231
+ humanized = processed_key.split('_').map(&:capitalize).join
222
232
  next unless DIMS_VECTOR.include?(humanized)
223
233
 
224
234
  dim_quantity = dims.public_send(processed_key)
225
235
  {
226
236
  dimension: humanized,
227
237
  symbol: dim_quantity.symbol,
228
- exponent: dim_quantity.power_numerator,
238
+ exponent: dim_quantity.power
229
239
  }
230
240
  end
231
241
  end
232
242
 
233
243
  def prefixes(units, options)
234
- uniq_prefixes = units.map { |unit| unit.prefix }.compact.uniq {|d| d.prefix_name }
244
+ uniq_prefixes = units.map(&:prefix).compact.uniq(&:prefix_name)
235
245
  uniq_prefixes.map do |prefix|
236
246
  prefix_attrs = { prefix_base: prefix&.base, prefix_power: prefix&.power, id: prefix&.id }
237
247
  type_and_methods = { ASCII: :to_asciimath, unicode: :to_unicode, LaTeX: :to_latex, HTML: :to_html }
@@ -239,10 +249,10 @@ module Unitsml
239
249
  prefix_attrs[:symbol] = type_and_methods.map do |type, method_name|
240
250
  Model::Prefixes::Symbol.new(
241
251
  type: type,
242
- content: prefix&.public_send(method_name, options),
252
+ content: prefix&.public_send(method_name, options)
243
253
  )
244
254
  end
245
- Model::Prefix.new(prefix_attrs).to_xml.gsub("&amp;", "&")
255
+ Model::Prefix.new(prefix_attrs).to_xml.force_encoding('UTF-8').gsub('&amp;', '&')
246
256
  end.join("\n")
247
257
  end
248
258
 
@@ -252,18 +262,24 @@ module Unitsml
252
262
  enum_root_units = units.map do |unit|
253
263
  attributes = { unit: unit.enumerated_name }
254
264
  attributes[:prefix] = unit.prefix_name if unit.prefix
255
- unit.power_numerator && unit.power_numerator != "1" and
256
- attributes[:power_numerator] = unit.power_numerator
265
+ unit.power_numerator && unit.power_numerator != '1' and
266
+ attributes[:power_numerator] = unit.power_numerator.raw_value
257
267
  Model::Units::EnumeratedRootUnit.new(attributes)
258
268
  end
259
269
  Model::Units::RootUnits.new(enumerated_root_unit: enum_root_units)
260
270
  end
261
271
 
262
272
  def unit_id(text)
263
- norm_text = text
264
- text = text&.gsub(/[()]/, "")
265
- unit = unit_instance(norm_text)
266
- "U_#{unit ? unit.id&.gsub(/'/, '_') : norm_text&.gsub(/\*/, '.')&.gsub(/\^/, '')}"
273
+ text = text&.gsub(/[()]/, '')
274
+ unit = unit_instance(text)
275
+
276
+ format_unit_id(unit, text)&.insert(0, 'U_')
277
+ end
278
+
279
+ def format_unit_id(unit, text)
280
+ return unit.nist_id&.gsub(/'/, '_') if unit
281
+
282
+ text&.gsub(/\*/, '.')&.gsub(/\^/, '')
267
283
  end
268
284
 
269
285
  def dimension_components(dims)
@@ -271,45 +287,59 @@ module Unitsml
271
287
 
272
288
  dim_attrs = { id: dim_id(dims) }
273
289
  dims.map { |u| dimension1(u, dim_attrs) }
274
- Model::Dimension.new(dim_attrs).to_xml
290
+ Model::Dimension.new(dim_attrs).to_xml.force_encoding('UTF-8')
275
291
  end
276
292
 
277
- def quantity(normtext, quantity)
293
+ def quantity(normtext, instance)
278
294
  unit = unit_instance(normtext)
279
- return unless unit && unit.quantity_reference.size == 1 ||
295
+ return unless unit_or_quantity(unit, instance)
296
+
297
+ model_quantity_xml(
298
+ instance || unit.quantity_references&.first&.id,
299
+ "##{unit.dimension_url}"
300
+ )
301
+ end
302
+
303
+ def unit_or_quantity(unit, quantity)
304
+ unit && unit.quantity_references.size == 1 ||
280
305
  quantity_instance(quantity)
306
+ end
281
307
 
282
- id = (quantity || unit.quantity_reference&.first&.url).sub('#', '')
283
- dim_url = unit.dimension_url
284
- attributes = { id: id, name: quantity_name(id), dimension_url: dim_url }
285
- Model::Quantity.new(attributes).to_xml
308
+ def model_quantity_xml(id, url)
309
+ Model::Quantity.new(
310
+ id: id,
311
+ name: quantity_name(id),
312
+ dimension_url: url
313
+ ).to_xml.force_encoding('UTF-8')
286
314
  end
287
315
 
288
316
  def quantity_name(id)
289
- quantity_instance(id)&.quantity_name&.map do |content|
290
- Model::Quantities::Name.new(content: content)
317
+ quantity_instance(id)&.names&.filter_map do |name|
318
+ next unless name.lang == 'en'
319
+
320
+ Model::Quantities::Name.new(content: name.value)
291
321
  end
292
322
  end
293
323
 
294
324
  def string_to_html_entity(string)
295
- entities = HTMLEntities.new
296
- entities.encode(
325
+ HTMLEntities.new.encode(
297
326
  string.frozen? ? string : string.force_encoding('UTF-8'),
298
- :hexadecimal,
327
+ :hexadecimal
299
328
  )
300
329
  end
301
330
 
302
331
  def html_entity_to_unicode(string)
303
- entities = HTMLEntities.new
304
- entities.decode(string)
332
+ HTMLEntities.new.decode(string)
305
333
  end
306
334
 
307
335
  def underscore(str)
308
336
  str.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
309
337
  end
310
338
 
311
- def class_name(klass)
312
- klass.name.split("::").last
339
+ def set_to_fence(set)
340
+ return set if set.is_a?(Fenced)
341
+
342
+ Fenced.new('(', set, ')')
313
343
  end
314
344
  end
315
345
  end