unit 0.4.0 → 0.4.1
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/.travis.yml +2 -4
- data/lib/unit/class.rb +22 -14
- data/lib/unit/system.rb +48 -32
- data/lib/unit/systems/imperial.yml +16 -0
- data/lib/unit/systems/misc.yml +0 -4
- data/lib/unit/systems/si.yml +3 -0
- data/lib/unit/version.rb +1 -1
- data/spec/error_spec.rb +6 -4
- data/spec/system_spec.rb +85 -19
- data/spec/unit_spec.rb +9 -1
- metadata +7 -7
data/.travis.yml
CHANGED
data/lib/unit/class.rb
CHANGED
@@ -57,9 +57,12 @@ class Unit < Numeric
|
|
57
57
|
def /(other)
|
58
58
|
if Numeric === other
|
59
59
|
other = coerce_numeric(other)
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
result = if Integer === value && Integer === other.value
|
61
|
+
other.value == 1 ? value : Rational(value, other.value)
|
62
|
+
else
|
63
|
+
value / other.value
|
64
|
+
end
|
65
|
+
Unit.new(result, unit + Unit.power_unit(other.unit, -1), system)
|
63
66
|
else
|
64
67
|
apply_through_coercion(other, __method__)
|
65
68
|
end
|
@@ -150,7 +153,7 @@ class Unit < Numeric
|
|
150
153
|
unit = coerce_object(unit)
|
151
154
|
result = self.in(unit)
|
152
155
|
unless result.unit == unit.unit
|
153
|
-
raise TypeError, "Unexpected #{result.inspect}, expected to be in #{
|
156
|
+
raise TypeError, "Unexpected #{result.inspect}, expected to be in #{unit.unit_string}"
|
154
157
|
end
|
155
158
|
result
|
156
159
|
end
|
@@ -179,6 +182,10 @@ class Unit < Numeric
|
|
179
182
|
Unit.new(self.to_f, unit, system)
|
180
183
|
end
|
181
184
|
|
185
|
+
def round
|
186
|
+
Unit.new(value.round, unit, system)
|
187
|
+
end
|
188
|
+
|
182
189
|
def coerce(other)
|
183
190
|
[coerce_numeric(other), self]
|
184
191
|
end
|
@@ -231,16 +238,17 @@ class Unit < Numeric
|
|
231
238
|
end
|
232
239
|
|
233
240
|
# Reduce factors
|
234
|
-
@unit.each_with_index do |(factor1,
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
241
|
+
@unit.each_with_index do |(factor1, _, exp1), k|
|
242
|
+
if exp1 > 0
|
243
|
+
@unit.each_with_index do |(factor2, _, exp2), j|
|
244
|
+
if exp2 == -exp1
|
245
|
+
q, r = @system.factor[factor1][:value].divmod @system.factor[factor2][:value]
|
246
|
+
if r == 0 && new_factor = @system.factor_value[q]
|
247
|
+
@unit[k] = @unit[k].dup
|
248
|
+
@unit[j] = @unit[j].dup
|
249
|
+
@unit[k][0] = new_factor
|
250
|
+
@unit[j][0] = :one
|
251
|
+
end
|
244
252
|
end
|
245
253
|
end
|
246
254
|
end
|
data/lib/unit/system.rb
CHANGED
@@ -15,56 +15,42 @@ class Unit < Numeric
|
|
15
15
|
@factor_symbol = {'one' => :one}
|
16
16
|
@factor_value = {1 => :one}
|
17
17
|
|
18
|
+
@loaded_systems = []
|
19
|
+
@loaded_filenames = []
|
20
|
+
|
18
21
|
yield(self) if block_given?
|
19
22
|
end
|
20
23
|
|
21
24
|
def load(input)
|
22
25
|
case input
|
23
26
|
when Hash
|
24
|
-
|
25
|
-
data = input['units'] || input
|
27
|
+
data = input
|
26
28
|
when IO
|
27
29
|
data = YAML.load(input.read)
|
28
30
|
when String
|
29
31
|
if File.exist?(input)
|
32
|
+
return if @loaded_filenames.include?(input)
|
30
33
|
data = YAML.load_file(input)
|
34
|
+
@loaded_filenames << input
|
31
35
|
else
|
32
|
-
|
36
|
+
load(input.to_sym)
|
37
|
+
return
|
33
38
|
end
|
34
39
|
when Symbol
|
40
|
+
return if @loaded_systems.include?(input)
|
35
41
|
data = YAML.load_file(File.join(File.dirname(__FILE__), 'systems', "#{input}.yml"))
|
42
|
+
@loaded_systems << input
|
36
43
|
end
|
37
44
|
|
38
|
-
(data['factors']
|
39
|
-
|
40
|
-
symbols = [factor['sym'] || []].flatten
|
41
|
-
factor['def'] =~ /^(\d+)\^(-?\d+)$/
|
42
|
-
base = $1.to_i
|
43
|
-
exp = $2.to_i
|
44
|
-
value = base ** exp
|
45
|
-
$stderr.puts "Factor #{name} already defined" if @factor[name]
|
46
|
-
@factor[name] = { :symbol => symbols.first, :value => value }
|
47
|
-
symbols.each do |sym|
|
48
|
-
$stderr.puts "Factor symbol #{sym} for #{name} already defined" if @factor_symbol[name]
|
49
|
-
@factor_symbol[sym] = name
|
50
|
-
end
|
51
|
-
@factor_symbol[name.to_s] = @factor_value[value] = name
|
52
|
-
end
|
45
|
+
load_factors(data['factors']) if data['factors']
|
46
|
+
load_units(data['units']) if data['units']
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@unit[name] = { :symbol => symbols.first, :def => parse_unit(unit['def']) }
|
59
|
-
symbols.each do |sym|
|
60
|
-
$stderr.puts "Unit symbol #{sym} for #{name} already defined" if @unit_symbol[name]
|
61
|
-
@unit_symbol[sym] = name
|
62
|
-
end
|
63
|
-
@unit_symbol[name.to_s] = name
|
48
|
+
@unit.each do |name, unit|
|
49
|
+
defs = unit.delete(:defs)
|
50
|
+
unit[:def] = parse_unit(defs) if defs
|
51
|
+
validate_unit(unit[:def])
|
64
52
|
end
|
65
53
|
|
66
|
-
@unit.each {|name, unit| validate_unit(unit[:def]) }
|
67
|
-
|
68
54
|
true
|
69
55
|
end
|
70
56
|
|
@@ -112,7 +98,7 @@ class Unit < Numeric
|
|
112
98
|
|
113
99
|
REAL = /^-?(?:(?:\d*\.\d+|\d+\.\d*)(?:[eE][-+]?\d+)?|\d+[eE][-+]?\d+)$/
|
114
100
|
DEC = /^-?\d+$/
|
115
|
-
SYMBOL = /^[a-zA-Z_°'"][\
|
101
|
+
SYMBOL = /^[a-zA-Z_°'"][\w°'"]*$/
|
116
102
|
OPERATOR = { '/' => ['/', 1], '*' => ['*', 1], '·' => ['*', 1], '^' => ['^', 2], '**' => ['^', 2] }
|
117
103
|
OPERATOR_TOKENS = OPERATOR.keys.sort_by {|x| -x.size }. map {|x| Regexp.quote(x) }
|
118
104
|
VALUE_TOKENS = [REAL.source[1..-2], DEC.source[1..-2], SYMBOL.source[1..-2]]
|
@@ -146,7 +132,37 @@ class Unit < Numeric
|
|
146
132
|
end
|
147
133
|
end
|
148
134
|
|
149
|
-
|
135
|
+
def load_factors(factors)
|
136
|
+
factors.each do |name, factor|
|
137
|
+
name = name.to_sym
|
138
|
+
symbols = [factor['sym'] || []].flatten
|
139
|
+
base, exp = factor["def"].to_s.split("^").map { |value| Integer(value) }
|
140
|
+
exp ||= 1
|
141
|
+
raise "Invalid definition for factor #{name}" unless base
|
142
|
+
value = base ** exp
|
143
|
+
$stderr.puts "Factor #{name} already defined" if @factor[name]
|
144
|
+
@factor[name] = { :symbol => symbols.first, :value => value }
|
145
|
+
symbols.each do |sym|
|
146
|
+
$stderr.puts "Factor symbol #{sym} for #{name} already defined" if @factor_symbol[name]
|
147
|
+
@factor_symbol[sym] = name
|
148
|
+
end
|
149
|
+
@factor_symbol[name.to_s] = @factor_value[value] = name
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def load_units(units)
|
154
|
+
units.each do |name, unit|
|
155
|
+
name = name.to_sym
|
156
|
+
symbols = [unit['sym'] || []].flatten
|
157
|
+
$stderr.puts "Unit #{name} already defined" if @unit[name]
|
158
|
+
@unit[name] = { :symbol => symbols.first, :defs => unit['def'] }
|
159
|
+
symbols.each do |sym|
|
160
|
+
$stderr.puts "Unit symbol #{sym} for #{name} already defined" if @unit_symbol[name]
|
161
|
+
@unit_symbol[sym] = name
|
162
|
+
end
|
163
|
+
@unit_symbol[name.to_s] = name
|
164
|
+
end
|
165
|
+
end
|
150
166
|
|
151
167
|
SI = new('SI') do |system|
|
152
168
|
system.load(:si)
|
@@ -35,3 +35,19 @@ units:
|
|
35
35
|
knot:
|
36
36
|
sym: kt
|
37
37
|
def: nauticalmile / hour
|
38
|
+
|
39
|
+
gallon:
|
40
|
+
sym: [gal, gallons]
|
41
|
+
def: 231 inch^3
|
42
|
+
quart:
|
43
|
+
sym: [qt, quarts]
|
44
|
+
def: 1 gallon / 4
|
45
|
+
pint:
|
46
|
+
sym: [pt, pints]
|
47
|
+
def: 1 gallon / 8
|
48
|
+
cup:
|
49
|
+
sym: cups
|
50
|
+
def: 1 gallon / 16
|
51
|
+
fluid_ounce:
|
52
|
+
sym: [fl_oz, fluid_ounces]
|
53
|
+
def: 1 gallon / 128
|
data/lib/unit/systems/misc.yml
CHANGED
data/lib/unit/systems/si.yml
CHANGED
data/lib/unit/version.rb
CHANGED
data/spec/error_spec.rb
CHANGED
@@ -13,14 +13,16 @@ describe "Errors" do
|
|
13
13
|
describe "TypeError when trying to convert incompatible unit using #in!" do
|
14
14
|
it "should have a nice error message" do
|
15
15
|
unit = Unit(1000, "m / s")
|
16
|
-
|
17
|
-
raise_error(TypeError, %{Unexpected
|
16
|
+
expect { unit.in!("seconds") }.to(
|
17
|
+
raise_error(TypeError, %{Unexpected #{unit.inspect}, expected to be in s})
|
18
|
+
)
|
18
19
|
end
|
19
20
|
|
20
21
|
it "should have a nice error message using the DSL", :dsl => true do
|
21
22
|
unit = Unit(1000, "m / s")
|
22
|
-
|
23
|
-
raise_error(TypeError, %{Unexpected
|
23
|
+
expect { unit.in_seconds! }.to(
|
24
|
+
raise_error(TypeError, %{Unexpected #{unit.inspect}, expected to be in s})
|
25
|
+
)
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
data/spec/system_spec.rb
CHANGED
@@ -1,31 +1,97 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
describe
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
describe Unit::System do
|
5
|
+
let(:system) { Unit::System.new("test") }
|
6
|
+
|
7
|
+
describe "#load" do
|
8
|
+
it "should load an IO object" do
|
9
|
+
system.load(:si)
|
10
|
+
test_file = File.join(File.dirname(__FILE__), "yml", "io.yml")
|
11
|
+
File.open(test_file) { |file| system.load(file) }
|
12
|
+
Unit(1, "pim", system).should == Unit(3.14159, "m", system)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when passed a String" do
|
16
|
+
context "that is a filename" do
|
17
|
+
it "should load the file" do
|
18
|
+
filename = File.join(File.dirname(__FILE__), "yml", "filename.yml")
|
19
|
+
system.load(:si)
|
20
|
+
system.load(filename)
|
21
|
+
Unit(2, "dzm", system).should == Unit(24, "m", system)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "that is not a filename" do
|
26
|
+
it "should load the built-in system of that name" do
|
27
|
+
system.load("si")
|
28
|
+
lambda { Unit(2, 'm', system) }.should_not raise_exception
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when passed a Hash" do
|
34
|
+
context "of units" do
|
35
|
+
it "should load the units" do
|
36
|
+
system.load(:si)
|
37
|
+
system.load(
|
38
|
+
'units' => {
|
39
|
+
'dozen_meter' => {
|
40
|
+
'sym' => 'dzm',
|
41
|
+
'def' => '12 m'
|
42
|
+
}
|
43
|
+
}
|
44
|
+
)
|
45
|
+
Unit(2, "dzm", system).should == Unit(24, "m", system)
|
11
46
|
end
|
12
|
-
Unit(1, "pim").should == Unit(3.14159, "m")
|
13
47
|
end
|
14
48
|
|
15
|
-
|
49
|
+
context "of factors" do
|
50
|
+
it "should load the factors" do
|
51
|
+
system.load(:si)
|
52
|
+
system.load(
|
53
|
+
'factors' => {
|
54
|
+
'dozen' => {
|
55
|
+
'sym' => 'dz',
|
56
|
+
'def' => 12
|
57
|
+
}
|
58
|
+
}
|
59
|
+
)
|
60
|
+
Unit(2, "dzm", system).should == Unit(24, "m", system)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when passed an invalid factor" do
|
65
|
+
it "should raise an exception" do
|
66
|
+
system.load(:si)
|
67
|
+
lambda {
|
68
|
+
system.load(
|
69
|
+
'factors' => {
|
70
|
+
'dozen' => {
|
71
|
+
'sym' => 'dz'
|
72
|
+
}
|
73
|
+
}
|
74
|
+
)
|
75
|
+
}.should raise_exception("Invalid definition for factor dozen")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when called on the same filename a second time" do
|
81
|
+
it "should be a no-op" do
|
82
|
+
$stderr.should_not_receive(:puts)
|
16
83
|
test_file = File.join(File.dirname(__FILE__), "yml", "filename.yml")
|
17
|
-
|
18
|
-
|
84
|
+
system.load(:si)
|
85
|
+
system.load(test_file)
|
86
|
+
lambda { system.load(test_file) }.should_not raise_exception
|
19
87
|
end
|
88
|
+
end
|
20
89
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
}
|
27
|
-
})
|
28
|
-
Unit(2, "dzm").should == Unit(24, "m")
|
90
|
+
context "when called on the same symbol a second time" do
|
91
|
+
it "should be a no-op" do
|
92
|
+
$stderr.should_not_receive(:puts)
|
93
|
+
system.load(:si)
|
94
|
+
lambda { system.load(:si) }.should_not raise_exception
|
29
95
|
end
|
30
96
|
end
|
31
97
|
end
|
data/spec/unit_spec.rb
CHANGED
@@ -158,7 +158,7 @@ describe 'Unit' do
|
|
158
158
|
|
159
159
|
it 'should reduce units' do
|
160
160
|
Unit(1, "joule/kilogram").normalize.unit.should == [[:one, :meter, 2], [:one, :second, -2]].sort
|
161
|
-
Unit(1, "megaton/kilometer").unit.should == [[:
|
161
|
+
Unit(1, "megaton/kilometer").unit.should == [[:one, :meter, -1], [:kilo, :ton, 1]]
|
162
162
|
end
|
163
163
|
|
164
164
|
it 'should work with floating point values' do
|
@@ -213,6 +213,14 @@ describe 'Unit' do
|
|
213
213
|
Unit(Rational(1,3), "m").approx.should == Unit(1.0/3.0, "m")
|
214
214
|
end
|
215
215
|
|
216
|
+
it "should be able to round and return a unit" do
|
217
|
+
Unit(Rational(1,3), "m").round.should == Unit(0, "m")
|
218
|
+
Unit(Rational(2,3), "m").round.should == Unit(1, "m")
|
219
|
+
Unit(0.1, "m").round.should == Unit(0, "m")
|
220
|
+
Unit(0.5, "m").round.should == Unit(1, "m")
|
221
|
+
Unit(1, "m").round.should == Unit(1, "m")
|
222
|
+
end
|
223
|
+
|
216
224
|
end
|
217
225
|
|
218
226
|
describe "Unit DSL", :dsl => true do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &11788980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.8.7
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *11788980
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &11978500 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *11978500
|
36
36
|
description:
|
37
37
|
email:
|
38
38
|
- mail@daniel-mendler.de
|
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
88
|
version: '0'
|
89
89
|
requirements: []
|
90
90
|
rubyforge_project: unit
|
91
|
-
rubygems_version: 1.8.
|
91
|
+
rubygems_version: 1.8.15
|
92
92
|
signing_key:
|
93
93
|
specification_version: 3
|
94
94
|
summary: Scientific unit support for ruby for calculations
|