unit 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Rakefile CHANGED
@@ -1,6 +1,18 @@
1
- task :default => :test
2
1
 
3
- desc 'Run tests with bacon'
4
- task :test => FileList['test/*_test.rb'] do |t|
5
- sh "bacon -q -Ilib:test #{t.prerequisites.join(' ')}"
2
+ require 'rspec/core/rake_task'
3
+ task :default => :spec
4
+
5
+ desc "Run specs"
6
+ task :spec => ["spec:no_dsl", "spec:dsl"]
7
+
8
+ namespace :spec do
9
+ desc "Run specs without DSL"
10
+ RSpec::Core::RakeTask.new(:no_dsl) do |t|
11
+ t.rspec_opts = "-t ~dsl"
12
+ end
13
+
14
+ desc "Run specs with DSL"
15
+ RSpec::Core::RakeTask.new(:dsl) do |t|
16
+ # Run all tests
17
+ end
6
18
  end
@@ -2,6 +2,8 @@
2
2
  class Unit < Numeric
3
3
  attr_reader :value, :normalized, :unit, :system
4
4
 
5
+ class IncompatibleUnitError < TypeError; end
6
+
5
7
  def initialize(value, unit, system)
6
8
  @system = system
7
9
  @value = value
@@ -44,21 +46,33 @@ class Unit < Numeric
44
46
  end
45
47
 
46
48
  def *(other)
47
- a, b = coerce(other)
48
- Unit.new(a.value * b.value, a.unit + b.unit, system)
49
+ if Numeric === other
50
+ other = coerce_numeric(other)
51
+ Unit.new(other.value * self.value, other.unit + self.unit, system)
52
+ else
53
+ apply_through_coercion(other, __method__)
54
+ end
49
55
  end
50
56
 
51
57
  def /(other)
52
- a, b = coerce(other)
53
- Unit.new(Integer === a.value && Integer === b.value ? Rational(a.value, b.value) : a.value / b.value,
54
- a.unit + Unit.power_unit(b.unit, -1), system)
58
+ if Numeric === other
59
+ other = coerce_numeric(other)
60
+ Unit.new(Integer === value && Integer === other.value ?
61
+ Rational(value, other.value) : value / other.value,
62
+ unit + Unit.power_unit(other.unit, -1), system)
63
+ else
64
+ apply_through_coercion(other, __method__)
65
+ end
55
66
  end
56
67
 
57
68
  def +(other)
58
- raise TypeError, "Incompatible units: #{self.inspect} and #{other.inspect}" if !compatible?(other)
59
- a, b = coerce(other)
60
- a, b = a.normalize, b.normalize
61
- Unit.new(a.value + b.value, a.unit, system).in(self)
69
+ if Numeric === other
70
+ other = coerce_numeric_compatible(other)
71
+ a, b = self.normalize, other.normalize
72
+ Unit.new(a.value + b.value, b.unit, system).in(self)
73
+ else
74
+ apply_through_coercion(other, __method__)
75
+ end
62
76
  end
63
77
 
64
78
  def **(exp)
@@ -67,23 +81,49 @@ class Unit < Numeric
67
81
  end
68
82
 
69
83
  def -(other)
70
- self + (-other)
84
+ if Numeric === other
85
+ other = coerce_numeric_compatible(other)
86
+ a, b = self.normalize, other.normalize
87
+ Unit.new(a.value - b.value, b.unit, system).in(self)
88
+ else
89
+ apply_through_coercion(other, __method__)
90
+ end
71
91
  end
72
92
 
73
93
  def -@
74
94
  Unit.new(-value, unit, system)
75
95
  end
76
96
 
97
+ def abs
98
+ Unit.new(value.abs, unit, system)
99
+ end
100
+
101
+ def zero?
102
+ value.zero?
103
+ end
104
+
77
105
  def ==(other)
78
- a, b = coerce(other)
79
- a, b = a.normalize, b.normalize
80
- a.value == b.value && a.unit == b.unit
106
+ if Numeric === other
107
+ other = coerce_numeric(other)
108
+ a, b = self.normalize, other.normalize
109
+ a.value == b.value && a.unit == b.unit
110
+ else
111
+ apply_through_coercion(other, __method__)
112
+ end
113
+ end
114
+
115
+ def eql?(other)
116
+ Unit === other && value.eql?(other.value) && unit == other.unit
81
117
  end
82
118
 
83
119
  def <=>(other)
84
- a, b = coerce(other)
85
- a, b = a.normalize, b.normalize
86
- a.value <=> b.value if a.unit == b.unit
120
+ if Numeric === other
121
+ other = coerce_numeric_compatible(other)
122
+ a, b = self.normalize, other.normalize
123
+ a.value <=> b.value
124
+ else
125
+ apply_through_coercion(other, __method__)
126
+ end
87
127
  end
