unit 0.4.0 → 0.4.1

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