quantity 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +36 -0
- data/.gitignore +10 -0
- data/.yardopts +13 -0
- data/AUTHORS +1 -0
- data/Gemfile +4 -0
- data/README +4 -5
- data/README.md +217 -0
- data/Rakefile +48 -0
- data/VERSION +1 -1
- data/doc/.gitignore +2 -0
- data/lib/quantity.rb +10 -0
- data/lib/quantity/dimension.rb +8 -1
- data/lib/quantity/rails.rb +12 -0
- data/lib/quantity/unit.rb +17 -5
- data/lib/quantity/version.rb +1 -1
- data/quantity.gemspec +42 -0
- data/spec/dimension.spec +330 -0
- data/spec/quantity.spec +282 -0
- data/spec/systems.spec +31 -0
- data/spec/unit.spec +284 -0
- metadata +94 -24
data/lib/quantity/version.rb
CHANGED
data/quantity.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "quantity/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "quantity"
|
7
|
+
s.version = Quantity::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ben Lavender", "Arto Bendiken"]
|
10
|
+
s.email = ["blavender@gmail.com", "arto.bendiken@gmail.com"]
|
11
|
+
s.homepage = %q{http://quantity.rubyforge.org/}
|
12
|
+
s.summary = %q{Units and quantities for Ruby.}
|
13
|
+
s.description = %q{Quantity provides first-class quantities, units, and base quantities in pure ruby.
|
14
|
+
Things like 1.meter / 1.second == 1 meter/second.
|
15
|
+
}
|
16
|
+
|
17
|
+
s.rubyforge_project = %q{quantity}
|
18
|
+
|
19
|
+
s.add_development_dependency "rspec", ["= 1.2.9"]
|
20
|
+
if s.respond_to? :specification_version then
|
21
|
+
s.specification_version = 3
|
22
|
+
|
23
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
24
|
+
s.add_development_dependency(%q<rake>, ["= 0.8.7"])
|
25
|
+
s.add_development_dependency(%q<rspec>, ["= 1.2.9"])
|
26
|
+
s.add_development_dependency(%q<yard>, ["= 0.5.2"])
|
27
|
+
else
|
28
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
29
|
+
s.add_dependency(%q<rspec>, ["= 1.2.9"])
|
30
|
+
s.add_dependency(%q<yard>, ["= 0.5.2"])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
34
|
+
s.add_dependency(%q<rspec>, ["= 1.2.9"])
|
35
|
+
s.add_dependency(%q<yard>, ["= 0.5.2"])
|
36
|
+
end
|
37
|
+
|
38
|
+
s.files = `git ls-files`.split("\n")
|
39
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
40
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
end
|
data/spec/dimension.spec
ADDED
@@ -0,0 +1,330 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
|
2
|
+
|
3
|
+
require 'quantity/dimension'
|
4
|
+
|
5
|
+
describe Quantity::Dimension do
|
6
|
+
|
7
|
+
## we test other things, so make sure dimensions are destroyed before we start
|
8
|
+
before :all do
|
9
|
+
Quantity::Dimension.__reset!
|
10
|
+
end
|
11
|
+
|
12
|
+
context "definition" do
|
13
|
+
after :all do
|
14
|
+
Quantity::Dimension.__reset!
|
15
|
+
end
|
16
|
+
|
17
|
+
context "creation via DSL" do
|
18
|
+
it "creates simple dimensions" do
|
19
|
+
Quantity::Dimension.add_dimension :length, :width
|
20
|
+
length = Quantity::Dimension.for(:length)
|
21
|
+
length.to_s.should == "length"
|
22
|
+
length.name.should == :length
|
23
|
+
end
|
24
|
+
|
25
|
+
it "creates complex dimensions" do
|
26
|
+
Quantity::Dimension.add_dimension :'length^2', :area
|
27
|
+
area = Quantity::Dimension.for(:area)
|
28
|
+
area.to_s.should == "area"
|
29
|
+
area.name.should == :area
|
30
|
+
area.reduced_name.should == :'length^2'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns nil for non-existent dimensions" do
|
36
|
+
Quantity::Dimension.for(:nodimension).should be nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# this is useful for testing
|
40
|
+
it "should allow resetting the world" do
|
41
|
+
Quantity::Dimension.__reset!
|
42
|
+
x = Quantity::Dimension.for(:length)
|
43
|
+
x.should == nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "use case" do
|
48
|
+
|
49
|
+
before :all do
|
50
|
+
Quantity::Dimension.__reset!
|
51
|
+
length = Quantity::Dimension.add_dimension :length, :width
|
52
|
+
Quantity::Dimension.add_dimension :'length^2', :area
|
53
|
+
mass = Quantity::Dimension.add_dimension :mass
|
54
|
+
time = Quantity::Dimension.add_dimension :time
|
55
|
+
Quantity::Dimension.add_dimension :'length/time^2', :acceleration
|
56
|
+
Quantity::Dimension.add_dimension(((mass * length) / time**2), :force)
|
57
|
+
end
|
58
|
+
|
59
|
+
# We use these for testing dimension. the systems are tested in a separate spec.
|
60
|
+
# we'll be using these for the rest of the specs
|
61
|
+
before(:each) do
|
62
|
+
@length = Quantity::Dimension.for(:length)
|
63
|
+
@mass = Quantity::Dimension.for(:mass)
|
64
|
+
@area = Quantity::Dimension.for(:area)
|
65
|
+
@accel = Quantity::Dimension.for(:acceleration)
|
66
|
+
@force = Quantity::Dimension.for(:force)
|
67
|
+
@time = Quantity::Dimension.for(:time)
|
68
|
+
end
|
69
|
+
|
70
|
+
after :all do
|
71
|
+
Quantity::Dimension.__reset!
|
72
|
+
end
|
73
|
+
|
74
|
+
it "has a name for simple dimensions" do
|
75
|
+
@mass.name.should == :mass
|
76
|
+
end
|
77
|
+
|
78
|
+
it "uses the primary name for dimensions with aliases" do
|
79
|
+
@length.name.should == :length
|
80
|
+
end
|
81
|
+
|
82
|
+
it "has a name for named complex dimensions" do
|
83
|
+
@force.name.should == :force
|
84
|
+
end
|
85
|
+
|
86
|
+
it "provides a reduced form for base dimensions" do
|
87
|
+
@length.reduced_name.should == :'length'
|
88
|
+
end
|
89
|
+
|
90
|
+
it "provides a reduced form for complex dimensions" do
|
91
|
+
@force.reduced_name.should == :'length*mass/time^2'
|
92
|
+
end
|
93
|
+
|
94
|
+
it "provides a hash form for simple dimensions" do
|
95
|
+
@length.hash.should == { :length => 1 }
|
96
|
+
end
|
97
|
+
|
98
|
+
it "provides a hash form for unnamed dimensions" do
|
99
|
+
lm = @length * @mass
|
100
|
+
lm.hash.should == { :length => 1, :mass => 1 }
|
101
|
+
end
|
102
|
+
|
103
|
+
it "provides a hash form for named dimensions" do
|
104
|
+
@area.to_hash.should == { :area => 1 }
|
105
|
+
end
|
106
|
+
|
107
|
+
it "provides a reduced hash form" do
|
108
|
+
@length.to_hash.should == @length.reduced_hash
|
109
|
+
@area.reduced.to_hash.should == { :length => 2 }
|
110
|
+
end
|
111
|
+
|
112
|
+
it "provides a hash form with negative components" do
|
113
|
+
@force.to_hash.should = { :length => 1, :mass => -1 , :time => -2 }
|
114
|
+
end
|
115
|
+
|
116
|
+
it "provides a reduced form" do
|
117
|
+
@area.name.should == :area
|
118
|
+
@area.reduce.name.should == :'length^2'
|
119
|
+
end
|
120
|
+
|
121
|
+
## this is going to be removed for hashes, but unit still uses it, so we test it.
|
122
|
+
it "should track numerators and denominators" do
|
123
|
+
@length.numerators.first.dimension.should == :length
|
124
|
+
@length.numerators.first.power.should == 1
|
125
|
+
@force.numerators.first.dimension.should == :length
|
126
|
+
@force.numerators.first.power== 1
|
127
|
+
@force.numerators[1].dimension.should == :mass
|
128
|
+
@force.numerators[1].power== 1
|
129
|
+
@force.denominators.first.dimension.should == :time
|
130
|
+
@force.denominators.first.power.should == 2
|
131
|
+
end
|
132
|
+
|
133
|
+
# This is a bad interface and will be removed.
|
134
|
+
it "should provide a vaguely parsable string format" do
|
135
|
+
component = Quantity::Dimension::DimensionComponent.new(:length,3)
|
136
|
+
component2 = Quantity::Dimension::DimensionComponent.new(:length,2)
|
137
|
+
component3 = Quantity::Dimension::DimensionComponent.new(:time,2)
|
138
|
+
component4 = Quantity::Dimension::DimensionComponent.new(:length,5)
|
139
|
+
Quantity::Dimension.string_form([component],[]).should=='length^3'
|
140
|
+
Quantity::Dimension.string_form([component,component2],[]).should=='length^3*length^2'
|
141
|
+
Quantity::Dimension.string_form([component,component2],[component3]).should=='length^3*length^2/time^2'
|
142
|
+
Quantity::Dimension.parse_string_form('length^3*length^2/time^2').inspect.should == [[component4],[component3]].inspect
|
143
|
+
end
|
144
|
+
|
145
|
+
context "multiplication" do
|
146
|
+
it "multiplies base dimensions" do
|
147
|
+
mass_squared = @mass * @mass
|
148
|
+
mass_squared.name.should == :'mass^2'
|
149
|
+
end
|
150
|
+
|
151
|
+
it "interns base multiplcation results" do
|
152
|
+
mass_squared = @mass * @mass
|
153
|
+
mass_squared_again = @mass * @mass
|
154
|
+
mass_squared.should equal mass_squared_again
|
155
|
+
end
|
156
|
+
|
157
|
+
it "multiplies derived complex dimensions and base dimensions" do
|
158
|
+
mass_squared = @mass * @mass
|
159
|
+
mass_cubed = mass_squared * @mass
|
160
|
+
mass_cubed.name.should == :'mass^3'
|
161
|
+
end
|
162
|
+
|
163
|
+
it "multiplies base dimensions and derived complex dimensions" do
|
164
|
+
mass_squared = @mass * @mass
|
165
|
+
mass_cubed = @mass * mass_squared
|
166
|
+
mass_cubed.name.should == :'mass^3'
|
167
|
+
end
|
168
|
+
|
169
|
+
it "interns mixed multiplcation results" do
|
170
|
+
mass_squared = @mass * @mass
|
171
|
+
mass_cubed = @mass * mass_squared
|
172
|
+
mass_cubed_again = mass_squared * @mass
|
173
|
+
mass_cubed.should equal mass_cubed_again
|
174
|
+
end
|
175
|
+
|
176
|
+
it "multiplies derived complex dimensions with each other" do
|
177
|
+
mass_squared = @mass * @mass
|
178
|
+
time_squared = @time * @time
|
179
|
+
mt = mass_squared * time_squared
|
180
|
+
mt.name.should == :'mass^2*time^2'
|
181
|
+
end
|
182
|
+
|
183
|
+
it "multiplies named complex dimensions with base dimensions" do
|
184
|
+
ma = @area * @mass
|
185
|
+
ma.name.should == :'area*mass'
|
186
|
+
end
|
187
|
+
|
188
|
+
it "multiplies base dimensions with named complex dimensions" do
|
189
|
+
ma = @mass * @area
|
190
|
+
ma.name.should == :'area*mass'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "multiplies named complex dimensions with each other" do
|
194
|
+
l4 = @area * @area
|
195
|
+
l4.name.should == :'area^2'
|
196
|
+
end
|
197
|
+
|
198
|
+
it "performs exponentiation" do
|
199
|
+
(@length**3).should == @length * @length * @length
|
200
|
+
end
|
201
|
+
|
202
|
+
it "allows naming of units first derived via multiplication" do
|
203
|
+
t2 = @time * @time
|
204
|
+
Quantity::Dimension.add_dimension @time * @time, :time_squared
|
205
|
+
Quantity::Dimension.for(:time_squared).should equal t2
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
# in division, we test base / complex, but based on equality and
|
211
|
+
# multiplication tests, we do not specifically test division against named
|
212
|
+
# and derived. either is fine unless we expect different semantics
|
213
|
+
context "division" do
|
214
|
+
|
215
|
+
context "with base dividends" do
|
216
|
+
|
217
|
+
it "supports base divisors" do
|
218
|
+
speed = @length / @time
|
219
|
+
speed.name.should == :'length/time'
|
220
|
+
end
|
221
|
+
|
222
|
+
it "supports complex divisors" do
|
223
|
+
ta = @time / @area
|
224
|
+
ta.name.should == :'time/area'
|
225
|
+
end
|
226
|
+
|
227
|
+
it "supports complex divisors with a negative exponent" do
|
228
|
+
mt = @mass / @time
|
229
|
+
lmt = @length / mt
|
230
|
+
lmt.name.should == :'length*time/mass'
|
231
|
+
end
|
232
|
+
|
233
|
+
# This one is a tough call. Supporting this is difficult, since
|
234
|
+
# acceleration as a denominator is the same as multiplying by the
|
235
|
+
# reciprocal. It's confusing for the internal representation. I'm also
|
236
|
+
# hard-pressed to find examples of this being useful, i.e. when's it
|
237
|
+
# useful to have something divided by force? length per force?
|
238
|
+
it "supports reduced form with a named complex divisor " do
|
239
|
+
ma = @mass / @accel
|
240
|
+
ma.name.should == :'mass*time^2/length'
|
241
|
+
end
|
242
|
+
|
243
|
+
it "supports named form with a named complex divisor" do
|
244
|
+
ma = @mass / @accel
|
245
|
+
ma.name.should == :'mass/acceleration'
|
246
|
+
end
|
247
|
+
|
248
|
+
it "supports a nil numerator component" do
|
249
|
+
speed = @mass / @time
|
250
|
+
t_neg_1 = speed / @mass
|
251
|
+
t_neg_1.name.should == :'1/time'
|
252
|
+
t_neg_1.reduced_hash.should == { :time => -1 }
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
context "with complex dividends" do
|
258
|
+
it "supports base dimension divisors" do
|
259
|
+
am = @area / @mass
|
260
|
+
am.reduced_name.should == :'length^2/mass'
|
261
|
+
am.name.should == :'area/mass'
|
262
|
+
end
|
263
|
+
|
264
|
+
it "supports complex divisors" do
|
265
|
+
am = @area / (@mass * @mass)
|
266
|
+
am.reduced_name.should == :'length^2/mass^2'
|
267
|
+
am.name.should == :'area/mass^2'
|
268
|
+
end
|
269
|
+
|
270
|
+
it "supports divisors with a negative exponent" do
|
271
|
+
result = (@mass * @mass) / (@length / @time)
|
272
|
+
result.name.should == :'mass^2*time/length'
|
273
|
+
end
|
274
|
+
|
275
|
+
it "supports reduced form for divisors with a negative exponent" do
|
276
|
+
result = (@mass * @mass) / @accel
|
277
|
+
result.name.should == :'mass^2*time^2/length'
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "with dividends with negative exponents" do
|
282
|
+
it "supports reduced form for base divisor" do
|
283
|
+
jerk = @accel / @time
|
284
|
+
jerk.reduced_name.should == :'length/time^3'
|
285
|
+
end
|
286
|
+
|
287
|
+
it "supports named form for base divisor" do
|
288
|
+
jerk = @accel / @time
|
289
|
+
jerk.name.should == :'acceleration/time'
|
290
|
+
end
|
291
|
+
|
292
|
+
it "supports reduced form for complex divisors" do
|
293
|
+
af = @accel / (@mass * @mass)
|
294
|
+
af.reduced_name.should == :'length/mass^2*time^2'
|
295
|
+
end
|
296
|
+
|
297
|
+
it "supports named form for complex divisors" do
|
298
|
+
af = @accel / (@mass * @mass)
|
299
|
+
af.name.should == :'acceleration/mass^2'
|
300
|
+
end
|
301
|
+
|
302
|
+
it "only supports reduced form for a subdimension divisor" do
|
303
|
+
fa = @force / @accel
|
304
|
+
fa.reduced_name.should == :'force/acceleration'
|
305
|
+
fa.reduced_name.should == :'mass'
|
306
|
+
end
|
307
|
+
|
308
|
+
it "supports named form for complex divisors with negative exponents" do
|
309
|
+
tm = @time / @mass
|
310
|
+
fa = @force / tm
|
311
|
+
fa.name.should == :'force*mass/time'
|
312
|
+
end
|
313
|
+
|
314
|
+
it "supports reduced form for a complex divisors with negative exponents" do
|
315
|
+
tm = @time / @mass
|
316
|
+
fa = @force / tm
|
317
|
+
fa.reduced_name.should == :'length*mass^2/time^3'
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context "as negative exponentiation" do
|
322
|
+
it "supports negative exponents" do
|
323
|
+
length_neg_2 = @length**-2
|
324
|
+
length_neg_2.name.should == :'1/length^2'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
data/spec/quantity.spec
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
|
2
|
+
|
3
|
+
require 'quantity'
|
4
|
+
require 'quantity/systems/si'
|
5
|
+
require 'quantity/systems/us'
|
6
|
+
|
7
|
+
describe Quantity do
|
8
|
+
|
9
|
+
context "instantiation" do
|
10
|
+
it "should be instantiated from numbers" do
|
11
|
+
1.meter.should == 1
|
12
|
+
2.5.feet.should == 2.5
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should work with alias names" do
|
16
|
+
2.meters.should == 2
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should know what it measures" do
|
20
|
+
2.meters.unit.dimension.name.should == :length
|
21
|
+
2.meters.measures.name.should == :length
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should know its units" do
|
25
|
+
2.meters.unit.name.should == :meter
|
26
|
+
2.meters.units.should == :meter
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have a string representation" do
|
30
|
+
2.meters.to_s.should == "2 meter"
|
31
|
+
(2.meters * 2.meters).to_s.should == (defined?(Rational) ? "4 meter^2" : "4.0 meter^2")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "conversions" do
|
37
|
+
it "converts from one type to another" do
|
38
|
+
1.meter.in_centimeters.should == 100
|
39
|
+
10.meters.convert(:feet).should be_close 32.808399.feet, 10**-6
|
40
|
+
end
|
41
|
+
|
42
|
+
it "converts from one type to another when not using the reference value for that dimension" do
|
43
|
+
1.kilometer.in_centimeters.should == 100_000
|
44
|
+
end
|
45
|
+
|
46
|
+
it "fails to convert things that do not measure the same dimension" do
|
47
|
+
lambda { 1.picogram.in_meters }.should raise_error ArgumentError
|
48
|
+
end
|
49
|
+
|
50
|
+
it "converts derived units" do
|
51
|
+
Quantity.new(2,'m^2').to_feet.to_f.should be_close 21.5278208, 10**-5
|
52
|
+
Quantity.new(2,'m^2').convert('foot^2').to_f.should be_close 21.5278208, 10**-5
|
53
|
+
end
|
54
|
+
|
55
|
+
it "converts derived units to named units" do
|
56
|
+
(1.centimeter * 1.centimeter * 1.centimeter).should == 0.1.centiliter
|
57
|
+
(1000.mm * 1.mm * 1.mm).should == 1.ml
|
58
|
+
(1.mm**3).unit.name.should == 'millimeter^3'
|
59
|
+
(1.mm**3).measures.name.should == :volume
|
60
|
+
(1.centimeter * 1.centimeter).measures.name.should == :area
|
61
|
+
(30.meters / 1.second).measures.name.should == :speed
|
62
|
+
end
|
63
|
+
|
64
|
+
it "reduces derived units" do
|
65
|
+
((1.meter / 1.second) * 1.second).should == 1.meter
|
66
|
+
end
|
67
|
+
|
68
|
+
it "respond_to? conversion methods" do
|
69
|
+
1.meter.should respond_to(:in_centimeters)
|
70
|
+
1.meter.should respond_to(:to_centimeters)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
context "math operations" do
|
76
|
+
|
77
|
+
context "equality" do
|
78
|
+
it "enforces exact equality" do
|
79
|
+
12.meter.should == 12.meters
|
80
|
+
end
|
81
|
+
|
82
|
+
it "does not intern quantities" do
|
83
|
+
12.meter.should_not equal 12.meters
|
84
|
+
end
|
85
|
+
|
86
|
+
it "enforces equality across a dimension" do
|
87
|
+
1.meter.should == 100.centimeter
|
88
|
+
end
|
89
|
+
|
90
|
+
it "does not find quantities on different dimensions to be equal" do
|
91
|
+
1.millimeter.should_not == 1.milligram
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context "general" do
|
97
|
+
it "supports abs" do
|
98
|
+
((-(5.seconds)).abs).should == 5.seconds
|
99
|
+
(-5).seconds.abs.should == 5.seconds
|
100
|
+
5.seconds.abs.should == 5.seconds
|
101
|
+
end
|
102
|
+
|
103
|
+
it "supports @-" do
|
104
|
+
(-(35.meters)).should be_close -(114.829396.feet), 10**-5
|
105
|
+
end
|
106
|
+
|
107
|
+
it "supports @+" do
|
108
|
+
+4.kilograms.should == 4.kilograms
|
109
|
+
end
|
110
|
+
|
111
|
+
it "supports %" do
|
112
|
+
(35.meters % 6).should == 5.meters
|
113
|
+
(35.meters % 6.feet).should be_close 0.2528.meters, 10**-5
|
114
|
+
end
|
115
|
+
|
116
|
+
it "supports modulo" do
|
117
|
+
4.kilograms.modulo(15.grams).should == 10.grams
|
118
|
+
end
|
119
|
+
|
120
|
+
it "supports round" do
|
121
|
+
15.6.meters.round.should == 16.meters
|
122
|
+
end
|
123
|
+
|
124
|
+
it "supports truncate" do
|
125
|
+
15.2.meters.truncate.should == 15.meters
|
126
|
+
end
|
127
|
+
|
128
|
+
it "supports ceil" do
|
129
|
+
15.2.meters.ceil.should == 16.meters
|
130
|
+
end
|
131
|
+
|
132
|
+
it "supports floor" do
|
133
|
+
(-5.5.meters).floor.should == -6.meters
|
134
|
+
end
|
135
|
+
|
136
|
+
it "supports divmod" do
|
137
|
+
11.meters.divmod(3).should == [3.meter,2.meter]
|
138
|
+
11.meters.divmod(3.meters).should == [3.meter,2.meter]
|
139
|
+
11.meters.divmod(-3).should == [-4.meter,-1.meter]
|
140
|
+
11.meters.divmod(-3.meters).should == [-4.meter,-1.meter]
|
141
|
+
11.meters.divmod(3.5).should == [3.meter,0.5.meter]
|
142
|
+
11.meters.divmod(3.5.meters).should == [3.meter,0.5.meter]
|
143
|
+
(-11.meters).divmod(3.5).should == [-4.meter,3.0.meter]
|
144
|
+
(-11.meters).divmod(3.5.meters).should == [-4.meter,3.0.meter]
|
145
|
+
11.5.meters.divmod(3.5).should == [3.meter,1.0.meter]
|
146
|
+
11.5.meters.divmod(3.5.meters).should == [3.meter,1.0.meter]
|
147
|
+
end
|
148
|
+
|
149
|
+
it "supports zero?" do
|
150
|
+
0.kilograms.zero?.should == true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "addition and subtraction" do
|
155
|
+
it "adds quantities of the same thing" do
|
156
|
+
(12.meters + 5.meters).should == 17.meters
|
157
|
+
end
|
158
|
+
|
159
|
+
it "adds quantities of the same dimension" do
|
160
|
+
(12.meters + 15.centimeters).should == 1215.centimeters
|
161
|
+
end
|
162
|
+
|
163
|
+
it "adds numerics to quantities" do
|
164
|
+
3.meters.should == 1.meter + 2
|
165
|
+
end
|
166
|
+
|
167
|
+
it "adds quantities to numerics" do
|
168
|
+
6.4.meters.should == 4.4 + 2.meter
|
169
|
+
end
|
170
|
+
|
171
|
+
it "does not add items of different types" do
|
172
|
+
lambda { 12.meters + 24.picograms }.should raise_error TypeError
|
173
|
+
end
|
174
|
+
|
175
|
+
it "adds negative quantities" do
|
176
|
+
(5.meters + (-3.meters)).should == 2.meters
|
177
|
+
end
|
178
|
+
|
179
|
+
it "subtracts quantities of the same thing" do
|
180
|
+
(12.meters - 3.meters).should == 9.meters
|
181
|
+
end
|
182
|
+
|
183
|
+
it "subtracts quantities of the same dimension" do
|
184
|
+
(12.meters - 3650.centimeters).should == -2450.centimeters
|
185
|
+
end
|
186
|
+
|
187
|
+
it "does not add items of different types" do
|
188
|
+
lambda { (12.meters - 3650.picograms)}.should raise_error TypeError
|
189
|
+
end
|
190
|
+
|
191
|
+
it "subtracts numerics from quantities" do
|
192
|
+
(12.meters - 3).should == 9.meters
|
193
|
+
end
|
194
|
+
|
195
|
+
it "subtracts quantities from numerics" do
|
196
|
+
(15 - 5.meters).should == 10.meters
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "multiplication" do
|
201
|
+
|
202
|
+
it "multiplies quantities of the same unit" do
|
203
|
+
(2.meters * 5.meters).should == 10
|
204
|
+
end
|
205
|
+
|
206
|
+
it "multiplies quantities of the same dimension" do
|
207
|
+
(1.meter * 1.foot).should be_close Quantity.new(3.280839,:'foot^2'), 10**-5
|
208
|
+
end
|
209
|
+
|
210
|
+
it "uses the unit on the right when multiplying across the same dimension" do
|
211
|
+
(1.meter * 1.foot).unit.name.should == :'foot^2'
|
212
|
+
end
|
213
|
+
|
214
|
+
it "multiplies complex units" do
|
215
|
+
(3.meter * Quantity.new(1,:'m^2')).should == Quantity.new(3,:'m^3')
|
216
|
+
end
|
217
|
+
|
218
|
+
it "multiplies units of different dimensions" do
|
219
|
+
(2.meters * 2.kilograms).should == Quantity.new(4,:'meter*kilogram')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "division" do
|
224
|
+
it "divides numerics by quantities" do
|
225
|
+
(6 / 2.meters).should == 3.meters
|
226
|
+
end
|
227
|
+
|
228
|
+
it "divides quantities by numerics" do
|
229
|
+
(6.meters / 2).should == 3.meters
|
230
|
+
end
|
231
|
+
|
232
|
+
it "divides quantities of the same unit" do
|
233
|
+
(6.meters / 2.meters).should == 3.meters
|
234
|
+
end
|
235
|
+
|
236
|
+
it "divides quantities of the same dimension" do
|
237
|
+
(6.meters / 2.feet).should == 3.meters
|
238
|
+
end
|
239
|
+
|
240
|
+
it "divides quantities of different dimensions" do
|
241
|
+
(1.kilogram / 1.second).unit.name.should == :'kilogram/second'
|
242
|
+
end
|
243
|
+
|
244
|
+
it "correctly calculates the value of a divided unit" do
|
245
|
+
(10.meters / 2.picograms).should be_close 5, 10**-5
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
context "exponentiation" do
|
251
|
+
it "raises quantities to positive powers" do
|
252
|
+
(2.meters**2).should be_close Quantity.new(4,:'meter^2'), 10**-5
|
253
|
+
end
|
254
|
+
|
255
|
+
it "raises quantities to negative powers" do
|
256
|
+
(2.meters**-1).unit.name.should == :'1/meter'
|
257
|
+
(2.meters**-1).should == 2
|
258
|
+
end
|
259
|
+
|
260
|
+
it "supports a cubed function" do
|
261
|
+
(1.centimeter * 1.centimeter * 1.centimeter).should == 1.centimeter.cubed
|
262
|
+
end
|
263
|
+
|
264
|
+
it "supports a squared function" do
|
265
|
+
(1.centimeter * 1.centimeter).should == 1.centimeter.squared
|
266
|
+
end
|
267
|
+
|
268
|
+
it "does not raise to fractional powers" do
|
269
|
+
lambda {2.meters**1.5}.should raise_error ArgumentError
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context "enumerable" do
|
274
|
+
it "should be comparable" do
|
275
|
+
2.meters.should be < 3.meters
|
276
|
+
150.centimeters.should be > 1.meter
|
277
|
+
[1.meter, 1.foot, 1.inch].sort.should == [1.inch, 1.foot, 1.meter]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end
|