rails-units 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ # some rubies return an array of strings for .instance_methods and others return an array of symbols
4
+ # so let's stringify them before we compare
5
+ describe Numeric do
6
+ specify { Float.instance_methods.map {|m| m.to_s}.should include("to_unit") }
7
+ specify { Integer.instance_methods.map {|m| m.to_s}.should include("to_unit") }
8
+ specify { Fixnum.instance_methods.map {|m| m.to_s}.should include("to_unit") }
9
+ specify { Complex.instance_methods.map {|m| m.to_s}.should include("to_unit") }
10
+ specify { Bignum.instance_methods.map {|m| m.to_s}.should include("to_unit") }
11
+ specify { Rational.instance_methods.map {|m| m.to_s}.should include("to_unit") }
12
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Object do
4
+ specify { Unit('1 mm').should be_instance_of Unit}
5
+ specify { U('1 mm').should be_instance_of Unit}
6
+ specify { u('1 mm').should be_instance_of Unit}
7
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe String do
4
+ context "Unit creation from strings" do
5
+ specify { "1 mm".to_unit.should be_instance_of Unit }
6
+ specify { "1 mm".unit.should be_instance_of Unit }
7
+ specify { "1 mm".u.should be_instance_of Unit }
8
+ specify { "1 m".to("ft").should be_within(Unit("0.01 ft")).of Unit("3.28084 ft") }
9
+ end
10
+
11
+ context "Time syntax sugar" do
12
+ before(:each) do
13
+ Time.stub(:now).and_return(Time.at(1303656390))
14
+ Date.stub(:today).and_return(Date.new(2011,4,1))
15
+ DateTime.stub(:now).and_return(DateTime.new(2011,4,1,0,0,0))
16
+ end
17
+ specify { "5 min".ago.should be_instance_of Time }
18
+
19
+ specify { "5 min".from.should be_instance_of Time }
20
+ specify { "5 min".from('now').should be_instance_of Time }
21
+ specify { "5 min".from_now.should be_instance_of Time }
22
+
23
+ specify { "5 min".after('12:00').should be_instance_of Time }
24
+
25
+ specify { "5 min".before.should be_instance_of Time}
26
+ specify { "5 min".before('now').should be_instance_of Time}
27
+ specify { "5 min".before_now.should be_instance_of Time}
28
+ specify { "5 min".before('12:00').should be_instance_of Time}
29
+
30
+ specify { "min".since.should be_instance_of Unit}
31
+ specify { "min".since("12:00").should be_instance_of Unit}
32
+ specify { "min".since(Time.now - 60).should == Unit("1 min")}
33
+ specify { "days".since(Date.today - 3).should == Unit("3 d")}
34
+ specify { expect {"days".since(1000) }.to raise_error(ArgumentError, "Must specify a Time, DateTime, or String")}
35
+
36
+ specify { "min".until.should be_instance_of Unit}
37
+ specify { "min".until("12:00").should be_instance_of Unit}
38
+ specify { "min".until(Time.now + 60).should == Unit("1 min")}
39
+ specify { "days".until(Date.today + 3).should == Unit("3 d")}
40
+ specify { expect {"days".until(1000) }.to raise_error(ArgumentError, "Must specify a Time, DateTime, or String")}
41
+
42
+ specify { "today".to_date.should be_instance_of Date }
43
+ specify { "2011-4-1".to_date.should be_instance_of Date }
44
+
45
+ specify { "now".to_datetime.should be_instance_of DateTime }
46
+ specify { "now".to_time.should be_instance_of Time }
47
+
48
+ specify { "10001-01-01 12:00".time.should be_instance_of Time }
49
+ specify { "2001-01-01 12:00".time.should be_instance_of Time }
50
+
51
+ end
52
+
53
+ context "output format" do
54
+ subject { Unit("1.23456 m/s^2") }
55
+ specify { ("" % subject).should == ""}
56
+ specify { ("%0.2f" % subject).should == "1.23 m/s^2"}
57
+ specify { ("%0.2f km/h^2" % subject).should == "15999.90 km/h^2"}
58
+ specify { ("km/h^2" % subject).should == "15999.9 km/h^2"}
59
+ specify { ("%H:%M:%S" % Unit("1.5 h")).should == "01:30:00"}
60
+ end
61
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Time do
4
+ before(:each) do
5
+ Time.stub(:now).and_return(Time.at(1303656390))
6
+ end
7
+
8
+ context ".at" do
9
+ subject { Date.new(2011,4,1).to_unit }
10
+ specify { Time.at(subject - Date.new(1970,1,1)).strftime("%D %T").should == "03/31/11 20:00:00"}
11
+ end
12
+
13
+ context ".in" do
14
+ specify { Time.in("5 min").should be_a Time}
15
+ specify { Time.in("5 min").should > Time.now}
16
+ end
17
+
18
+ context 'addition (+)' do
19
+ specify { (Time.now + 1).should == Time.at(1303656390 + 1)}
20
+ specify { (Time.now + Unit("10 min")).should == Time.at(1303656390 + 600)}
21
+ end
22
+
23
+ context 'subtraction (-)' do
24
+ specify { (Time.now - 1).should == Time.at(1303656390 - 1)}
25
+ specify { (Time.now - Unit("10 min")).should == Time.at(1303656390 - 600)}
26
+ end
27
+
28
+ end
@@ -0,0 +1,888 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "Create some simple units" do
4
+
5
+ describe Unit("0") do
6
+ it {should be_a Numeric}
7
+ it {should be_an_instance_of Unit}
8
+ its(:scalar) {should === 0}
9
+ its(:scalar) {should be_an Integer}
10
+ its(:units) {should be_empty}
11
+ its(:kind) {should == :unitless}
12
+ it {should_not be_temperature}
13
+ it {should_not be_degree}
14
+ it {should be_base}
15
+ it {should be_unitless}
16
+ it {should be_zero}
17
+ its(:base) {should == subject}
18
+ end
19
+
20
+ describe Unit("1") do
21
+ it {should be_a Numeric}
22
+ it {should be_an_instance_of Unit}
23
+ its(:scalar) {should === 1}
24
+ its(:scalar) {should be_an Integer}
25
+ its(:units) {should be_empty}
26
+ its(:kind) {should == :unitless}
27
+ it {should_not be_temperature}
28
+ it {should_not be_degree}
29
+ it {should be_base}
30
+ it {should be_unitless}
31
+ it {should_not be_zero}
32
+ its(:base) {should == subject}
33
+ end
34
+
35
+ describe Unit(1) do
36
+ it {should be_a Numeric}
37
+ it {should be_an_instance_of Unit}
38
+ its(:scalar) {should === 1}
39
+ its(:scalar) {should be_an Integer}
40
+ its(:units) {should be_empty}
41
+ its(:kind) {should == :unitless}
42
+ it {should_not be_temperature}
43
+ it {should_not be_degree}
44
+ it {should be_base}
45
+ it {should be_unitless}
46
+ it {should_not be_zero}
47
+ its(:base) {should == subject}
48
+ end
49
+
50
+ describe Unit(Rational(1,2)) do
51
+ it {should be_a Numeric}
52
+ it {should be_an_instance_of Unit}
53
+ its(:scalar) {should === Rational(1,2)}
54
+ its(:scalar) {should be_a Rational}
55
+ its(:units) {should be_empty}
56
+ its(:kind) {should == :unitless}
57
+ it {should_not be_temperature}
58
+ it {should_not be_degree}
59
+ it {should be_base}
60
+ it {should be_unitless}
61
+ it {should_not be_zero}
62
+ its(:base) {should == subject}
63
+ end
64
+
65
+ describe Unit(0.5) do
66
+ it {should be_a Numeric}
67
+ it {should be_an_instance_of Unit}
68
+ its(:scalar) {should === 0.5}
69
+ its(:scalar) {should be_a Float}
70
+ its(:units) {should be_empty}
71
+ its(:kind) {should == :unitless}
72
+ it {should_not be_temperature}
73
+ it {should_not be_degree}
74
+ it {should be_base}
75
+ it {should be_unitless}
76
+ it {should_not be_zero}
77
+ its(:base) {should == subject}
78
+ end
79
+
80
+ describe Unit(Complex(1,1)) do
81
+ it {should be_a Numeric}
82
+ it {should be_an_instance_of Unit}
83
+ its(:scalar) {should === Complex(1,1)}
84
+ its(:scalar) {should be_a Complex}
85
+ its(:units) {should be_empty}
86
+ its(:kind) {should == :unitless}
87
+ it {should_not be_temperature}
88
+ it {should_not be_degree}
89
+ it {should be_base}
90
+ it {should be_unitless}
91
+ it {should_not be_zero}
92
+ its(:base) {should == subject}
93
+ end
94
+
95
+ describe Unit("1 mm") do
96
+ it {should be_a Numeric}
97
+ it {should be_an_instance_of Unit}
98
+ its(:scalar) {should == 1}
99
+ its(:scalar) {should be_an Integer}
100
+ its(:units) {should == "mm"}
101
+ its(:kind) {should == :length}
102
+ it {should_not be_temperature}
103
+ it {should_not be_degree}
104
+ it {should_not be_base}
105
+ it {should_not be_unitless}
106
+ it {should_not be_zero}
107
+ its(:base) {should == Unit("0.001 m")}
108
+ end
109
+
110
+ describe Unit("10 m/s^2") do
111
+ it {should be_an_instance_of Unit}
112
+ its(:scalar) {should == 10}
113
+ its(:scalar) {should be_an Integer}
114
+ its(:units) {should == "m/s^2"}
115
+ its(:kind) {should == :acceleration}
116
+ it {should_not be_temperature}
117
+ it {should_not be_degree}
118
+ it {should be_base}
119
+ it {should_not be_unitless}
120
+ it {should_not be_zero}
121
+ its(:base) {should == Unit("10 m/s^2")}
122
+ end
123
+
124
+ describe Unit("5ft 6in") do
125
+ it {should be_an_instance_of Unit}
126
+ its(:scalar) {should == 5.5}
127
+ its(:units) {should == "ft"}
128
+ its(:kind) {should == :length}
129
+ it {should_not be_temperature}
130
+ it {should_not be_degree}
131
+ it {should_not be_base}
132
+ it {should_not be_unitless}
133
+ it {should_not be_zero}
134
+ its(:base) {should be_within(Unit("0.01 m")).of Unit("1.6764 m")}
135
+ specify { subject.to_s(:ft).should == %{5'6"} }
136
+ end
137
+
138
+ describe Unit("6lbs 5oz") do
139
+ it {should be_an_instance_of Unit}
140
+ its(:scalar) {should be_within(0.001).of 6.312}
141
+ its(:units) {should == "lbs"}
142
+ its(:kind) {should == :mass}
143
+ it {should_not be_temperature}
144
+ it {should_not be_degree}
145
+ it {should_not be_base}
146
+ it {should_not be_unitless}
147
+ it {should_not be_zero}
148
+ its(:base) {should be_within(Unit("0.01 kg")).of Unit("2.8633 kg")}
149
+ specify { subject.to_s(:lbs).should == "6 lbs, 5 oz" }
150
+ end
151
+
152
+ describe Unit("100 tempC") do
153
+ it {should be_an_instance_of Unit}
154
+ its(:scalar) {should be_within(0.001).of 100}
155
+ its(:units) {should == "tempC"}
156
+ its(:kind) {should == :temperature}
157
+ it {should be_temperature}
158
+ it {should be_degree}
159
+ it {should_not be_base}
160
+ it {should_not be_unitless}
161
+ it {should_not be_zero}
162
+ its(:base) {should be_within(Unit("0.01 degK")).of Unit("373.15 tempK")}
163
+ its(:temperature_scale) {should == "degC"}
164
+ end
165
+
166
+ describe Unit(Time.now) do
167
+ it {should be_an_instance_of Unit}
168
+ its(:scalar) {should be_a(Numeric)}
169
+ its(:units) {should == "s"}
170
+ its(:kind) {should == :time}
171
+ it {should_not be_temperature}
172
+ it {should_not be_degree}
173
+ it {should be_base}
174
+ it {should_not be_unitless}
175
+ it {should_not be_zero}
176
+ its(:base) {should be_a(Numeric)}
177
+ its(:temperature_scale) {should be_nil}
178
+ end
179
+
180
+ describe Unit("100 degC") do
181
+ it {should be_an_instance_of Unit}
182
+ its(:scalar) {should be_within(0.001).of 100}
183
+ its(:units) {should == "degC"}
184
+ its(:kind) {should == :temperature}
185
+ it {should_not be_temperature}
186
+ it {should be_degree}
187
+ it {should_not be_base}
188
+ it {should_not be_unitless}
189
+ its(:base) {should be_within(Unit("0.01 degK")).of Unit("100 degK")}
190
+ end
191
+
192
+ describe Unit("75%") do
193
+ it {should be_an_instance_of Unit}
194
+ its(:scalar) {should be_an Integer}
195
+ its(:units) {should == "%"}
196
+ its(:kind) {should == :unitless}
197
+ it {should_not be_temperature}
198
+ it {should_not be_degree}
199
+ it {should_not be_base}
200
+ it {should_not be_unitless}
201
+ it {should_not be_zero}
202
+ its(:base) {should be_a(Numeric)}
203
+ its(:temperature_scale) {should be_nil}
204
+ end
205
+
206
+ describe Unit("180 deg") do
207
+ it {should be_an_instance_of Unit}
208
+ its(:scalar) {should be_a Numeric}
209
+ its(:units) {should == "deg"}
210
+ its(:kind) {should == :angle}
211
+ it {should_not be_temperature}
212
+ it {should_not be_degree}
213
+ it {should_not be_base}
214
+ it {should_not be_unitless}
215
+ it {should_not be_zero}
216
+ its(:base) {should be_a(Numeric)}
217
+ its(:temperature_scale) {should be_nil}
218
+ end
219
+
220
+ describe Unit("1 radian") do
221
+ it {should be_an_instance_of Unit}
222
+ its(:scalar) {should be_a Numeric}
223
+ its(:units) {should == "rad"}
224
+ its(:kind) {should == :angle}
225
+ it {should_not be_temperature}
226
+ it {should_not be_degree}
227
+ it {should be_base}
228
+ it {should_not be_unitless}
229
+ it {should_not be_zero}
230
+ its(:base) {should be_a Numeric}
231
+ its(:temperature_scale) {should be_nil}
232
+ end
233
+
234
+ describe Unit("12 dozen") do
235
+ it {should be_an_instance_of Unit}
236
+ its(:scalar) {should be_an Integer}
237
+ its(:units) {should == "doz"}
238
+ its(:kind) {should == :unitless}
239
+ it {should_not be_temperature}
240
+ it {should_not be_degree}
241
+ it {should_not be_base}
242
+ it {should_not be_unitless}
243
+ it {should_not be_zero}
244
+ its(:base) {should be_a Numeric}
245
+ its(:temperature_scale) {should be_nil}
246
+ end
247
+
248
+ describe Unit("1/2 kg") do
249
+ it {should be_an_instance_of Unit}
250
+ its(:scalar) {should be_an Rational}
251
+ its(:units) {should == "kg"}
252
+ its(:kind) {should == :mass}
253
+ it {should_not be_temperature}
254
+ it {should_not be_degree}
255
+ it {should be_base}
256
+ it {should_not be_unitless}
257
+ it {should_not be_zero}
258
+ its(:base) {should be_a Numeric}
259
+ its(:temperature_scale) {should be_nil}
260
+ end
261
+
262
+ describe Unit("1/2 kg/m") do
263
+ it {should be_an_instance_of Unit}
264
+ its(:scalar) {should be_an Rational}
265
+ its(:units) {should == "kg/m"}
266
+ its(:kind) {should be_nil}
267
+ it {should_not be_temperature}
268
+ it {should_not be_degree}
269
+ it {should be_base}
270
+ it {should_not be_unitless}
271
+ it {should_not be_zero}
272
+ its(:base) {should be_a Numeric}
273
+ its(:temperature_scale) {should be_nil}
274
+ end
275
+
276
+ describe Unit("1:23:45") do
277
+ it {should be_an_instance_of Unit}
278
+ its(:scalar) {should be_an Rational}
279
+ its(:units) {should == "h"}
280
+ its(:kind) {should == :time}
281
+ it {should_not be_temperature}
282
+ it {should_not be_degree}
283
+ it {should_not be_base}
284
+ it {should_not be_unitless}
285
+ it {should_not be_zero}
286
+ its(:base) {should be_a Numeric}
287
+ its(:temperature_scale) {should be_nil}
288
+ end
289
+
290
+ # also '1 hours as minutes'
291
+ # '1 hour to minutes'
292
+ describe Unit.parse("1 hour in minutes") do
293
+ it {should be_an_instance_of Unit}
294
+ its(:scalar) {should be_an Integer}
295
+ its(:units) {should == "min"}
296
+ its(:kind) {should == :time}
297
+ it {should_not be_temperature}
298
+ it {should_not be_degree}
299
+ it {should_not be_base}
300
+ it {should_not be_unitless}
301
+ it {should_not be_zero}
302
+ its(:base) {should be_a Numeric}
303
+ its(:temperature_scale) {should be_nil}
304
+ end
305
+
306
+ describe Unit.new("1 attoparsec/microfortnight") do
307
+ it {should be_an_instance_of Unit}
308
+ its(:scalar) {should be_an Integer}
309
+ its(:units) {should == "apc/ufortnight"}
310
+ its(:kind) {should == :speed}
311
+ it {should_not be_temperature}
312
+ it {should_not be_degree}
313
+ it {should_not be_base}
314
+ it {should_not be_unitless}
315
+ it {should_not be_zero}
316
+ its(:base) {should be_a Numeric}
317
+ its(:temperature_scale) {should be_nil}
318
+ it { subject.to("in/s").should be_within(Unit("0.0001 in/s")).of(Unit("1.0043269330917 in/s"))}
319
+ end
320
+
321
+
322
+ end
323
+
324
+ describe "Unit handles attempts to create bad units" do
325
+ specify "no empty strings" do
326
+ expect {Unit("")}.to raise_error(ArgumentError,"No Unit Specified")
327
+ end
328
+
329
+ specify "no blank strings" do
330
+ expect {Unit(" ")}.to raise_error(ArgumentError,"No Unit Specified")
331
+ end
332
+
333
+ specify "no strings with tabs" do
334
+ expect {Unit("\t")}.to raise_error(ArgumentError,"No Unit Specified")
335
+ end
336
+
337
+ specify "no strings with newlines" do
338
+ expect {Unit("\n")}.to raise_error(ArgumentError,"No Unit Specified")
339
+ end
340
+
341
+ specify "no strings that don't specify a valid unit" do
342
+ expect {Unit("random string")}.to raise_error(ArgumentError,"'random string' Unit not recognized")
343
+ end
344
+
345
+ specify "no unhandled classes" do
346
+ expect {Unit(STDIN)}.to raise_error(ArgumentError,"Invalid Unit Format")
347
+ end
348
+
349
+ specify "no undefined units" do
350
+ expect {Unit("1 mFoo")}.to raise_error(ArgumentError,"'1 mFoo' Unit not recognized")
351
+ end
352
+
353
+ specify "no units with powers greater than 19" do
354
+ expect {Unit("1 m^20")}.to raise_error(ArgumentError, "Power out of range (-20 < net power of a unit < 20)")
355
+ end
356
+
357
+ specify "no units with powers less than 19" do
358
+ expect {Unit("1 m^-20")}.to raise_error(ArgumentError, "Power out of range (-20 < net power of a unit < 20)")
359
+ end
360
+
361
+ specify "no temperatures less than absolute zero" do
362
+ expect {Unit("-100 tempK")}.to raise_error(ArgumentError,"Temperatures must not be less than absolute zero")
363
+ expect {Unit("-100 tempR")}.to raise_error(ArgumentError,"Temperatures must not be less than absolute zero")
364
+ expect {Unit("-500/9 tempR")}.to raise_error(ArgumentError,"Temperatures must not be less than absolute zero")
365
+ end
366
+
367
+ end
368
+
369
+ describe Unit do
370
+ it "is a subclass of Numeric" do
371
+ described_class.should < Numeric
372
+ end
373
+ it "is Comparable" do
374
+ described_class.should < Comparable
375
+ end
376
+ end
377
+
378
+ describe "Unit Comparisons" do
379
+ context "Unit should detect if two units are 'compatible' (i.e., can be converted into each other)" do
380
+ specify { Unit("1 ft").should =~ Unit('1 m')}
381
+ specify { Unit("1 ft").should =~ "m"}
382
+ specify { Unit("1 ft").should be_compatible_with Unit('1 m')}
383
+ specify { Unit("1 ft").should be_compatible_with "m"}
384
+ specify { Unit("1 m").should be_compatible_with Unit('1 kg*m/kg')}
385
+ specify { Unit("1 ft").should_not =~ Unit('1 kg')}
386
+ specify { Unit("1 ft").should_not be_compatible_with Unit('1 kg')}
387
+ end
388
+
389
+ context "Equality" do
390
+ context "units of same kind" do
391
+ specify { Unit("1000 m").should == Unit('1 km')}
392
+ specify { Unit("100 m").should_not == Unit('1 km')}
393
+ specify { Unit("1 m").should == Unit('100 cm')}
394
+ end
395
+
396
+ context "units of incompatible types" do
397
+ specify { Unit("1 m").should_not == Unit("1 kg")}
398
+ end
399
+
400
+ context "units with a zero scalar are equal" do
401
+ specify {Unit("0 m").should == Unit("0 s")}
402
+ specify {Unit("0 m").should == Unit("0 kg")}
403
+
404
+ context "except for temperature units" do
405
+ specify {Unit("0 tempK").should == Unit("0 m")}
406
+ specify {Unit("0 tempR").should == Unit("0 m")}
407
+ specify {Unit("0 tempC").should_not == Unit("0 m")}
408
+ specify {Unit("0 tempF").should_not == Unit("0 m")}
409
+ end
410
+ end
411
+ end
412
+
413
+ context "Equivalence" do
414
+ context "units and scalars are the exactly the same" do
415
+ specify { Unit("1 m").should === Unit("1 m")}
416
+ specify { Unit("1 m").should be_same Unit("1 m")}
417
+ specify { Unit("1 m").should be_same_as Unit("1 m")}
418
+ end
419
+
420
+ context "units are compatible but not identical" do
421
+ specify { Unit("1000 m").should_not === Unit("1 km")}
422
+ specify { Unit("1000 m").should_not be_same Unit("1 km")}
423
+ specify { Unit("1000 m").should_not be_same_as Unit("1 km")}
424
+ end
425
+
426
+ context "units are not compatible" do
427
+ specify { Unit("1000 m").should_not === Unit("1 hour")}
428
+ specify { Unit("1000 m").should_not be_same Unit("1 hour")}
429
+ specify { Unit("1000 m").should_not be_same_as Unit("1 hour")}
430
+ end
431
+
432
+ context "scalars are different" do
433
+ specify { Unit("1 m").should_not === Unit("2 m")}
434
+ specify { Unit("1 m").should_not be_same Unit("2 m")}
435
+ specify { Unit("1 m").should_not be_same_as Unit("2 m")}
436
+ end
437
+ end
438
+
439
+ context "Comparisons" do
440
+ context "compatible units can be compared" do
441
+ specify { Unit("1 m").should < Unit("2 m")}
442
+ specify { Unit("2 m").should > Unit("1 m")}
443
+ specify { Unit("1 m").should < Unit("1 mi")}
444
+ specify { Unit("2 m").should > Unit("1 ft")}
445
+ specify { Unit("70 tempF").should > Unit("10 degC")}
446
+ specify { Unit("1 m").should > 0 }
447
+ end
448
+
449
+ context "incompatible units cannot be compared" do
450
+ specify { expect { Unit("1 m") < Unit("1 liter")}.to raise_error(ArgumentError,"Incompatible Units (m !~ l)")}
451
+ specify { expect { Unit("1 kg") > Unit("60 mph")}.to raise_error(ArgumentError,"Incompatible Units (kg !~ mph)")}
452
+ end
453
+ end
454
+
455
+ end
456
+
457
+ describe "Unit Conversions" do
458
+
459
+ context "between compatible units" do
460
+ specify { Unit("1 s").to("ns").should == Unit("1e9 ns")}
461
+ specify { Unit("1 s").convert_to("ns").should == Unit("1e9 ns")}
462
+ specify { (Unit("1 s") >> "ns").should == Unit("1e9 ns")}
463
+
464
+ specify { Unit("1 m").to(Unit("ft")).should be_within(Unit("0.001 ft")).of(Unit("3.28084 ft"))}
465
+ end
466
+
467
+ context "between incompatible units" do
468
+ specify { expect { Unit("1 s").to("m")}.to raise_error(ArgumentError,"Incompatible Units")}
469
+ end
470
+
471
+ context "given bad input" do
472
+ specify { expect { Unit("1 m").to("random string")}.to raise_error(ArgumentError,"'random string' Unit not recognized")}
473
+ specify { expect { Unit("1 m").to(STDOUT)}.to raise_error(ArgumentError,"Unknown target units")}
474
+ end
475
+
476
+ context "between temperature scales" do
477
+ # note that 'temp' units are for temperature readings on a scale, while 'deg' units are used to represent
478
+ # differences between temperatures, offsets, or other differential temperatures.
479
+
480
+ specify { Unit("100 tempC").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
481
+ specify { Unit("0 tempC").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
482
+ specify { Unit("37 tempC").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
483
+ specify { Unit("-273.15 tempC").should == Unit("0 tempK") }
484
+
485
+ specify { Unit("212 tempF").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
486
+ specify { Unit("32 tempF").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
487
+ specify { Unit("98.6 tempF").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
488
+ specify { Unit("-459.67 tempF").should == Unit("0 tempK") }
489
+
490
+ specify { Unit("671.67 tempR").should be_within(Unit("0.001 degK")).of(Unit("373.15 tempK")) }
491
+ specify { Unit("491.67 tempR").should be_within(Unit("0.001 degK")).of(Unit("273.15 tempK")) }
492
+ specify { Unit("558.27 tempR").should be_within(Unit("0.01 degK")).of(Unit("310.15 tempK"))}
493
+ specify { Unit("0 tempR").should == Unit("0 tempK") }
494
+
495
+ specify { Unit("100 tempK").to("tempC").should be_within(U"0.01 degC").of(Unit("-173.15 tempC"))}
496
+ specify { Unit("100 tempK").to("tempF").should be_within(U"0.01 degF").of(Unit("-279.67 tempF"))}
497
+ specify { Unit("100 tempK").to("tempR").should be_within(U"0.01 degR").of(Unit("180 tempR"))}
498
+
499
+ specify { Unit("1 degC").should == Unit("1 degK")}
500
+ specify { Unit("1 degF").should == Unit("1 degR")}
501
+ specify { Unit("1 degC").should == Unit("1.8 degR")}
502
+ specify { Unit("1 degF").should be_within(Unit("0.001 degK")).of(Unit("0.5555 degK"))}
503
+ end
504
+ end
505
+
506
+ describe "Unit Math" do
507
+ context "operators:" do
508
+ context "addition (+)" do
509
+ context "between compatible units" do
510
+ specify { (Unit("0 m") + Unit("10 m")).should == Unit("10 m")}
511
+ specify { (Unit("5 kg") + Unit("10 kg")).should == Unit("15 kg")}
512
+ end
513
+
514
+ context "between a zero unit and another unit" do
515
+ specify { (Unit("0 kg") + Unit("10 m")).should == Unit("10 m")}
516
+ specify { (Unit("0 m") + Unit("10 kg")).should == Unit("10 kg")}
517
+ end
518
+
519
+ context "between incompatible units" do
520
+ specify { expect {Unit("10 kg") + Unit("10 m")}.to raise_error(ArgumentError)}
521
+ specify { expect {Unit("10 m") + Unit("10 kg")}.to raise_error(ArgumentError)}
522
+ end
523
+
524
+ context "a number from a unit" do
525
+ specify { expect { Unit("10 kg") + 1 }.to raise_error(ArgumentError)}
526
+ specify { expect { 10 + Unit("10 kg") }.to raise_error(ArgumentError)}
527
+ end
528
+
529
+ context "between two temperatures" do
530
+ specify { expect {(Unit("100 tempK") + Unit("100 tempK"))}.to raise_error(ArgumentError,"Cannot add two temperatures") }
531
+ end
532
+
533
+ context "between a temperature and a degree" do
534
+ specify { (Unit("100 tempK") + Unit("100 degK")).should == Unit("200 tempK") }
535
+ end
536
+
537
+ context "between a degree and a temperature" do
538
+ specify { (Unit("100 degK") + Unit("100 tempK")).should == Unit("200 tempK") }
539
+ end
540
+
541
+ end
542
+
543
+ context "subtracting (-)" do
544
+ context "compatible units" do
545
+ specify { (Unit("0 m") - Unit("10 m")).should == Unit("-10 m")}
546
+ specify { (Unit("5 kg") - Unit("10 kg")).should == Unit("-5 kg")}
547
+ end
548
+
549
+ context "a unit from a zero unit" do
550
+ specify { (Unit("0 kg") - Unit("10 m")).should == Unit("-10 m")}
551
+ specify { (Unit("0 m") - Unit("10 kg")).should == Unit("-10 kg")}
552
+ end
553
+
554
+ context "incompatible units" do
555
+ specify { expect {Unit("10 kg") - Unit("10 m")}.to raise_error(ArgumentError)}
556
+ specify { expect {Unit("10 m") - Unit("10 kg")}.to raise_error(ArgumentError)}
557
+ end
558
+
559
+ context "a number from a unit" do
560
+ specify { expect { Unit("10 kg") - 1 }.to raise_error(ArgumentError)}
561
+ specify { expect { 10 - Unit("10 kg") }.to raise_error(ArgumentError)}
562
+ end
563
+
564
+ context "between two temperatures" do
565
+ specify { (Unit("100 tempK") - Unit("100 tempK")).should == Unit("0 degK") }
566
+ end
567
+
568
+ context "between a temperature and a degree" do
569
+ specify { (Unit("100 tempK") - Unit("100 degK")).should == Unit("0 tempK") }
570
+ end
571
+
572
+ context "between a degree and a temperature" do
573
+ specify { expect {(Unit("100 degK") - Unit("100 tempK"))}.to raise_error(ArgumentError,"Cannot subtract a temperature from a differential degree unit")}
574
+ end
575
+
576
+ end
577
+
578
+ context "multiplying (*)" do
579
+ context "between compatible units" do
580
+ specify { (Unit("0 m") * Unit("10 m")).should == Unit("0 m^2")}
581
+ specify { (Unit("5 kg") * Unit("10 kg")).should == Unit("50 kg^2")}
582
+ end
583
+
584
+ context "between incompatible units" do
585
+ specify { (Unit("0 m") * Unit("10 kg")).should == Unit("0 kg*m")}
586
+ specify { (Unit("5 m") * Unit("10 kg")).should == Unit("50 kg*m")}
587
+ end
588
+
589
+ context "by a temperature" do
590
+ specify { expect { Unit("5 kg") * Unit("100 tempF")}.to raise_exception(ArgumentError) }
591
+ end
592
+
593
+ context "by a number" do
594
+ specify { (10 * Unit("5 kg")).should == Unit("50 kg")}
595
+ end
596
+
597
+ end
598
+
599
+ context "dividing (/)" do
600
+ context "compatible units" do
601
+ specify { (Unit("0 m") / Unit("10 m")).should == Unit(0)}
602
+ specify { (Unit("5 kg") / Unit("10 kg")).should == Rational(1,2)}
603
+ end
604
+
605
+ context "incompatible units" do
606
+ specify { (Unit("0 m") / Unit("10 kg")).should == Unit("0 m/kg")}
607
+ specify { (Unit("5 m") / Unit("10 kg")).should == Unit("1/2 m/kg")}
608
+ end
609
+
610
+ context "by a temperature" do
611
+ specify { expect { Unit("5 kg") / Unit("100 tempF")}.to raise_exception(ArgumentError) }
612
+ end
613
+
614
+ context "a number by a unit" do
615
+ specify { (10 / Unit("5 kg")).should == Unit("2 1/kg")}
616
+ end
617
+
618
+ context "a unit by a number" do
619
+ specify { (Unit("5 kg") / 2).should == Unit("2.5 kg")}
620
+ end
621
+
622
+ context "by zero" do
623
+ specify { expect { Unit("10 m") / 0}.to raise_error(ZeroDivisionError)}
624
+ specify { expect { Unit("10 m") / Unit("0 m")}.to raise_error(ZeroDivisionError)}
625
+ specify { expect { Unit("10 m") / Unit("0 kg")}.to raise_error(ZeroDivisionError)}
626
+ end
627
+ end
628
+
629
+ context "exponentiating (**)" do
630
+
631
+ specify "a temperature raises an execption" do
632
+ expect { Unit("100 tempK")**2 }.to raise_error(ArgumentError,"Cannot raise a temperature to a power")
633
+ end
634
+
635
+ context Unit("0 m") do
636
+ it { (subject**1).should == subject }
637
+ it { (subject**2).should == subject }
638
+ end
639
+
640
+ context Unit("1 m") do
641
+ it { (subject**0).should == 1 }
642
+ it { (subject**1).should == subject }
643
+ it { (subject**(-1)).should == 1/subject }
644
+ it { (subject**(2)).should == Unit("1 m^2")}
645
+ it { (subject**(-2)).should == Unit("1 1/m^2")}
646
+ specify { expect { subject**(1/2)}.to raise_error(ArgumentError, "Illegal root")}
647
+ # because 1 m^(1/2) doesn't make any sense
648
+ specify { expect { subject**(Complex(1,1))}.to raise_error(ArgumentError, "exponentiation of complex numbers is not yet supported.")}
649
+ specify { expect { subject**(Unit("1 m"))}.to raise_error(ArgumentError, "Invalid Exponent")}
650
+ end
651
+
652
+ context Unit("1 m^2") do
653
+ it { (subject**(Rational(1,2))).should == Unit("1 m")}
654
+ it { (subject**(0.5)).should == Unit("1 m")}
655
+
656
+ specify { expect { subject**(0.12345) }.to raise_error(ArgumentError,"Not a n-th root (1..9), use 1/n")}
657
+ specify { expect { subject**("abcdefg") }.to raise_error(ArgumentError,"Invalid Exponent")}
658
+ end
659
+
660
+ end
661
+
662
+ context "modulo (%)" do
663
+ context "compatible units" do
664
+ specify { (Unit("2 m") % Unit("1 m")).should == 0 }
665
+ specify { (Unit("5 m") % Unit("2 m")).should == 1 }
666
+ end
667
+
668
+ specify "incompatible units raises an exception" do
669
+ expect { Unit("1 m") % Unit("1 kg")}.to raise_error(ArgumentError,"Incompatible Units")
670
+ end
671
+ end
672
+ end
673
+
674
+ context "#power" do
675
+ subject { Unit("1 m") }
676
+ it "raises an exception when passed a Float argument" do
677
+ expect {subject.power(1.5)}.to raise_error(ArgumentError,"Exponent must an Integer")
678
+ end
679
+ it "raises an exception when passed a Rational argument" do
680
+ expect {subject.power(Rational(1,2))}.to raise_error(ArgumentError,"Exponent must an Integer")
681
+ end
682
+ it "raises an exception when passed a Complex argument" do
683
+ expect {subject.power(Complex(1,2))}.to raise_error(ArgumentError,"Exponent must an Integer")
684
+ end
685
+ it "raises an exception when called on a temperature unit" do
686
+ expect { Unit("100 tempC").power(2)}.to raise_error(ArgumentError,"Cannot raise a temperature to a power")
687
+ end
688
+
689
+ specify { (subject.power(-1)).should == Unit("1 1/m") }
690
+ specify { (subject.power(0)).should == 1 }
691
+ specify { (subject.power(1)).should == subject }
692
+ specify { (subject.power(2)).should == Unit("1 m^2") }
693
+
694
+ end
695
+
696
+ context "#root" do
697
+ subject { Unit("1 m") }
698
+ it "raises an exception when passed a Float argument" do
699
+ expect {subject.root(1.5)}.to raise_error(ArgumentError,"Exponent must an Integer")
700
+ end
701
+ it "raises an exception when passed a Rational argument" do
702
+ expect {subject.root(Rational(1,2))}.to raise_error(ArgumentError,"Exponent must an Integer")
703
+ end
704
+ it "raises an exception when passed a Complex argument" do
705
+ expect {subject.root(Complex(1,2))}.to raise_error(ArgumentError,"Exponent must an Integer")
706
+ end
707
+ it "raises an exception when called on a temperature unit" do
708
+ expect { Unit("100 tempC").root(2)}.to raise_error(ArgumentError,"Cannot take the root of a temperature")
709
+ end
710
+
711
+ specify { (Unit("1 m^2").root(-2)).should == Unit("1 1/m") }
712
+ specify { (subject.root(-1)).should == Unit("1 1/m") }
713
+ specify { expect {(subject.root(0))}.to raise_error(ArgumentError, "0th root undefined")}
714
+ specify { (subject.root(1)).should == subject }
715
+ specify { (Unit("1 m^2").root(2)).should == Unit("1 m") }
716
+
717
+ end
718
+
719
+ context "#inverse" do
720
+ specify { Unit("1 m").inverse.should == Unit("1 1/m") }
721
+ specify { expect {Unit("100 tempK").inverse}.to raise_error(ArgumentError,"Cannot divide with temperatures") }
722
+ end
723
+
724
+ context "convert to scalars" do
725
+ specify {Unit("10").to_i.should be_kind_of(Integer)}
726
+ specify { expect { Unit("10 m").to_i }.to raise_error(RuntimeError,"Cannot convert '10 m' to Integer unless unitless. Use Unit#scalar") }
727
+
728
+ specify {Unit("10.0").to_f.should be_kind_of(Float)}
729
+ specify { expect { Unit("10.0 m").to_f }.to raise_error(RuntimeError,"Cannot convert '10 m' to Float unless unitless. Use Unit#scalar") }
730
+
731
+ specify {Unit("1+1i").to_c.should be_kind_of(Complex)}
732
+ specify { expect { Unit("1+1i m").to_c }.to raise_error(RuntimeError,"Cannot convert '1.0+1.0i m' to Complex unless unitless. Use Unit#scalar") }
733
+
734
+ specify {Unit("3/7").to_r.should be_kind_of(Rational)}
735
+ specify { expect { Unit("3/7 m").to_r }.to raise_error(RuntimeError,"Cannot convert '3/7 m' to Rational unless unitless. Use Unit#scalar") }
736
+
737
+ end
738
+
739
+ context "absolute value (#abs)" do
740
+ context "of a unitless unit" do
741
+ specify "returns the absolute value of the scalar" do
742
+ Unit("-10").abs.should == 10
743
+ end
744
+ end
745
+
746
+ context "of a unit" do
747
+ specify "returns a unit with the absolute value of the scalar" do
748
+ Unit("-10 m").abs.should == Unit("10 m")
749
+ end
750
+ end
751
+ end
752
+
753
+ context "#ceil" do
754
+ context "of a unitless unit" do
755
+ specify "returns the ceil of the scalar" do
756
+ Unit("10.1").ceil.should == 11
757
+ end
758
+ end
759
+
760
+ context "of a unit" do
761
+ specify "returns a unit with the ceil of the scalar" do
762
+ Unit("10.1 m").ceil.should == Unit("11 m")
763
+ end
764
+ end
765
+ end
766
+
767
+ context "#floor" do
768
+ context "of a unitless unit" do
769
+ specify "returns the floor of the scalar" do
770
+ Unit("10.1").floor.should == 10
771
+ end
772
+ end
773
+
774
+ context "of a unit" do
775
+ specify "returns a unit with the floor of the scalar" do
776
+ Unit("10.1 m").floor.should == Unit("10 m")
777
+ end
778
+ end
779
+ end
780
+
781
+ context "#round" do
782
+ context "of a unitless unit" do
783
+ specify "returns the round of the scalar" do
784
+ Unit("10.5").round.should == 11
785
+ end
786
+ end
787
+
788
+ context "of a unit" do
789
+ specify "returns a unit with the round of the scalar" do
790
+ Unit("10.5 m").round.should == Unit("11 m")
791
+ end
792
+ end
793
+ end
794
+
795
+ context "#truncate" do
796
+ context "of a unitless unit" do
797
+ specify "returns the truncate of the scalar" do
798
+ Unit("10.5").truncate.should == 10
799
+ end
800
+ end
801
+
802
+ context "of a unit" do
803
+ specify "returns a unit with the truncate of the scalar" do
804
+ Unit("10.5 m").truncate.should == Unit("10 m")
805
+ end
806
+ end
807
+ end
808
+
809
+ context '#zero?' do
810
+ it "is true when the scalar is zero on the base scale" do
811
+ Unit("0").should be_zero
812
+ Unit("0 mm").should be_zero
813
+ Unit("-273.15 tempC").should be_zero
814
+ end
815
+
816
+ it "is false when the scalar is not zero" do
817
+ Unit("1").should_not be_zero
818
+ Unit("1 mm").should_not be_zero
819
+ Unit("0 tempC").should_not be_zero
820
+ end
821
+ end
822
+
823
+ context '#succ' do
824
+ specify { Unit("1").succ.should == Unit("2")}
825
+ specify { Unit("1 mm").succ.should == Unit("2 mm")}
826
+ specify { Unit("1 mm").next.should == Unit("2 mm")}
827
+ specify { Unit("-1 mm").succ.should == Unit("0 mm")}
828
+ specify { expect {Unit("1.5 mm").succ}.to raise_error(ArgumentError,"Non Integer Scalar")}
829
+ end
830
+
831
+ context '#pred' do
832
+ specify { Unit("1").pred.should == Unit("0")}
833
+ specify { Unit("1 mm").pred.should == Unit("0 mm")}
834
+ specify { Unit("-1 mm").pred.should == Unit("-2 mm")}
835
+ specify { expect {Unit("1.5 mm").pred}.to raise_error(ArgumentError,"Non Integer Scalar")}
836
+ end
837
+
838
+ context '#divmod' do
839
+ specify { Unit("5 mm").divmod(Unit("2 mm")).should == [2,1] }
840
+ specify { Unit("1 km").divmod(Unit("2 m")).should == [500,0] }
841
+ end
842
+
843
+ end
844
+
845
+ describe "Unit Output formatting" do
846
+ context Unit("10.5 m/s^2") do
847
+ specify { subject.to_s.should == "10.5 m/s^2" }
848
+ specify { subject.to_s("%0.2f").should == "10.50 m/s^2"}
849
+ specify { subject.to_s("%0.2e km/s^2").should == "1.05e-02 km/s^2"}
850
+ specify { subject.to_s("km/s^2").should == "0.0105 km/s^2"}
851
+ specify { subject.to_s(STDOUT).should == "10.5 m/s^2" }
852
+ specify { expect {subject.to_s("random string")}.to raise_error(ArgumentError,"'random' Unit not recognized")}
853
+ end
854
+
855
+ end
856
+
857
+ describe "Foot-inch conversions" do
858
+ [
859
+ ["76 in", %Q{6'4"}],
860
+ ["77 in", %Q{6'5"}],
861
+ ["78 in", %Q{6'6"}],
862
+ ["79 in", %Q{6'7"}],
863
+ ["80 in", %Q{6'8"}],
864
+ ["87 in", %Q{7'3"}],
865
+ ["88 in", %Q{7'4"}],
866
+ ["89 in", %Q{7'5"}]
867
+ ].each do |inches, feet|
868
+ specify { Unit(inches).to("ft").should == Unit(feet)}
869
+ specify { Unit(inches).to_s(:ft).should == feet}
870
+ end
871
+ end
872
+
873
+ describe "pound-ounce conversions" do
874
+ [
875
+ ["76 oz", "4 lbs, 12 oz"],
876
+ ["77 oz", "4 lbs, 13 oz"],
877
+ ["78 oz", "4 lbs, 14 oz"],
878
+ ["79 oz", "4 lbs, 15 oz"],
879
+ ["80 oz", "5 lbs, 0 oz"],
880
+ ["87 oz", "5 lbs, 7 oz"],
881
+ ["88 oz", "5 lbs, 8 oz"],
882
+ ["89 oz", "5 lbs, 9 oz"]
883
+ ].each do |ounces, pounds|
884
+ specify { Unit(ounces).to("lbs").should == Unit(pounds)}
885
+ specify { Unit(ounces).to_s(:lbs).should == pounds}
886
+ end
887
+ end
888
+