88
128
 
89
129
  # Number without dimension
@@ -91,22 +131,28 @@ class Unit < Numeric
91
131
  normalize.unit.empty?
92
132
  end
93
133
 
94
- alias unitless? dimensionless?
134
+ alias_method :unitless?, :dimensionless?
95
135
 
96
136
  # Compatible units can be added
97
137
  def compatible?(other)
98
- a, b = coerce(other)
99
- a, b = a.normalize, b.normalize
100
- a.unit == b.unit
138
+ self.normalize.unit == Unit.to_unit(other, system).normalize.unit
101
139
  end
102
140
 
103
- alias compatible_with? compatible?
141
+ alias_method :compatible_with?, :compatible?
104
142
 
105
143
  # Convert to other unit
106
144
  def in(unit)
107
- a, b = coerce(unit)
108
- conversion = Unit.new(1, b.unit, system)
109
- (a / conversion).normalize * conversion
145
+ conversion = Unit.new(1, Unit.to_unit(unit, system).unit, system)
146
+ (self / conversion).normalize * conversion
147
+ end
148
+
149
+ def in!(unit)
150
+ unit = coerce_object(unit)
151
+ result = self.in(unit)
152
+ unless result.unit == unit.unit
153
+ raise TypeError, "Unexpected #{result.inspect}, expected to be in #{other_unit.unit_string}"
154
+ end
155
+ result
110
156
  end
111
157
 
112
158
  def inspect
@@ -114,7 +160,7 @@ class Unit < Numeric
114
160
  end
115
161
 
116
162
  def to_s
117
- unit.empty? ? value.to_s : "#{value} #{unit_string('·')}"
163
+ unit.empty? ? value.to_s : "#{value} #{unit_string}"
118
164
  end
119
165
 
120
166
  def to_tex
@@ -130,40 +176,20 @@ class Unit < Numeric
130
176
  end
131
177
 
132
178
  def approx
133
- to_f.unit(unit)
134
- end
135
-
136
- def coerce(val)
137
- [self, Unit.to_unit(val, system)]
138
- end
139
-
140
- def self.to_unit(object, system = nil)
141
- system ||= Unit.default_system
142
- case object
143
- when Unit
144
- raise TypeError, 'Different unit system' if object.system != system
145
- object
146
- when Array
147
- system.validate_unit(object)
148
- Unit.new(1, object, system)
149
- when String, Symbol
150
- unit = system.parse_unit(object.to_s)
151
- system.validate_unit(unit)
152
- Unit.new(1, unit, system)
153
- when Numeric
154
- Unit.new(object, [], system)
155
- else
156
- raise TypeError, "#{object.class} has no unit support"
157
- end
179
+ Unit.new(self.to_f, unit, system)
158
180
  end
159
181
 
160
- private
182
+ def coerce(other)
183
+ [coerce_numeric(other), self]
184
+ end
161
185
 
162
- def unit_string(sep)
186
+ def unit_string(sep = '·')
163
187
  (unit_list(@unit.select {|factor, name, exp| exp >= 0 }) +
164
188
  unit_list(@unit.select {|factor, name, exp| exp < 0 })).join(sep)
165
189
  end
166
190
 
191
+ private
192
+
167
193
  def unit_list(list)
168
194
  units = []
169
195
  list.each do |factor, name, exp|
@@ -176,18 +202,14 @@ class Unit < Numeric
176
202
  units.sort
177
203
  end
178
204
 
179
- def self.power_unit(unit, pow)
180
- unit.map {|factor, name, exp| [factor, name, exp * pow] }
181
- end
182
-
183
205
  # Reduce units and factors
184
206
  def reduce!
185
207
  # Remove numbers from units
186
208
  numbers = @unit.select {|factor, unit, exp| Numeric === unit }
187
209
  @unit -= numbers
188
210
  numbers.each do |factor, number, exp|
189
- raise RuntimeError, 'Numeric unit with factor' if factor != :one
190
- @value *= number ** exp
211
+ raise RuntimeError, 'Numeric unit with factor' if factor != :one
212
+ @value *= number ** exp
191
213
  end
192
214
 
193
215
  # Reduce units
@@ -227,7 +249,66 @@ class Unit < Numeric
227
249
  self
228
250
  end
229
251
 
