dimensional 1.1.1 → 2.0.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.
- data/CHANGELOG +2 -1
- data/TODO +4 -1
- data/lib/dimensional.rb +1 -0
- data/lib/dimensional/locale.rb +42 -0
- data/lib/dimensional/metric.rb +33 -25
- data/lib/dimensional/system.rb +6 -4
- data/lib/dimensional/unit.rb +1 -1
- data/lib/dimensional/version.rb +1 -1
- data/test/demo.rb +36 -20
- data/test/dimensional_test.rb +13 -11
- data/test/locale_test.rb +64 -0
- data/test/metric_test.rb +22 -25
- data/test/system_test.rb +2 -2
- metadata +5 -2
data/CHANGELOG
CHANGED
@@ -3,4 +3,5 @@
|
|
3
3
|
0.1.2 Cleanup tests to work with gem check -t.
|
4
4
|
1.0.0 Production Release 1.
|
5
5
|
1.0.1 Use Rationals for default values and avoid the dreaded integer division = 0 problem.
|
6
|
-
1.1.1 Add Metric.load constructor, improve docs and enhance scanner.
|
6
|
+
1.1.1 Add Metric.load constructor, improve docs and enhance scanner.
|
7
|
+
2.0.0 Introduce Dimensional::Locale
|
data/TODO
CHANGED
data/lib/dimensional.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Dimensional
|
2
|
+
# A Locale is a prioritized list of Systems per Metric.
|
3
|
+
# Locales solve the problem of parsing ambiguous units such as US gallons and Imperial gallons. They also allow
|
4
|
+
# unit systems to be prioritized for specific metrics. For example, in the domain of aeronautics, the "international
|
5
|
+
# nautical mile" is used universally for distances and "knots" are used for speed. For these metrics, an "International"
|
6
|
+
# system should have the highest priority when parsing ambiguous units and when formatting output.
|
7
|
+
class Locale
|
8
|
+
class << self
|
9
|
+
# The default locale, which is used when auto-creating locales.
|
10
|
+
def default
|
11
|
+
@default ||= self.new(:DEFAULT)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create a new locale and define a constant for it
|
15
|
+
def register(name, ss = Array.new)
|
16
|
+
l = new(name, ss)
|
17
|
+
const_set(l.to_s.to_sym, l)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new locale (using systems from the default locale) and define a constant for it
|
21
|
+
def const_missing(symbol)
|
22
|
+
register(symbol, default.systems.dup) # Copy default's systems for independence
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset!
|
26
|
+
@default = nil
|
27
|
+
constants.each {|d| remove_const(d)}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :systems
|
32
|
+
# Locales have a prioritized list of systems and a name.
|
33
|
+
def initialize(name, ss = Array.new)
|
34
|
+
@name = name
|
35
|
+
@systems = ss
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
@name.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/dimensional/metric.rb
CHANGED
@@ -8,21 +8,25 @@ module Dimensional
|
|
8
8
|
# A Measure string is composed of a number followed by a unit separated by optional whitespace.
|
9
9
|
# A unit (optional) is composed of a non-digit character followed by zero or more word characters and terminated by some stuff.
|
10
10
|
# Scientific notation is not currently supported.
|
11
|
+
# TODO: Move this to a locale
|
11
12
|
NUMERIC_REGEXP = /((?=\d|\.\d)\d*(?:\.\d*)?)\s*(\D.*?)?\s*(?=\d|$)/
|
12
13
|
|
13
14
|
class << self
|
14
|
-
attr_accessor :dimension, :base, :default
|
15
|
+
attr_accessor :dimension, :base, :default, :universal_systems
|
15
16
|
|
16
|
-
# The units
|
17
|
+
# The units of this metric, grouped by system.
|
17
18
|
def units
|
18
|
-
@units ||= Unit.select{|u| u.dimension == dimension}.
|
19
|
+
@units ||= Hash.new([]).merge(Unit.select{|u| u.dimension == dimension}.group_by{|u| u.system})
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def systems(locale)
|
23
|
+
locale.systems.dup.unshift(*(universal_systems || [])).uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
# Find the unit matching the given string, preferring units in the given locale
|
27
|
+
def find_unit(str, locale = Locale.default)
|
28
|
+
us = systems(locale).inject([]){|us, system| us + units[system].sort_by{|u| configuration[u][:preference]}.reverse}
|
29
|
+
us.detect{|u| configuration[u][:detector].match(str.to_s)}
|
26
30
|
end
|
27
31
|
|
28
32
|
def configuration
|
@@ -35,19 +39,16 @@ module Dimensional
|
|
35
39
|
@dimension ||= unit.dimension
|
36
40
|
@base ||= unit
|
37
41
|
@default ||= unit
|
38
|
-
@units = nil
|
39
42
|
raise "Unit #{unit} is not compatible with dimension #{dimension || '<nil>'}." unless unit.dimension == dimension
|
40
43
|
configuration[unit] = {:detector => unit.detector, :format => unit.format, :preference => unit.preference * 1.01}.merge(options)
|
41
44
|
end
|
42
45
|
|
43
|
-
# Parse a string into a Metric instance. Providing a
|
46
|
+
# Parse a string into a Metric instance. Providing a locale will help resolve ambiguities.
|
44
47
|
# Unrecognized strings return nil.
|
45
|
-
def parse(str,
|
46
|
-
system = System[system] unless system.kind_of?(System)
|
48
|
+
def parse(str, locale = Locale.default)
|
47
49
|
elements = str.to_s.scan(NUMERIC_REGEXP).map do |(v, us)|
|
48
|
-
unit = us.nil? ? default : find_unit(us,
|
50
|
+
unit = us.nil? ? default : find_unit(us, locale)
|
49
51
|
raise ArgumentError, "Unit cannot be determined (#{us})" unless unit
|
50
|
-
system = unit.system # Set the system to restrict subsequent filtering
|
51
52
|
value = Integer(v) rescue Float(v)
|
52
53
|
new(value, unit)
|
53
54
|
end
|
@@ -60,12 +61,16 @@ module Dimensional
|
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
|
-
# Sort units by "best" fit for the desired order of magnitude. Preference values offset OOM differences.
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
# Sort units by "best" fit for the desired order of magnitude. Preference values offset OOM differences. There is
|
65
|
+
# a bias in favor of negative OOM differences (humans like 6" more than 0.5ft).
|
66
|
+
def best_fit(target_oom, system)
|
67
|
+
us = units[system]
|
68
|
+
us = us.sort_by do |u|
|
69
|
+
oom_delta = Math.log10(u.factor) - target_oom
|
70
|
+
avoid_fraction_bonus = (oom_delta > 0.0 ? 0 : 1.5)
|
71
|
+
(configuration[u][:preference] - oom_delta.abs) + avoid_fraction_bonus
|
68
72
|
end
|
73
|
+
us.last
|
69
74
|
end
|
70
75
|
|
71
76
|
# Create a new instance with the given value (assumed to be in the base unit) and convert it to the preferred unit.
|
@@ -75,7 +80,7 @@ module Dimensional
|
|
75
80
|
end
|
76
81
|
|
77
82
|
attr_reader :unit
|
78
|
-
def initialize(value, unit = self.class.default)
|
83
|
+
def initialize(value, unit = self.class.default || self.class.base)
|
79
84
|
raise ArgumentError, "No default unit set" unless unit
|
80
85
|
@unit = unit
|
81
86
|
super(value)
|
@@ -87,20 +92,23 @@ module Dimensional
|
|
87
92
|
self.class.new(new_value, new_unit)
|
88
93
|
end
|
89
94
|
|
90
|
-
# Convert
|
95
|
+
# Convert into the "most appropriate" unit in the given system. A similar order-of-magnitude for the result is preferred.
|
91
96
|
def change_system(system)
|
92
97
|
system = System[system] unless system.kind_of?(System)
|
93
98
|
target_oom = Math.log10(self.unit.factor)
|
94
|
-
bu = self.class.best_fit(target_oom
|
99
|
+
bu = self.class.best_fit(target_oom, system)
|
95
100
|
convert(bu)
|
96
101
|
end
|
97
102
|
|
98
|
-
# Convert
|
99
|
-
|
103
|
+
# Convert into the best unit for the given Locale. The first system of the given locale with units is elected the preferred system,
|
104
|
+
# and within the preferred system, preference is given to units yielding a metric whose order of magnitude is close to zero.
|
105
|
+
def localize(locale = Locale.default)
|
100
106
|
target_oom = Math.log10(self) + Math.log10(self.unit.factor)
|
101
|
-
|
107
|
+
preferred_system = self.class.systems(locale).detect{ |s| self.class.units[s].any? }
|
108
|
+
bu = self.class.best_fit(target_oom, preferred_system)
|
102
109
|
convert(bu)
|
103
110
|
end
|
111
|
+
alias preferred localize
|
104
112
|
|
105
113
|
# Return a new metric expressed in the base unit
|
106
114
|
def base
|
data/lib/dimensional/system.rb
CHANGED
@@ -15,13 +15,15 @@ module Dimensional
|
|
15
15
|
const_set(s.abbreviation, s) rescue nil # Not all symbols strings are valid constant names
|
16
16
|
s
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
# Lookup the system by name or abbreviation
|
20
20
|
def self.[](sym)
|
21
|
-
|
22
|
-
@abbreviation_registry[sym] || @registry[sym]
|
21
|
+
sym = sym && sym.to_sym
|
22
|
+
s = @abbreviation_registry[sym] || @registry[sym]
|
23
|
+
raise "Unknown system #{sym.to_s}" unless s
|
24
|
+
s
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
# Purge all systems from storage.
|
26
28
|
def self.reset!
|
27
29
|
constants.each {|d| remove_const(d)}
|
data/lib/dimensional/unit.rb
CHANGED
@@ -52,7 +52,7 @@ module Dimensional
|
|
52
52
|
@reference_units = options[:reference_units] || {}
|
53
53
|
@abbreviation = options[:abbreviation]
|
54
54
|
@detector = options[:detector] || /\A#{[name, abbreviation].compact.join('|')}\Z/
|
55
|
-
@format = options[:format] ||
|
55
|
+
@format = options[:format] || "%s %U"
|
56
56
|
@preference = options[:preference] || 0
|
57
57
|
validate
|
58
58
|
end
|
data/lib/dimensional/version.rb
CHANGED
data/test/demo.rb
CHANGED
@@ -17,6 +17,7 @@ Dimension.register('Volume', 'V', {Dimension::L => 3})
|
|
17
17
|
Dimension.register('Velocity', 'Vel', {Dimension::L => 1, Dimension::T => -1})
|
18
18
|
Dimension.register('Acceleration', 'Acc', {Dimension::L => 1, Dimension::T => -2})
|
19
19
|
Dimension.register('Force', 'F', {Dimension::M => 1, Dimension::L => 1, Dimension::T => -2})
|
20
|
+
Dimension.register('Pressure', 'Press', {Dimension::M => 1, Dimension::L => -1, Dimension::T => -2})
|
20
21
|
Dimension.register('Torque', 'τ', {Dimension::M => 1, Dimension::L => 2, Dimension::T => -2}) # equivalent to Energy
|
21
22
|
Dimension.register('Energy', 'E', {Dimension::M => 1, Dimension::L => 2, Dimension::T => -2}) # a.k.a. work
|
22
23
|
Dimension.register('Power', 'P', {Dimension::M => 1, Dimension::L => 2, Dimension::T => -3})
|
@@ -24,23 +25,27 @@ Dimension.register('Voltage', 'emf', {Dimension::M => 1, Dimension::L => 2, Dime
|
|
24
25
|
|
25
26
|
# Define common Systems of Measurement
|
26
27
|
System.register('SI - International System (kg, tonne, m)', 'SI')
|
28
|
+
System.register('Universal', 'U') # International standards not included in SI
|
27
29
|
System.register('US Customary (lbs, ton, ft)', 'US') # http://en.wikipedia.org/wiki/United_States_customary_units
|
28
30
|
System.register('US Customary Troy (oz)', 'USt') # http://en.wikipedia.org/wiki/United_States_customary_units
|
29
31
|
System.register('British Imperial (lbs, ton, ft)', 'Imp') # http://en.wikipedia.org/wiki/Imperial_units
|
30
32
|
System.register('British Admiralty', 'BA')
|
31
33
|
System.register('Foot-Pound-Second', 'FPS') #http://en.wikipedia.org/wiki/Foot-pound-second_system
|
34
|
+
System.register('Furlong/Firkin/Fortnight', 'FFF') # http://en.wikipedia.org/wiki/FFF_System
|
32
35
|
|
33
36
|
Configurator.start do
|
34
37
|
dimension(:L) do
|
35
38
|
system(:SI) do
|
36
39
|
base('meter', 'm', :detector => /\A(met(er|re)s?|m)\Z/) do
|
37
|
-
derive('decimeter', 'dm', Rational(1, 10), :detector => /\A(decimet(er|re)s?|dm)\Z/, :preference =>
|
40
|
+
derive('decimeter', 'dm', Rational(1, 10), :detector => /\A(decimet(er|re)s?|dm)\Z/, :preference => -3)
|
38
41
|
derive('centimeter', 'cm', Rational(1, 100), :detector => /\A(centimet(er|re)s?|cm)\Z/)
|
39
|
-
derive('decameter', 'dam', 10, :detector => /\A(de(c|k)amet(er|re)s?|dam)\Z
|
42
|
+
derive('decameter', 'dam', 10, :detector => /\A(de(c|k)amet(er|re)s?|dam)\Z/, :preference => -3)
|
40
43
|
derive('kilometer', 'km', 1000, :detector => /\A(kilomet(er|re)s?|km)\Z/)
|
41
|
-
derive('nautical mile', 'M', 1852, :detector => /\A(nautical miles?|NMs?|nmis?)\Z/)
|
42
44
|
end
|
43
45
|
end
|
46
|
+
system(:U) do
|
47
|
+
reference('nautical mile', 'M', Dimensional::Unit[:L, :SI, 'meter'], 1852, :detector => /\A(nautical miles?|nm|nmi|M)\Z/)
|
48
|
+
end
|
44
49
|
system(:US) do # As of 1 July 1959 (http://en.wikipedia.org/wiki/United_States_customary_units#Units_of_length)
|
45
50
|
reference('yard', 'yd', Unit[:L, :SI, 'meter'], 0.9144, :detector => /\A(yards?|yds?)\Z/) do
|
46
51
|
derive('foot', 'ft', Rational(1,3), :detector => /\A(foot|feet|ft|')\Z/, :format => "%p'") do
|
@@ -84,16 +89,14 @@ Configurator.start do
|
|
84
89
|
dimension(:M) do
|
85
90
|
system(:SI) do
|
86
91
|
base('kilogram', 'kg', :detector => /\A(kilograms?|kg)\Z/) do
|
87
|
-
derive('tonne', 't', 1000, :detector => /\A(tonnes?)\Z/)
|
88
|
-
end
|
92
|
+
derive('tonne', 't', 1000, :detector => /\A(tonnes?)\Z/) # metric ton
|
89
93
|
derive('gram', 'g', Rational(1, 1000), :detector => /\A(grams?|g)\Z/)
|
90
94
|
end
|
91
95
|
end
|
92
96
|
system(:US) do # Common units for mass and, occasionally, force/weight (http://en.wikipedia.org/wiki/United_States_customary_units#Units_of_mass)
|
93
97
|
reference('pound', 'lb', Unit[:M, :SI, 'gram'], 453.59237, :detector => /\A(pounds?|lbs?|#)\Z/) do # avoirdupois
|
94
98
|
derive('hundredweight', 'cwt', 100, :detector => /\A(hundredweights?|cwt)\Z/) do
|
95
|
-
derive('ton', 't', 20, :detector => /\A(tons?|t)\Z/)
|
96
|
-
end
|
99
|
+
derive('ton', 't', 20, :detector => /\A(tons?|t)\Z/) # short ton
|
97
100
|
end
|
98
101
|
derive('ounce', 'oz', Rational(1, 16), :detector => /\A(ounces?|ozs?)\Z/)
|
99
102
|
derive('grain', 'gr', Rational(1, 7000), :detector => /\A(grains?|gr)\Z/) do
|
@@ -138,13 +141,14 @@ Configurator.start do
|
|
138
141
|
end
|
139
142
|
end
|
140
143
|
combine('square foot', 'ft2', {Unit[:L, :US, :ft] => 2}, :detector => /ft2/)
|
144
|
+
combine('square inch', 'in2', {Unit[:L, :US, :in] => 2}, :detector => /in2/)
|
141
145
|
end
|
142
146
|
end
|
143
147
|
|
144
148
|
dimension(:V) do
|
145
149
|
system(:SI) do
|
146
|
-
combine('cubic meter', 'm3', {Unit[:L, :SI, :meter] => 3}, :detector => /\A(cubic met(er|re)s?|m3)\Z/, :preference =>
|
147
|
-
derive('cubic decimeter', 'dm3', Rational(1, 1000), :detector => /\A(cubic decimet(er|re)s?|dm3)\Z/, :preference =>
|
150
|
+
combine('cubic meter', 'm3', {Unit[:L, :SI, :meter] => 3}, :detector => /\A(cubic met(er|re)s?|m3)\Z/, :preference => -2) do
|
151
|
+
derive('cubic decimeter', 'dm3', Rational(1, 1000), :detector => /\A(cubic decimet(er|re)s?|dm3)\Z/, :preference => -3) do
|
148
152
|
self.alias('liter', 'l', :detector => /\A(lit(er|re)s?|l|L)\Z/) do
|
149
153
|
derive('milliliter', 'ml', Rational(1, 1000), :detector => /\A(millilit(er|re)s?|ml|mL)\Z/)
|
150
154
|
end
|
@@ -157,7 +161,7 @@ Configurator.start do
|
|
157
161
|
derive('cup', 'cp', 2, :detector => /\A(cups?|cps?)\Z/) do
|
158
162
|
derive('pint', 'pt', 2, :detector => /\A(pints?|pts?)\Z/) do
|
159
163
|
derive('quart', 'qt', 2, :detector => /\A(quarts?|qts?)\Z/) do
|
160
|
-
derive('gallon', 'gal', 4, :detector => /\A(gallons?|
|
164
|
+
derive('gallon', 'gal', 4, :detector => /\A(gallons?|gals?)\Z/)
|
161
165
|
end
|
162
166
|
end
|
163
167
|
end
|
@@ -166,7 +170,7 @@ Configurator.start do
|
|
166
170
|
end
|
167
171
|
system(:US) do
|
168
172
|
combine('cubic inch', 'in3', {Unit[:L, :US, :inch] => 3}, :detector => /\A(cubic inch(es)?|in3)\Z/ ) do
|
169
|
-
derive('gallon', '
|
173
|
+
derive('gallon', 'gal', 231, :detector => /\A(gallons?|gals?)\Z/) do
|
170
174
|
derive('quart', 'qt', Rational(1,4), :detector => /\A(quarts?|qts?)\Z/) do
|
171
175
|
derive('pint', 'pt', Rational(1,2), :detector => /\A(pints?|pts?)\Z/) do
|
172
176
|
derive('cup', nil, Rational(1,2), :detector => /\Acups?\Z/) do
|
@@ -191,7 +195,9 @@ Configurator.start do
|
|
191
195
|
derive('minute', 'm', 60, :detector => /\A(minutes?|m)\Z/) do
|
192
196
|
derive('hour', 'h', 60, :detector => /\A(hours?|h)\Z/) do
|
193
197
|
derive('day', 'd', 24, :detector => /\A(days?)\Z/) do
|
194
|
-
derive('week', 'w', 7, :detector => /\A(weeks?|wks?)\Z/)
|
198
|
+
derive('week', 'w', 7, :detector => /\A(weeks?|wks?)\Z/) do
|
199
|
+
derive('fortnight', nil, 2, :detector => /\A(fortnights?)\Z/)
|
200
|
+
end
|
195
201
|
derive('year', 'yr', 365 + Rational(1, 4), :detector => /\A(years?|yrs?)\Z/)
|
196
202
|
end
|
197
203
|
end
|
@@ -218,8 +224,8 @@ Configurator.start do
|
|
218
224
|
system(:US) do
|
219
225
|
combine('mile per hour', 'mph', {Unit[:L, :US, :mile] => 1, Unit[:T, :SI, :hour] => -1}, :detector => /\A(mile per hour|mph)\Z/)
|
220
226
|
end
|
221
|
-
system(:
|
222
|
-
combine('knot', 'kt', {Unit[:L, :
|
227
|
+
system(:U) do
|
228
|
+
combine('knot', 'kt', {Unit[:L, :U, :M] => 1, Unit[:T, :SI, :hour] => -1}, :detector => /\A(knots?|kn|kts?)\Z/)
|
223
229
|
end
|
224
230
|
end
|
225
231
|
|
@@ -278,12 +284,20 @@ Configurator.start do
|
|
278
284
|
|
279
285
|
dimension(:Voltage) do
|
280
286
|
system(:SI) do
|
281
|
-
combine('Volt', 'V',{Unit[:P, :SI, :W] => 1, Unit[:I, :SI, :A] => -1}, :detector => /\A(volts?|V)\Z/) do
|
287
|
+
combine('Volt', 'V', {Unit[:P, :SI, :W] => 1, Unit[:I, :SI, :A] => -1}, :detector => /\A(volts?|V)\Z/) do
|
282
288
|
derive('millivolt', 'mV', Rational(1, 1000))
|
283
289
|
derive('kilovolt', 'kV', 1000)
|
284
290
|
end
|
285
291
|
end
|
286
292
|
end
|
293
|
+
dimension(:Pressure) do
|
294
|
+
system(:SI) do
|
295
|
+
combine('pascal', 'Pa', {Unit[:F, :SI, :N] => 1, Unit[:A, :SI, :m2] => -1}, :detector => /\A(pascals?|Pa)\Z/)
|
296
|
+
end
|
297
|
+
system(:US) do
|
298
|
+
combine('pound per square inch', 'psi', {Unit[:F, :US, :lbf] => 1, Unit[:A, :US, :in2] => -1}, :detector => /\A(psi)\Z/)
|
299
|
+
end
|
300
|
+
end
|
287
301
|
|
288
302
|
dimension(nil) do
|
289
303
|
system(:SI) do
|
@@ -301,6 +315,10 @@ Configurator.start do
|
|
301
315
|
end
|
302
316
|
end
|
303
317
|
|
318
|
+
Locale.default.systems = [System::SI, System::U, System::US, System::Imp]
|
319
|
+
Locale::US.systems = [System::US, System::USt, System::U, System::SI, System::Imp]
|
320
|
+
Locale::GB.systems = [System::Imp, System::SI, System::U, System::US]
|
321
|
+
|
304
322
|
class Length < Dimensional::Metric
|
305
323
|
self.dimension = Dimensional::Dimension::L
|
306
324
|
self.base = Unit[:L, :SI, :m]
|
@@ -310,17 +328,15 @@ end
|
|
310
328
|
class Autonomy < Dimensional::Metric
|
311
329
|
self.dimension = Dimension::L
|
312
330
|
self.base = Unit[:L, :SI, :m]
|
313
|
-
self.default = Unit[:L, :
|
314
|
-
|
315
|
-
configure(Unit[:L, :US, :mi], {:preference => 3.5})
|
316
|
-
configure(Unit[:L, :Imp, :mi], {:preference => 3.5})
|
331
|
+
self.default = Unit[:L, :U, :M]
|
332
|
+
self.universal_systems = [System::U]
|
317
333
|
end
|
318
334
|
|
319
335
|
class EngineDisplacement < Dimensional::Metric
|
320
336
|
self.dimension = Dimensional::Dimension::V
|
321
337
|
self.base = Unit[:V, :SI, :l]
|
322
338
|
self.default = base
|
323
|
-
configure(Unit[:V, :US, :in3], {:preference =>
|
339
|
+
configure(Unit[:V, :US, :in3], {:preference => 3})
|
324
340
|
end
|
325
341
|
|
326
342
|
class MechanicalPower < Dimensional::Metric
|
data/test/dimensional_test.rb
CHANGED
@@ -16,6 +16,7 @@ class DimensionalTest < Test::Unit::TestCase
|
|
16
16
|
Dimension.reset!
|
17
17
|
System.reset!
|
18
18
|
Unit.reset!
|
19
|
+
Locale.reset!
|
19
20
|
end
|
20
21
|
|
21
22
|
def test_a_lot
|
@@ -29,10 +30,10 @@ class DimensionalTest < Test::Unit::TestCase
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def test_engine_displacement
|
32
|
-
assert m = EngineDisplacement.
|
33
|
+
assert m = EngineDisplacement.load(5.7)
|
33
34
|
assert_equal 5.7, m
|
34
35
|
assert_same Unit[:V, :SI, 'liter'], m.unit
|
35
|
-
m = m.
|
36
|
+
m = m.localize(Locale::US)
|
36
37
|
assert_in_delta 347, m, 1
|
37
38
|
assert_same Unit[:V, :US, 'in3'], m.unit
|
38
39
|
end
|
@@ -41,7 +42,7 @@ class DimensionalTest < Test::Unit::TestCase
|
|
41
42
|
assert m = MechanicalPower.parse('430hp')
|
42
43
|
assert_equal 430, m
|
43
44
|
assert_same Unit[:P, :US, :hp], m.unit
|
44
|
-
m = m.
|
45
|
+
m = m.localize(Locale::FI)
|
45
46
|
assert_in_delta 320, m, 1
|
46
47
|
assert_same Unit[:P, :SI, :kW], m.unit
|
47
48
|
end
|
@@ -53,19 +54,20 @@ class DimensionalTest < Test::Unit::TestCase
|
|
53
54
|
self.default = Unit[:Vel, :SI, :"km/h"]
|
54
55
|
self.base = default
|
55
56
|
end
|
56
|
-
assert m = speed.parse('20 knots')
|
57
|
+
assert m = speed.parse('20 knots', Locale::DK)
|
57
58
|
assert_equal 20, m
|
58
|
-
assert_same Unit[:Vel, :
|
59
|
-
m = m.
|
59
|
+
assert_same Unit[:Vel, :U, 'knot'], m.unit
|
60
|
+
m = m.localize(Locale::US)
|
60
61
|
assert_in_delta 23, m, 0.1
|
61
62
|
assert_same Unit[:Vel, :US, 'mph'], m.unit
|
62
63
|
end
|
63
64
|
|
65
|
+
# All Locale's should have an explicit system ordering for the Autonomy metric that places the Universal system first
|
64
66
|
def test_autonomy
|
65
|
-
assert m = Autonomy.
|
66
|
-
assert_same Unit[:L, :
|
67
|
-
|
68
|
-
|
69
|
-
assert_same Unit[:L, :
|
67
|
+
assert m = Autonomy.load(200000)
|
68
|
+
assert_same Unit[:L, :U, :M], m.unit
|
69
|
+
assert_in_delta 108, m, 0.1
|
70
|
+
m = m.localize(Locale::US)
|
71
|
+
assert_same Unit[:L, :U, :M], m.unit
|
70
72
|
end
|
71
73
|
end
|
data/test/locale_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class LocaleTest < Test::Unit::TestCase
|
4
|
+
include Dimensional
|
5
|
+
|
6
|
+
def setup
|
7
|
+
System.register('British Imperial', 'Imp')
|
8
|
+
System.register('United States Customary', 'US')
|
9
|
+
System.register('International System of Units', 'SI')
|
10
|
+
System.register('International Standards', 'Std')
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
System.reset!
|
15
|
+
Locale.reset!
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_default_locale
|
19
|
+
assert_kind_of Locale, d = Locale.default
|
20
|
+
assert_kind_of Array, d.systems
|
21
|
+
assert d.systems.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_create
|
25
|
+
default_systems = [System::US, System::SI, System::Std]
|
26
|
+
assert us = Locale.new(:US, default_systems)
|
27
|
+
assert_equal default_systems, us.systems
|
28
|
+
assert_equal 'US', us.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_register
|
32
|
+
assert_kind_of Locale, us = Locale.register(:US)
|
33
|
+
assert_same us, Locale::US
|
34
|
+
assert_equal 'US', us.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_auto_constant_defaults
|
38
|
+
default_default_systems = [System::SI, System::Std, System::US, System::Imp]
|
39
|
+
Locale.default.systems = default_default_systems
|
40
|
+
assert_kind_of Locale, ca = Locale::CA
|
41
|
+
assert_equal default_default_systems, ca.systems
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_auto_constant_defaults_for_all_metrics
|
45
|
+
default_default_systems = [System::SI, System::Std, System::US, System::Imp]
|
46
|
+
us_default_systems = [System::Std, System::US, System::SI, System::Imp]
|
47
|
+
Locale.default.systems = default_default_systems
|
48
|
+
assert_kind_of Locale, us = Locale::US
|
49
|
+
us.systems = us_default_systems
|
50
|
+
assert_equal us_default_systems, us.systems
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_auto_constant_defaults_are_independent
|
54
|
+
default_default_systems = [System::SI, System::Std, System::US, System::Imp]
|
55
|
+
default_ca_systems = [System::SI, System::Std, System::Imp, System::US]
|
56
|
+
default_us_systems = [System::US, System::Std, System::SI, System::Imp]
|
57
|
+
Locale.default.systems = default_default_systems
|
58
|
+
Locale::CA.systems = default_ca_systems
|
59
|
+
Locale::US.systems.replace(default_us_systems)
|
60
|
+
assert_equal default_default_systems, Locale::BE.systems
|
61
|
+
assert_equal default_us_systems, Locale::US.systems
|
62
|
+
assert_equal default_ca_systems, Locale::CA.systems
|
63
|
+
end
|
64
|
+
end
|
data/test/metric_test.rb
CHANGED
@@ -35,19 +35,16 @@ class MetricTest < Test::Unit::TestCase
|
|
35
35
|
# Dimensionless Units
|
36
36
|
@each = Unit.register('each', System::US, nil, {:abbreviation => 'ea'})
|
37
37
|
@dozen = Unit.register('dozen', System::US, nil, {:reference_units => {@each => 0}, :reference_factor => 12, :abbreviation => 'dz'})
|
38
|
+
Locale.default.systems = [System::SI, System::US, System::BA]
|
39
|
+
Locale::US.systems = [System::US, System::SI, System::BA]
|
40
|
+
Locale::BA.systems = [System::BA]
|
38
41
|
end
|
39
42
|
|
40
43
|
def teardown
|
41
44
|
Dimension.reset!
|
42
45
|
System.reset!
|
43
46
|
Unit.reset!
|
44
|
-
|
45
|
-
|
46
|
-
def test_associated_units
|
47
|
-
beam = Class.new(Metric)
|
48
|
-
beam.dimension = Dimension::L
|
49
|
-
assert beam.units.include?(@cable)
|
50
|
-
assert !beam.units.include?(@pound_force)
|
47
|
+
Locale.reset!
|
51
48
|
end
|
52
49
|
|
53
50
|
def test_register_conflicting_unit
|
@@ -90,8 +87,8 @@ class MetricTest < Test::Unit::TestCase
|
|
90
87
|
def test_find_unit
|
91
88
|
depth = Class.new(Metric)
|
92
89
|
depth.dimension = Dimension::L
|
93
|
-
assert_same @foot_ba, depth.find_unit('foot',
|
94
|
-
assert_same @foot_us, depth.find_unit('foot',
|
90
|
+
assert_same @foot_ba, depth.find_unit('foot', Locale::BA)
|
91
|
+
assert_same @foot_us, depth.find_unit('foot', Locale::US)
|
95
92
|
end
|
96
93
|
|
97
94
|
def test_scanner
|
@@ -109,7 +106,7 @@ class MetricTest < Test::Unit::TestCase
|
|
109
106
|
def test_parse
|
110
107
|
depth = Class.new(Metric)
|
111
108
|
depth.dimension = Dimension::L
|
112
|
-
assert m = depth.parse("15ft",
|
109
|
+
assert m = depth.parse("15ft", Locale::BA)
|
113
110
|
assert_same @foot_ba, m.unit
|
114
111
|
assert_equal 15, m
|
115
112
|
end
|
@@ -117,7 +114,7 @@ class MetricTest < Test::Unit::TestCase
|
|
117
114
|
def test_parse_with_whitespace
|
118
115
|
depth = Class.new(Metric)
|
119
116
|
depth.dimension = Dimension::L
|
120
|
-
m = depth.parse("15 ft",
|
117
|
+
m = depth.parse("15 ft", Locale::BA)
|
121
118
|
assert_same @foot_ba, m.unit
|
122
119
|
assert_equal 15, m
|
123
120
|
end
|
@@ -125,7 +122,7 @@ class MetricTest < Test::Unit::TestCase
|
|
125
122
|
def test_parse_with_multiword_unit
|
126
123
|
range = Class.new(Metric)
|
127
124
|
range.dimension = Dimension::L
|
128
|
-
m = range.parse("15 nautical miles"
|
125
|
+
m = range.parse("15 nautical miles")
|
129
126
|
assert_same @international_nautical_mile, m.unit
|
130
127
|
assert_equal 15, m
|
131
128
|
end
|
@@ -133,7 +130,7 @@ class MetricTest < Test::Unit::TestCase
|
|
133
130
|
def test_parse_compound
|
134
131
|
depth = Class.new(Metric)
|
135
132
|
depth.dimension = Dimension::L
|
136
|
-
d = depth.parse("15ft11in",
|
133
|
+
d = depth.parse("15ft11in", Locale::US)
|
137
134
|
assert_in_delta(15 + Rational(11, 12), d, 0.000001)
|
138
135
|
assert_same @foot_us, d.unit
|
139
136
|
end
|
@@ -141,7 +138,7 @@ class MetricTest < Test::Unit::TestCase
|
|
141
138
|
def test_parse_compound_with_whitespace
|
142
139
|
depth = Class.new(Metric)
|
143
140
|
depth.dimension = Dimension::L
|
144
|
-
d = depth.parse("1 ft 11 in",
|
141
|
+
d = depth.parse("1 ft 11 in", Locale::US)
|
145
142
|
assert_same d.unit, @foot_us
|
146
143
|
assert_in_delta(1 + Rational(11, 12).to_f, d, 0.000001)
|
147
144
|
assert_same @foot_us, d.unit
|
@@ -151,7 +148,7 @@ class MetricTest < Test::Unit::TestCase
|
|
151
148
|
depth = Class.new(Metric)
|
152
149
|
depth.dimension = Dimension::L
|
153
150
|
assert_raises ArgumentError do
|
154
|
-
depth.parse("1 foot 11cm",
|
151
|
+
depth.parse("1 foot 11cm", Locale::US)
|
155
152
|
end
|
156
153
|
end
|
157
154
|
|
@@ -159,7 +156,7 @@ class MetricTest < Test::Unit::TestCase
|
|
159
156
|
depth = Class.new(Metric)
|
160
157
|
depth.dimension = Dimension::L
|
161
158
|
depth.default = @meter
|
162
|
-
assert_instance_of depth, m = depth.parse("10",
|
159
|
+
assert_instance_of depth, m = depth.parse("10", Locale::US)
|
163
160
|
assert_equal @meter, m.unit
|
164
161
|
end
|
165
162
|
|
@@ -176,7 +173,7 @@ class MetricTest < Test::Unit::TestCase
|
|
176
173
|
def test_to_f
|
177
174
|
depth = Class.new(Metric)
|
178
175
|
depth.dimension = Dimension::L
|
179
|
-
d = depth.parse("1.85m",
|
176
|
+
d = depth.parse("1.85m", Locale::SI)
|
180
177
|
assert_instance_of Float, d.to_f
|
181
178
|
end
|
182
179
|
|
@@ -232,7 +229,7 @@ class MetricTest < Test::Unit::TestCase
|
|
232
229
|
range.dimension = Dimension::L
|
233
230
|
m0 = range.new(1, @foot_us)
|
234
231
|
assert m1 = m0.change_system(:SI)
|
235
|
-
assert_same @
|
232
|
+
assert_same @centimeter, m1.unit
|
236
233
|
end
|
237
234
|
|
238
235
|
def test_change_system_mile
|
@@ -250,7 +247,7 @@ class MetricTest < Test::Unit::TestCase
|
|
250
247
|
self.dimension = Dimension::L
|
251
248
|
end
|
252
249
|
m0 = range.new(100000, @meter)
|
253
|
-
assert m1 = m0.preferred
|
250
|
+
assert m1 = m0.preferred(Locale::FR)
|
254
251
|
assert_same @kilometer, m1.unit
|
255
252
|
end
|
256
253
|
|
@@ -278,26 +275,26 @@ class MetricTest < Test::Unit::TestCase
|
|
278
275
|
def test_stringify_with_abbreviation
|
279
276
|
range = Class.new(Metric)
|
280
277
|
range.dimension = Dimension::L
|
281
|
-
assert_equal "1.
|
278
|
+
assert_equal "1.85 nm", range.parse('1.85 miles', Locale::BA).to_s
|
282
279
|
end
|
283
280
|
|
284
281
|
def test_parse_gibberish_as_nil
|
285
282
|
beam = Class.new(Metric)
|
286
283
|
beam.dimension = Dimension::L
|
287
|
-
assert_nil beam.parse("gibberish",
|
284
|
+
assert_nil beam.parse("gibberish", Locale::US)
|
288
285
|
end
|
289
286
|
|
290
287
|
def test_format_output
|
291
288
|
depth = Class.new(Metric)
|
292
289
|
depth.dimension = Dimension::L
|
293
|
-
m = depth.parse("15ft3in",
|
290
|
+
m = depth.parse("15ft3in", Locale::BA)
|
294
291
|
assert_equal "15.25 (ft)", m.strfmeasure("%4.2f (%U)")
|
295
292
|
end
|
296
293
|
|
297
294
|
def test_format_output_with_multiple_substitutions
|
298
295
|
depth = Class.new(Metric)
|
299
296
|
depth.dimension = Dimension::L
|
300
|
-
m = depth.parse("15ft4in",
|
297
|
+
m = depth.parse("15ft4in", Locale::BA)
|
301
298
|
assert_equal "15.33 (ft)\t%\t<15.3333333ft>", m.strfmeasure("%4.2f (%U)\t%%\t<%10.7f%U>")
|
302
299
|
end
|
303
300
|
|
@@ -305,7 +302,7 @@ class MetricTest < Test::Unit::TestCase
|
|
305
302
|
distance = Class.new(Metric)
|
306
303
|
distance.dimension = Dimension::L
|
307
304
|
distance.configure(@nautical_mile, :precision => -2)
|
308
|
-
assert_equal "1.8600nm", distance.parse('1.8565454 nm',
|
309
|
-
assert_equal "1.86", distance.parse('1.8565454 nm',
|
305
|
+
assert_equal "1.8600nm", distance.parse('1.8565454 nm', Locale::BA).strfmeasure("%.4f%U")
|
306
|
+
assert_equal "1.86", distance.parse('1.8565454 nm', Locale::BA).strfmeasure("%s")
|
310
307
|
end
|
311
308
|
end
|
data/test/system_test.rb
CHANGED
@@ -31,8 +31,8 @@ class SystemTest < Test::Unit::TestCase
|
|
31
31
|
assert_same s, System::BA
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
|
34
|
+
def test_raise_exception_when_system_not_found
|
35
|
+
assert_raises RuntimeError do
|
36
36
|
System['British Admiralty']
|
37
37
|
end
|
38
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dimensional
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hapgood
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-10 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -31,6 +31,7 @@ files:
|
|
31
31
|
- lib/dimensional.rb
|
32
32
|
- lib/dimensional/configurator.rb
|
33
33
|
- lib/dimensional/dimension.rb
|
34
|
+
- lib/dimensional/locale.rb
|
34
35
|
- lib/dimensional/metric.rb
|
35
36
|
- lib/dimensional/system.rb
|
36
37
|
- lib/dimensional/unit.rb
|
@@ -40,6 +41,7 @@ files:
|
|
40
41
|
- test/dimension_test.rb
|
41
42
|
- test/dimensional_test.rb
|
42
43
|
- test/helper.rb
|
44
|
+
- test/locale_test.rb
|
43
45
|
- test/metric_test.rb
|
44
46
|
- test/system_test.rb
|
45
47
|
- test/unit_test.rb
|
@@ -77,6 +79,7 @@ test_files:
|
|
77
79
|
- test/configurator_test.rb
|
78
80
|
- test/dimension_test.rb
|
79
81
|
- test/dimensional_test.rb
|
82
|
+
- test/locale_test.rb
|
80
83
|
- test/metric_test.rb
|
81
84
|
- test/system_test.rb
|
82
85
|
- test/unit_test.rb
|