unit 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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