230
- class<< self
252
+ # Given another object and an operator, use the other object's #coerce method
253
+ # to perform the operation.
254
+ #
255
+ # Based on Matrix#apply_through_coercion
256
+ def apply_through_coercion(obj, oper)
257
+ coercion = obj.coerce(self)
258
+ raise TypeError unless coercion.is_a?(Array) && coercion.length == 2
259
+ first, last = coercion
260
+ first.send(oper, last)
261
+ rescue
262
+ raise TypeError, "#{obj.class} can't be coerced into #{self.class}"
263
+ end
264
+
265
+ def coerce_numeric_compatible(object)
266
+ object = coerce_numeric(object)
267
+ raise IncompatibleUnitError, "#{inspect} and #{object.inspect} are incompatible" if !compatible?(object)
268
+ object
269
+ end
270
+
271
+ def coerce_numeric(object)
272
+ Unit.numeric_to_unit(object, system)
273
+ end
274
+
275
+ def coerce_object(object)
276
+ Unit.to_unit(object, system)
277
+ end
278
+
279
+ class << self
231
280
  attr_accessor :default_system
281
+
282
+ def power_unit(unit, pow)
283
+ unit.map {|factor, name, exp| [factor, name, exp * pow] }
284
+ end
285
+
286
+ def numeric_to_unit(object, system = nil)
287
+ system ||= Unit.default_system
288
+ case object
289
+ when Unit
290
+ raise IncompatibleUnitError, "Unit system of #{object.inspect} is incompatible with #{system.name}" if object.system != system
291
+ object
292
+ when Numeric
293
+ Unit.new(object, [], system)
294
+ else
295
+ raise TypeError, "#{object.class} can't be coerced into Unit"
296
+ end
297
+ end
298
+
299
+ def to_unit(object, system = nil)
300
+ system ||= Unit.default_system
301
+ case object
302
+ when String, Symbol
303
+ unit = system.parse_unit(object.to_s)
304
+ system.validate_unit(unit)
305
+ Unit.new(1, unit, system)
306
+ when Array
307
+ system.validate_unit(object)
308
+ Unit.new(1, object, system)
309
+ else
310
+ numeric_to_unit(object, system)
311
+ end
312
+ end
232
313
  end
233
314
  end
@@ -13,11 +13,12 @@ class Unit < Numeric
13
13
  name.to_s.sub(/^per_/, '1/').gsub('_per_', '/').gsub('_', ' ')
14
14
  end
15
15
 
16
- def method_missing(name)
17
- if name.to_s =~ /^in_/
18
- self.in(Unit.method_name_to_unit($'))
16
+ def method_missing(name, system = nil)
17
+ if name.to_s =~ /^in_(.*?)(!?)$/
18
+ unit = Unit.method_name_to_unit($1)
19
+ $2.empty? ? self.in(unit) : self.in!(unit)
19
20
  else
20
- Unit.to_unit(Unit.method_name_to_unit(name), system) * self
21
+ super(name, system || @system)
21
22
  end
22
23
  end
23
24
  end
@@ -20,6 +20,9 @@ class Unit < Numeric
20
20
 
21
21
  def load(input)
22
22
  case input
23
+ when Hash
24
+ raise "Invalid hash format to load system" unless (input["units"] && input["units"].first.last['def']) || input.first.last['def']
25
+ data = input['units'] || input
23
26
  when IO
24
27
  data = YAML.load(input.read)
25
28
  when String
@@ -44,7 +44,7 @@ units:
44
44
  def: newton * meter
45
45
  watt:
46
46
  sym: W
47
- def: joule * second
47
+ def: joule / second
48
48
  coloumb:
49
49
  sym: C
50
50
  def: ampere * second
@@ -1,3 +1,3 @@
1
1
  class Unit < Numeric
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe "Errors" do
5
+ describe "TypeError when adding incompatible units" do
6
+ it "should have a nice error message" do
7
+ a = Unit(1, "meter")
8
+ b = Unit(1, "second")
9
+ lambda { a + b }.should raise_error(TypeError, "#{a.inspect} and #{b.inspect} are incompatible")
10
+ end
11
+ end
12
+
13
+ describe "TypeError when trying to convert incompatible unit using #in!" do
14
+ it "should have a nice error message" do
15
+ unit = Unit(1000, "m / s")
16
+ lambda { unit.in!("seconds") }.should
17
+ raise_error(TypeError, %{Unexpected Unit("1000/1 m.s^-1"), expected to be in s})
18
+ end
19
+
20
+ it "should have a nice error message using the DSL", :dsl => true do
21
+ unit = Unit(1000, "m / s")
22
+ lambda { unit.in_seconds! }.should
23
+ raise_error(TypeError, %{Unexpected Unit("1000/1 m.s^-1"), expected to be in s})
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,12 @@
1
+ require 'rspec'
2
+ require 'unit'
3
+ require './spec/support/unit_one'
4
+
5
+ RSpec.configure do |config|
6
+ unless config.exclusion_filter[:dsl]
7
+ config.before(:suite) do
8
+ require "unit/dsl"
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,46 @@
1
+ # Example test class for coercion
2
+ #
3
+ # UnitOne behaves as Unit(1) when added to a Unit, and as 1 when added to anything else.
4
+ class UnitOne
5
+ def coerce(other)
6
+ case other
7
+ when Unit
8
+ [other, Unit(1)]
9
+ else
10
+ [other, 1]
11
+ end
12
+ end
13
+
14
+ def +(other)
15
+ apply_through_coercion(other, __method__)
16
+ end
17
+
18
+ def -(other)
19
+ apply_through_coercion(other, __method__)
20
+ end
21
+
22
+ def /(other)
23
+ apply_through_coercion(other, __method__)
24
+ end
25
+
26
+ def *(other)
27
+ apply_through_coercion(other, __method__)
28
+ end
29
+
30
+ def ==(other)
31
+ apply_through_coercion(other, __method__)
32
+ end
33
+
34
+ private
35
+
36
+ def apply_through_coercion(other, operation)
37
+ case other
38
+ when Unit
39
+ a, b = other.coerce(Unit(1))
40
+ else
41
+ a, b = other.coerce(1)
42
+ end
43
+
44
+ a.send(operation, b)
45
+ end
46
+ end
@@ -1,7 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- require 'bacon'
3
- require 'unit'
4
- require 'unit/dsl'
2
+ require 'spec_helper'
5
3
 
6
4
  describe "Unit" do
7
5
  describe "#default_system" do
@@ -11,13 +9,23 @@ describe "Unit" do
11
9
  File.open(test_file) do |file|
12
10
  Unit.default_system.load(file)
13
11
  end
14
- Unit(1, "pim").should.equal Unit(3.14159, "m")
12
+ Unit(1, "pim").should == Unit(3.14159, "m")
15
13
  end
16
14
 
17
15
  it "should load a file" do
18
16
  test_file = File.join(File.dirname(__FILE__), "yml", "filename.yml")
19
17
  Unit.default_system.load(test_file)
20
- Unit(2, "dzm").should.equal Unit(24, "m")
18
+ Unit(2, "dzm").should == Unit(24, "m")
19
+ end
20
+
21
+ it "should load a hash" do
22
+ Unit.default_system.load({
23
+ 'dozen_meter' => {
24
+ 'sym' => 'dzm',
25
+ 'def' => '12 m'
26
+ }
27
+ })
28
+ Unit(2, "dzm").should == Unit(24, "m")
21
29
  end
22
30
  end
23
31
  end
@@ -0,0 +1,225 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ Unit.default_system.load(:scientific)
5
+ Unit.default_system.load(:imperial)
6
+ Unit.default_system.load(:misc)
7
+
8
+ describe 'Unit' do
9
+ it 'should support multiplication' do
10
+ (Unit(2, 'm') * Unit(3, 'm')).should == Unit(6, 'm^2')
11
+ (Unit(2, 'm') * 3).should == Unit(6, 'm')
12
+ (Unit(2, 'm') * Rational(3, 4)).should == Unit(3, 2, 'm')
13
+ (Unit(2, 'm') * 0.5).should == Unit(1.0, 'm')
14
+ end
15
+
16
+ it 'should support division' do
17
+ (Unit(2, 'm') / Unit(3, 'm^2')).should == Unit(2, 3, '1/m')
18
+ (Unit(2, 'm') / 3).should == Unit(2, 3, 'm')
19
+ (Unit(2, 'm') / Rational(3, 4)).should == Unit(8, 3, 'm')
20
+ (Unit(2, 'm') / 0.5).should == Unit(4.0, 'm')
21
+ end
22
+
23
+ it 'should support addition' do
24
+ (Unit(42, 'm') + Unit(1, 'km')).should == Unit(1042, 'm')
25
+ end
26
+
27
+ it 'should support subtraction' do
28
+ (Unit(1, 'm') - Unit(1, 'cm')).should == Unit(99, 100, 'm')
29
+ (Unit(2, 'm') - Unit(1, 'm')).should == Unit(1, 'm')
30
+ end
31
+
32
+ it "should support arithmetic with Integers when appropriate" do
33
+ (1 + Unit(1)).should == Unit(2)
34
+ (2 - Unit(1)).should == Unit(1)
35
+ (Unit(2) - 1).should == Unit(1)
36
+ (2 - Unit(-1)).should == Unit(3)
37
+ (Unit(2) - -1).should == Unit(3)
38
+ end
39
+
40
+ it "should support arithmetic with other classes using #coerce" do
41
+ (Unit(2) + UnitOne.new).should == Unit(3)
42
+ (2 + UnitOne.new).should == 3
43
+ (Unit(2) - UnitOne.new).should == Unit(1)
44
+ (2 - UnitOne.new).should == 1
45
+ (Unit(2) * UnitOne.new).should == Unit(2)
46
+ (2 * UnitOne.new).should == 2
47
+ (Unit(2) / UnitOne.new).should == Unit(2)
48
+ (2 / UnitOne.new).should == 2
49
+
50
+ (UnitOne.new + Unit(4)).should == Unit(5)
51
+ (UnitOne.new + 4).should == 5
52
+ (UnitOne.new - Unit(4)).should == Unit(-3)
53
+ (UnitOne.new - 4).should == -3
54
+ (UnitOne.new * Unit(4)).should == Unit(4)
55
+ (UnitOne.new * 4).should == 4
56
+ (UnitOne.new / Unit(4)).should == Unit(1, 4)
57
+ (UnitOne.new / 4).should == 0
58
+ end
59
+
60
+ it "should support logic with other classes using #coerce" do
61
+ Unit(1).should == UnitOne.new
62
+ Unit(2).should > UnitOne.new
63
+ end
64
+
65
+ it "should support eql comparison" do
66
+ Unit(1).should eql(Unit(1))
67
+ Unit(1.0).should_not eql(Unit(1))
68
+
69
+ Unit(1).should_not eql(UnitOne.new)
70
+ Unit(1.0).should_not eql(UnitOne.new)
71
+ end
72
+
73
+ it "should not support adding anything but numeric unless object is coerceable" do
74
+ expect { Unit(1) + 'string'}.to raise_error(TypeError)
75
+ expect { Unit(1) + []}.to raise_error(TypeError)
76
+ expect { Unit(1) + :symbol }.to raise_error(TypeError)
77
+ expect { Unit(1) + {}}.to raise_error(TypeError)
78
+ end
79
+
80
+ it "should support adding through zero" do
81
+ (Unit(0, "m") + Unit(1, "m")).should == Unit(1, "m")
82
+ (Unit(1, "m") + Unit(-1, "m") + Unit(1, "m")).should == Unit(1, "m")
83
+ end
84
+
85
+ it 'should check unit compatiblity' do
86
+ expect {Unit(42, 'm') + Unit(1, 's')}.to raise_error(TypeError)
87
+ expect {Unit(42, 'g') + Unit(1, 'm')}.to raise_error(TypeError)
88
+ expect {Unit(0, 'g') + Unit(1, 'm')}.to raise_error(TypeError)
89
+ end
90
+
91
+ it 'should support exponentiation' do
92
+ (Unit(2, 'm') ** 3).should == Unit(8, 'm^3')
93
+ (Unit(9, 'm^2') ** 0.5).should == Unit(3.0, 'm')
94
+ (Unit(9, 'm^2') ** Rational(1, 2)).should == Unit(3, 'm')
95
+ (Unit(2, 'm') ** 1.3).should == Unit(2 ** 1.3, 'm^1.3')
96
+ end
97
+
98
+ it 'should not allow units as exponent' do
99
+ expect { Unit(42, 'g') ** Unit(1, 'm') }.to raise_error(TypeError)
100
+ end
101
+
102
+ describe "#normalize" do
103
+ it "should return a normalized unit" do
104
+ unit = Unit(1, 'joule')
105
+ normalized_unit = Unit(1000, 'gram meter^2 / second^2')
106
+
107
+ unit.normalize.should eql normalized_unit
108
+ end
109
+
110
+ it "should not modify the receiver" do
111
+ unit = Unit(1, 'joule')
112
+ normalized_unit = Unit(1000, 'gram meter^2 / second^2')
113
+
114
+ unit.normalize
115
+ unit.should_not eql normalized_unit
116
+ end
117
+ end
118
+
119
+ describe "#normalize!" do
120
+ it "should return a normalized unit" do
121
+ unit = Unit(1, 'joule')
122
+ normalized_unit = Unit(1000, 'gram meter^2 / second^2')
123
+
124
+ unit.normalize!.should eql normalized_unit
125
+ end
126
+
127
+ it "should modify the receiver" do
128
+ unit = Unit(1, 'joule')
129
+ normalized_unit = Unit(1000, 'gram meter^2 / second^2')
130
+
131
+ unit.normalize!
132
+ unit.should eql normalized_unit
133
+ end
134
+ end
135
+
136
+ it 'should convert units' do
137
+ Unit(1, "MeV").in("joule").should == Unit(1.602176487e-13, 'joule')
138
+ Unit(1, "kilometer").in("meter").should == Unit(1000, 'meter')
139
+ Unit(1, "liter").in('meter^3').should == Unit(1, 1000, 'meter^3')
140
+ Unit(1, "kilometer/hour").in("meter/second").should == Unit(5, 18, 'meter/second')
141
+ end
142
+
143
+ it 'should have a working compatible? method' do
144
+ Unit(7, "meter").compatible?('kilogram').should == false
145
+ Unit(3, "parsec").compatible_with?('meter').should == true
146
+ end
147
+
148
+ it 'should have a pretty string representation' do
149
+ Unit(7, "joule").normalize.to_s.should == '7000 g·m^2·s^-2'
150
+ end
151
+
152
+ it 'should parse units' do
153
+ Unit(1, 'KiB s^-1').unit.should == [[:kibi, :byte, 1], [:one, :second, -1]].sort
154
+ Unit(1, 'KiB/s').unit.should == [[:kibi, :byte, 1], [:one, :second, -1]].sort
155
+ Unit(1, 'kilometer^2 / megaelectronvolt^7 * gram centiliter').unit.should == [[:kilo, :meter, 2], [:mega, :electronvolt, -7],
156
+ [:one, :gram, 1], [:centi, :liter, 1]].sort
157
+ end
158
+
159
+ it 'should reduce units' do
160
+ Unit(1, "joule/kilogram").normalize.unit.should == [[:one, :meter, 2], [:one, :second, -2]].sort
161
+ Unit(1, "megaton/kilometer").unit.should == [[:kilo, :ton, 1], [:one, :meter, -1]].reverse
162
+ end
163
+
164
+ it 'should work with floating point values' do
165
+ w = 5.2 * Unit('kilogram')
166
+ w.in("pounds").to_int.should == 11
167
+ end
168
+
169
+ it 'should have dimensionless? method' do
170
+ Unit(100, "m/km").should be_dimensionless
171
+ Unit(42, "meter/second").should_not be_unitless
172
+ Unit(100, "meter/km").should == Unit(Rational(1, 10))
173
+ end
174
+
175
+ it 'should be equal to rational if dimensionless' do
176
+ Unit(100, "meter/km").should == Rational(1, 10)
177
+ Unit(100, "meter/km").approx.should == 0.1
178
+ end
179
+
180
+ it 'should be comparable' do
181
+ Unit(1, 'm').should < Unit(2, 'm')
182
+ Unit(1, 'm').should <= Unit(2, 'm')
183
+
184
+ Unit(1, 'm').should <= Unit(1, 'm')
185
+ Unit(1, 'm').should >= Unit(1, 'm')
186
+
187
+ Unit(1, 'm').should > Unit(0, 'm')
188
+ Unit(1, 'm').should >= Unit(0, 'm')
189
+
190
+ Unit(100, "m").should < Unit(1, "km")
191
+ Unit(100, "m").should > Unit(0.0001, "km")
192
+ end
193
+
194
+ it "should fail comparison on differing units" do
195
+ expect { Unit(1, "second") > Unit(1, "meter") }.to raise_error(Unit::IncompatibleUnitError)
196
+ end
197
+
198
+ it "should keep units when the value is zero" do
199
+ Unit(0, "m").unit.should == [[:one, :meter, 1]]
200
+ end
201
+
202
+ it "should support absolute value" do
203
+ Unit(1, "m").abs.should == Unit(1, "m")
204
+ Unit(-1, "m").abs.should == Unit(1, "m")
205
+ end
206
+
207
+ it "should have #zero?" do
208
+ Unit(0, "m").zero?.should == true
209
+ Unit(1, "m").zero?.should == false
210
+ end
211
+
212
+ it "should produce an approximation" do
213
+ Unit(Rational(1,3), "m").approx.should == Unit(1.0/3.0, "m")
214
+ end
215
+
216
+ end
217
+
218
+ describe "Unit DSL", :dsl => true do
219
+ it 'should provide method sugar' do
220
+ 1.meter.should == Unit('1 meter')
221
+ 1.meter_per_second.should == Unit('1 m/s')
222
+ 1.meter.in_kilometer.should == Unit('1 m').in('km')
223
+ 1.unit('°C').should == Unit(1, '°C')
224
+ end
225
+ end
File without changes
File without changes
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
6
6
  s.version = Unit::VERSION
7
7
 
8
8
  s.authors = ["Daniel Mendler"]
9
- s.date = %q{2009-05-17}
9
+ s.date = Date.today.to_s
10
10
  s.email = ["mail@daniel-mendler.de"]
11
11
 
12
12
  s.files = `git ls-files`.split("\n")
@@ -19,5 +19,5 @@ Gem::Specification.new do |s|
19
19
  s.homepage = %q{http://github.com/minad/unit}
20
20
 
21
21
  s.add_development_dependency('rake', ['>= 0.8.7'])
22
- s.add_development_dependency('bacon')
22
+ s.add_development_dependency('rspec')
23
23
  end
metadata CHANGED
@@ -1,50 +1,46 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: unit
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
4
5
  prerelease:
5
- version: 0.3.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Daniel Mendler
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2009-05-17 00:00:00 +02:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2012-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: rake
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &7079200 !ruby/object:Gem::Requirement
20
17
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
24
21
  version: 0.8.7
25
22
  type: :development
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: bacon
29
23
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *7079200
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &7078560 !ruby/object:Gem::Requirement
31
28
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
36
33
  type: :development
37
- version_requirements: *id002
34
+ prerelease: false
35
+ version_requirements: *7078560
38
36
  description:
39
- email:
37
+ email:
40
38
  - mail@daniel-mendler.de
41
39
  executables: []
42
-
43
40
  extensions: []
44
-
45
41
  extra_rdoc_files: []
46
-
47
- files:
42
+ files:
43
+ - .gitignore
48
44
  - .travis.yml
49
45
  - Gemfile
50
46
  - LICENSE
@@ -64,39 +60,36 @@ files:
64
60
  - lib/unit/systems/si.yml
65
61
  - lib/unit/systems/time.yml
66
62
  - lib/unit/version.rb
67
- - test/error_test.rb
68
- - test/system_test.rb
69
- - test/unit_test.rb
70
- - test/yml/filename.yml
71
- - test/yml/io.yml
63
+ - spec/error_spec.rb
64
+ - spec/spec_helper.rb
65
+ - spec/support/unit_one.rb
66
+ - spec/system_spec.rb
67
+ - spec/unit_spec.rb
68
+ - spec/yml/filename.yml
69
+ - spec/yml/io.yml
72
70
  - unit.gemspec
73
- has_rdoc: true
74
71
  homepage: http://github.com/minad/unit
75
72
  licenses: []
76
-
77
73
  post_install_message:
78
74
  rdoc_options: []
79
-
80
- require_paths:
75
+ require_paths:
81
76
  - lib
82
- required_ruby_version: !ruby/object:Gem::Requirement
77
+ required_ruby_version: !ruby/object:Gem::Requirement
83
78
  none: false
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- version: "0"
88
- required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
84
  none: false
90
- requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- version: "0"
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
94
89
  requirements: []
95
-
96
90
  rubyforge_project: unit
97
- rubygems_version: 1.6.2
91
+ rubygems_version: 1.8.11
98
92
  signing_key:
99
93
  specification_version: 3
100
94
  summary: Scientific unit support for ruby for calculations
101
95
  test_files: []
102
-
@@ -1,16 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'bacon'
3
- require 'unit'
4
- require 'unit/dsl'
5
-
6
- describe "Errors" do
7
- describe "TypeError when adding incompatible units" do
8
- it "should have a nice error message" do
9
- unit_1 = Unit(1, "meter")
10
- unit_2 = Unit(1, "second")
11
- lambda {
12
- unit_1 + unit_2
13
- }.should.raise(TypeError).message.should.equal("Incompatible units: #{unit_1.inspect} and #{unit_2.inspect}")
14
- end
15
- end
16
- end
@@ -1,144 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'bacon'
3
- require 'unit'
4
- require 'unit/dsl'
5
-
6
- Unit.default_system.load(:scientific)
7
- Unit.default_system.load(:imperial)
8
- Unit.default_system.load(:misc)
9
-
10
- describe 'Unit' do
11
- it 'should support multiplication' do
12
- (Unit(2, 'm') * Unit(3, 'm')).should.equal Unit(6, 'm^2')
13
- (Unit(2, 'm') * 3).should.equal Unit(6, 'm')
14
- (Unit(2, 'm') * Rational(3, 4)).should.equal Unit(3, 2, 'm')
15
- (Unit(2, 'm') * 0.5).should.equal Unit(1.0, 'm')
16
- end
17
-
18
- it 'should support division' do
19
- (Unit(2, 'm') / Unit(3, 'm^2')).should.equal Unit(2, 3, '1/m')
20
- (Unit(2, 'm') / 3).should.equal Unit(2, 3, 'm')
21
- (Unit(2, 'm') / Rational(3, 4)).should.equal Unit(8, 3, 'm')
22
- (Unit(2, 'm') / 0.5).should.equal Unit(4.0, 'm')
23
- end
24
-
25
- it 'should support addition' do
26
- (Unit(42, 'm') + Unit(1, 'km')).should.equal Unit(1042, 'm')
27
- (Unit(1, 'm') - Unit(1, 'cm')).should.equal Unit(99, 100, 'm')
28
- end
29
-
30
- it "should support adding through zero" do
31
- (Unit(0, "m") + Unit(1, "m")).should.equal Unit(1, "m")
32
- (Unit(1, "m") + Unit(-1, "m") + Unit(1, "m")).should.equal Unit(1, "m")
33
- end
34
-
35
- it 'should check unit compatiblity' do
36
- should.raise TypeError do
37
- (Unit(42, 'm') + Unit(1, 's'))
38
- end
39
- should.raise TypeError do
40
- (Unit(42, 'g') + Unit(1, 'm'))
41
- end
42
- should.raise TypeError do
43
- (Unit(0, 'g') + Unit(1, 'm'))
44
- end
45
- end
46
-
47
- it 'should support exponentiation' do
48
- (Unit(2, 'm') ** 3).should.equal Unit(8, 'm^3')
49
- (Unit(9, 'm^2') ** 0.5).should.equal Unit(3.0, 'm')
50
- (Unit(9, 'm^2') ** Rational(1, 2)).should.equal Unit(3, 'm')
51
- (Unit(2, 'm') ** 1.3).should.equal Unit(2 ** 1.3, 'm^1.3')
52
- end
53
-
54
- it 'should not allow units as exponent' do
55
- should.raise TypeError do
56
- Unit(42, 'g') ** Unit(1, 'm')
57
- end
58
- end
59
-
60
- it 'should provide method sugar' do
61
- 1.meter.should.equal Unit('1 meter')
62
- 1.meter_per_second.should.equal Unit('1 m/s')
63
- 1.meter.in_kilometer.should.equal Unit('1 m').in('km')
64
- 1.unit('°C').should.equal Unit(1, '°C')
65
- end
66
-
67
- it 'should have a normalizer' do
68
- 1.joule.normalize.should.equal Unit(1000, 'gram meter^2 / second^2')
69
- unit = 1.joule.normalize!
70
- unit.should.equal unit.normalized
71
- end
72
-
73
- it 'should convert units' do
74
- 1.MeV.in_joule.should.equal Unit(1.602176487e-13, 'joule')
75
- 1.kilometer.in_meter.should.equal Unit(1000, 'meter')
76
- 1.liter.in('meter^3').should.equal Unit(1, 1000, 'meter^3')
77
- 1.kilometer_per_hour.in_meter_per_second.should.equal Unit(5, 18, 'meter/second')
78
- end
79
-
80
- it 'should have a working compatible? method' do
81
- 7.meter.compatible?('kilogram').should.equal false
82
- 3.parsec.compatible_with?('meter').should.equal true
83
- end
84
-
85
- it 'should have a pretty string representation' do
86
- 7.joule.normalize.to_s.should.equal '7000 g·m^2·s^-2'
87
- end
88
-
89
- it 'should parse units' do
90
- Unit(1, 'KiB s^-1').unit.should.equal [[:kibi, :byte, 1], [:one, :second, -1]].sort
91
- Unit(1, 'KiB/s').unit.should.equal [[:kibi, :byte, 1], [:one, :second, -1]].sort
92
- Unit(1, 'kilometer^2 / megaelectronvolt^7 * gram centiliter').unit.should.equal [[:kilo, :meter, 2], [:mega, :electronvolt, -7],
93
- [:one, :gram, 1], [:centi, :liter, 1]].sort
94
- end
95
-
96
- it 'should reduce units' do
97
- 1.joule_per_kilogram.normalize.unit.should.equal [[:one, :meter, 2], [:one, :second, -2]].sort
98
- 1.megaton_per_kilometer.unit.should.equal [[:kilo, :ton, 1], [:one, :meter, -1]].sort
99
- end
100
-
101
- it 'should work with floating point values' do
102
- #w = (5.2).kilogram
103
- w = 5.2 * Unit('kilogram')
104
- w.in_pounds.to_int.should.equal 11
105
- end
106
-
107
- it 'should have dimensionless? method' do
108
- 100.meter_per_km.should.be.dimensionless
109
- 100.meter.per_km.should.be.dimensionless
110
- 100.meter.per_km.should.be.unitless
111
- 42.meter.per_second.should.not.be.unitless
112
- 100.meter.per_km.should.equal Unit(Rational(1, 10))
113
- end
114
-
115
- it 'should be equal to rational if dimensionless' do
116
- 100.meter.per_km.should.equal Rational(1, 10)
117
- 100.meter.per_km.approx.should.equal 0.1
118
- end
119
-
120
- it 'should be comparable' do
121
- Unit(1,'m').should < Unit(2,'m')
122
- Unit(1,'m').should <= Unit(2,'m')
123
-
124
- Unit(1,'m').should <= Unit(1,'m')
125
- Unit(1,'m').should >= Unit(1,'m')
126
-
127
- Unit(1,'m').should > Unit(0,'m')
128
- Unit(1,'m').should >= Unit(0,'m')
129
-
130
- Unit(100, "m").should < Unit(1, "km")
131
- Unit(100, "m").should > Unit(0.0001, "km")
132
- end
133
-
134
- it "should fail comparison on differing units" do
135
- lambda do
136
- Unit(1, "second") > Unit(1, "meter")
137
- end.should.raise(ArgumentError)
138
- end
139
-
140
- it "should keep units when the value is zero" do
141
- Unit(0, "m").unit.should.equal [[:one, :meter, 1]]
142
- end
143
- end
144
-