unite 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/unite/lookup.rb CHANGED
@@ -21,9 +21,19 @@ module Unite
21
21
  class << self
22
22
  def add object
23
23
  raise Invalid, object.errors.inspect unless object.valid?
24
- raise Duplicate, "The Unit #{object.name} already exists" unless @@unit_names[object.name.to_sym].nil?
24
+
25
+ names = [object.name] + object.aliases
26
+
27
+ names.each do |name|
28
+ raise Duplicate, "The Unit #{name} already exists" unless @@unit_names[name.to_sym].nil?
29
+ end
30
+
25
31
  key = store_object object
26
- @@unit_names[object.name.to_sym] = key
32
+
33
+ names.each do |name|
34
+ @@unit_names[name.to_sym] = key
35
+ end
36
+
27
37
  @@unit_ints[object.dimension_int] ||= []
28
38
  @@unit_ints[object.dimension_int] << key
29
39
  end
@@ -5,6 +5,7 @@ module Unite
5
5
  include Dimension::Vector
6
6
  include Conversion
7
7
  include Arithmetic
8
+ include Simplify
8
9
 
9
10
  def initialize(attributes = {})
10
11
  attributes.each do |name, value|
@@ -13,7 +14,6 @@ module Unite
13
14
  super
14
15
  end
15
16
 
16
-
17
17
  #Initialize the quantity,
18
18
  #
19
19
  #A nil value is treated as 0.0
@@ -0,0 +1,35 @@
1
+
2
+ # -*- encoding : utf-8 -*-
3
+ module Unite
4
+ class IncompatibleError < RuntimeError
5
+ end
6
+
7
+ module SiFactor
8
+
9
+ extend ::ActiveSupport::Concern
10
+ include Fraction
11
+
12
+ included do
13
+
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ end
19
+
20
+ protected
21
+
22
+ def si_factor
23
+ value * (get_si_factor(numerator) / get_si_factor(denominator))
24
+ end
25
+
26
+ def get_si_factor unit_array
27
+ expand_unit_array(unit_array).map do |element|
28
+ Lookup.find!(element).si_factor
29
+ end.inject(BigDecimal.new(1)) {|product, factor| product*factor}
30
+ end
31
+
32
+
33
+ end
34
+ end
35
+
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Unite
3
+
4
+ module Simplify
5
+ extend ::ActiveSupport::Concern
6
+
7
+ included Conversion
8
+ included Fraction
9
+
10
+ included do
11
+
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ end
17
+
18
+ def simplify
19
+ new_unit = simple_unit
20
+ convert_to!(new_unit) if new_unit && new_unit != self.unit
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ def simple_unit
27
+ u = Lookup.compatible_units(dimension_int).first
28
+ u ? u.name : u
29
+ end
30
+
31
+ end
32
+ end
33
+
data/lib/unite/unit.rb ADDED
@@ -0,0 +1,34 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Unite
3
+ class Unit
4
+
5
+ include Dimension::Vector
6
+ include Comparison
7
+ include SiFactor
8
+
9
+ def initialize(attributes = {})
10
+ attributes.each do |name, value|
11
+ send("#{name}=", value)
12
+ end
13
+ super
14
+ end
15
+
16
+ #Initialize the quantity,
17
+ #
18
+ #A nil value is treated as 0.0
19
+ def self.init unit
20
+ new :expression => "#{unit}"
21
+ end
22
+
23
+ attr_accessor :name, :numerator, :denominator, :cached_expression
24
+ alias :to_s :expression
25
+
26
+ def value
27
+ @value ||= BigDecimal.new(1)
28
+ end
29
+
30
+ def value= x
31
+ value
32
+ end
33
+ end
34
+ end
data/lib/unite/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Unite
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/unite.rb CHANGED
@@ -21,11 +21,13 @@ end
21
21
  # Dir["#{Rails.root.to_s}/lib/units/**/*.rb"].each {|file| require file }
22
22
  require 'unite/fraction'
23
23
  require 'unite/dimension'
24
+ require 'unite/si_factor'
24
25
  require 'unite/comparison'
25
26
  require 'unite/conversion'
26
27
  require 'unite/arithmetic'
28
+ require 'unite/simplify'
27
29
 
28
-
30
+ require "unite/unit"
29
31
  require 'unite/lookup'
30
32
 
31
33
  require "unite/quantity"
@@ -17,9 +17,14 @@ describe Unite::Dimension do
17
17
  end
18
18
 
19
19
 
20
- it "should return the correct si unit" do
20
+ it "should return the correct si unit for dimension name" do
21
21
  Unite::Dimension::LIST.map{|d| Unite::Dimension.si_unit(d)}.should ==
22
- ['meter', 'second', 'kelvin', 'kilogram', 'ampere', 'mole', 'candela', 'GBP', '']
22
+ ['km', 's', 'K', 'kg', 'A', 'mol', 'cd', 'GBP', '']
23
+ end
24
+
25
+ it "should return the correct si unit for dimension index" do
26
+ Unite::Dimension::LIST.each_with_index.map {|_,i| Unite::Dimension.si_unit(i)}.should ==
27
+ ['km', 's', 'K', 'kg', 'A', 'mol', 'cd', 'GBP', '']
23
28
  end
24
29
 
25
30
  its(:blank_dimension_vector) { should == [0,0,0,0,0,0,0,0] }
@@ -20,18 +20,14 @@ module Unite
20
20
 
21
21
  describe 'based off of simple units' do
22
22
  subject { DerivedUnit.new(:name => "J", :expression => 'kg*m^2/s^2') }
23
-
24
23
  its(:dimension_vector) { should == [2, -2, 0, 1, 0, 0, 0, 0] }
25
24
  its(:property_name) { should == :energy }
26
25
  end
27
26
 
28
27
  describe 'based off of other derived unit' do
29
-
30
28
  subject { DerivedUnit.new(:name => "kWh", :expression => '3.6e5*J') }
31
-
32
29
  its(:dimension_vector) { should == [2, -2, 0, 1, 0, 0, 0, 0] }
33
30
  its(:property_name) { should == :energy }
34
-
35
31
  end
36
32
 
37
33
  include_examples "comparable unit"
@@ -31,10 +31,8 @@ module Unite
31
31
  describe "instance methods" do
32
32
  let(:dimension) { :length }
33
33
  subject { Fabricate.build :simple_unit, :name => "m", :si_factor => 1, :dimension => :length }
34
-
35
34
  its(:dimension_vector) { should == Dimension::VECTOR_LIST.map{|d| d == dimension ? 1 : 0}}
36
35
  its(:property_name) { should == :distance }
37
-
38
36
  end
39
37
 
40
38
  describe "class methods" do
data/spec/lookup_spec.rb CHANGED
@@ -9,7 +9,7 @@ module Unite
9
9
  describe "adding and finding" do
10
10
 
11
11
  def unit_stub stubs_hash
12
- stub({:valid? => true, :errors => {} , :dimension_int => 30}.merge(stubs_hash))
12
+ stub({:valid? => true, :errors => {} , :dimension_int => 30, :aliases => []}.merge(stubs_hash))
13
13
  end
14
14
 
15
15
  def property_stub stubs_hash
@@ -23,7 +23,8 @@ module Unite
23
23
 
24
24
  describe "units" do
25
25
  let(:name) { 'scott' }
26
- let(:unit_object) { unit_stub(:valid? => valid, :name => name) }
26
+ let(:aliases) { ['awesome', 'greatest_of_all_time'] }
27
+ let(:unit_object) { unit_stub(:valid? => valid, :name => name, :aliases => aliases) }
27
28
  context "with invalid object" do
28
29
  let(:valid) { false }
29
30
 
@@ -43,6 +44,11 @@ module Unite
43
44
  subject.find!(name).should == unit_object
44
45
  end
45
46
 
47
+ it 'should be able to find the unit by alias' do
48
+ aliases.each do |a|
49
+ subject.find!(a).should == unit_object
50
+ end
51
+ end
46
52
 
47
53
  it "should raise not found if unit is undefined" do
48
54
  lambda { subject.find!(name+"undefined") }.should raise_exception(Lookup::Undefined)
@@ -57,6 +63,16 @@ module Unite
57
63
  end
58
64
  end
59
65
 
66
+ context "with non uniq alias" do
67
+ let(:duplicate_alias) { aliases.first }
68
+ let(:uniq_name) { name + "uniq" }
69
+ let(:duplicate_unit_object) { unit_stub(:name => uniq_name, :aliases => [duplicate_alias]) }
70
+
71
+ it "should raise an error" do
72
+ lambda { subject.add(duplicate_unit_object) }.should raise_exception(Lookup::Duplicate)
73
+ end
74
+ end
75
+
60
76
  describe "compatible units" do
61
77
  let(:compatible_unit_object) { unit_stub(:name => "unit_name1", :dimension_int => unit_object.dimension_int) }
62
78
  let(:incompatible_unit_object) { unit_stub(:name => "unit_name2", :dimension_int => (unit_object.dimension_int + 10)) }
@@ -0,0 +1,35 @@
1
+ # require 'spec_helper'
2
+
3
+ # module Unite
4
+ # describe "Money Conversions" do
5
+ # describe "performing arithmetic" do
6
+ # subject { Quantity.init("100", "GBP") }
7
+
8
+ # context "when currency is the same" do
9
+ # let(:other) { Quantity.init("100", "GBP") }
10
+
11
+ # [:+,:-, :/, :*].each do |op|
12
+ # it "should not raise error when op is #{op}" do
13
+ # lambda { subject.send(op,other) }.should_not raise_exception
14
+ # end
15
+ # end
16
+
17
+ # end
18
+
19
+ # context "when currency is different" do
20
+ # let(:other) { Quantity.init("100", "CAD") }
21
+
22
+ # [:+,:-, :/, :*].each do |op|
23
+ # it "should not raise error when op is #{op}" do
24
+ # subject.send(op,other)
25
+ # # lambda {
26
+ # # debugger
27
+ # # subject.send(op,other) }.should_not raise_exception
28
+ # end
29
+ # end
30
+
31
+ # end
32
+
33
+ # end
34
+ # end
35
+ # end
@@ -12,6 +12,7 @@ module Unite
12
12
  include_examples "convertable value"
13
13
  include_examples "dimension vectors"
14
14
  include_examples "unit fractions"
15
+ include_examples "simplify units"
15
16
 
16
17
 
17
18
  describe ".init" do
data/spec/spec_helper.rb CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
6
 
7
7
 
8
8
  begin
9
- require 'ruby-debug'
9
+ require 'byebug'
10
10
  puts "USING DEBUGGER"
11
11
  rescue
12
12
  puts "NOT USING DEBUGGER"
@@ -19,7 +19,6 @@ require 'mocha'
19
19
  require 'unite'
20
20
  require 'fabrication'
21
21
 
22
-
23
22
  # Requires supporting files with custom matchers and macros, etc,
24
23
  # in ./support/ and its subdirectories.
25
24
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
@@ -102,6 +102,27 @@ shared_examples "unit arithmetic" do
102
102
  include_examples "division operation"
103
103
  end
104
104
 
105
+
106
+ context "that are compatible but inverse" do
107
+ let(:expression1) { "10*km" }
108
+ let(:expression2) { "1000/m" }
109
+
110
+
111
+ let(:division_result) { [0.00001, 'km^2'] }
112
+ let(:multiplication_result) { [10000000, ''] }
113
+
114
+ include_examples "multiplication operation"
115
+ include_examples "division operation"
116
+
117
+ it "should raise error for subtraction" do
118
+ lambda { object1 - object2 }.should raise_exception Unite::IncompatibleError
119
+ end
120
+ it "should raise error for addition" do
121
+ lambda { object1 + object2 }.should raise_exception Unite::IncompatibleError
122
+ end
123
+ end
124
+
125
+
105
126
  context "that are incompatible " do
106
127
  let(:expression1) { "10*kg" }
107
128
  let(:expression2) { "10*m" }
@@ -4,53 +4,124 @@ shared_examples "convertable value" do
4
4
  describe "converted unit" do
5
5
  let(:original) { described_class.new :expression => from }
6
6
  let(:final) { described_class.new :expression => to }
7
- subject { original.convert_to(final) }
8
7
 
9
- context "with compatible units" do
10
- let(:from) { '2*km' }
11
- let(:to) { 'm' }
8
+ describe "convert_to" do
9
+ subject { original.convert_to(final) }
12
10
 
13
- its(:value) { should == 2000.0 }
14
- its(:unit) { should == 'm' }
15
- end
11
+ context "with compatible units" do
12
+ let(:from) { '2*km' }
13
+ let(:to) { 'm' }
16
14
 
17
- context "with no unit" do
18
- let(:from) { '1.0' }
19
- let(:to) { '' }
15
+ its(:value) { should == 2000.0 }
16
+ its(:unit) { should == 'm' }
17
+ end
20
18
 
21
- its(:value) { should == 1.0 }
22
- its(:unit) { should == '' }
23
- end
19
+ context "with no unit" do
20
+ let(:from) { '1.0' }
21
+ let(:to) { '' }
24
22
 
25
- context "with exponents on units" do
26
- let(:from) { '1000000*m^2' }
27
- let(:to) { 'km^2' }
23
+ its(:value) { should == 1.0 }
24
+ its(:unit) { should == '' }
25
+ end
28
26
 
29
- its(:value) { should == 1.0 }
30
- its(:unit) { should == 'km^2' }
31
- end
27
+ context "with exponents on units" do
28
+ let(:from) { '1000000*m^2' }
29
+ let(:to) { 'km^2' }
32
30
 
33
- context "with non compatible units" do
34
- let(:from) { '1000000*m' }
35
- let(:to) { 'km^2' }
36
- it { should be_nil }
37
- end
31
+ its(:value) { should == 1.0 }
32
+ its(:unit) { should == 'km^2' }
33
+ end
34
+
35
+ context "with non compatible units" do
36
+ let(:from) { '1000000*m' }
37
+ let(:to) { 'km^2' }
38
+ it { should be_nil }
39
+ end
40
+
41
+ context "with derived units" do
42
+ let(:from) { '3.6e7*J' }
43
+ let(:to) { 'kWh' }
44
+
45
+ its(:value) { should == 10.0 }
46
+ its(:unit) { should == 'kWh' }
47
+ end
38
48
 
39
- context "with derived units" do
40
- let(:from) { '3.6e7*J' }
41
- let(:to) { 'kWh' }
49
+ context "with value on the to" do
50
+ let(:from) { '2*km' }
51
+ let(:to) { '10*m' }
52
+
53
+ its(:value) { should == 2000.0 }
54
+ its(:unit) { should == 'm' }
55
+ end
56
+
57
+ context "with string value for to" do
58
+ let(:from) { '2*km' }
59
+ let(:final) { 'm' }
60
+
61
+ its(:value) { should == 2000.0 }
62
+ its(:unit) { should == 'm' }
63
+ end
42
64
 
43
- its(:value) { should == 10.0 }
44
- its(:unit) { should == 'kWh' }
45
65
  end
46
66
 
47
- context "with value on the to" do
48
- let(:from) { '2*km' }
49
- let(:to) { '10*m' }
67
+ describe "convert_to!" do
68
+ subject { original.convert_to!(final) }
69
+
70
+ context "with compatible units" do
71
+ let(:from) { '2*km' }
72
+ let(:to) { 'm' }
73
+
74
+ its(:value) { should == 2000.0 }
75
+ its(:unit) { should == 'm' }
76
+ end
77
+
78
+ context "with no unit" do
79
+ let(:from) { '1.0' }
80
+ let(:to) { '' }
81
+
82
+ its(:value) { should == 1.0 }
83
+ its(:unit) { should == '' }
84
+ end
85
+
86
+ context "with exponents on units" do
87
+ let(:from) { '1000000*m^2' }
88
+ let(:to) { 'km^2' }
89
+
90
+ its(:value) { should == 1.0 }
91
+ its(:unit) { should == 'km^2' }
92
+ end
93
+
94
+ context "with non compatible units" do
95
+ let(:from) { '1000000*m' }
96
+ let(:to) { 'km^2' }
97
+ it { -> { subject }.should raise_exception(Unite::IncompatibleError) }
98
+ end
99
+
100
+ context "with derived units" do
101
+ let(:from) { '3.6e7*J' }
102
+ let(:to) { 'kWh' }
103
+
104
+ its(:value) { should == 10.0 }
105
+ its(:unit) { should == 'kWh' }
106
+ end
107
+
108
+ context "with value on the to" do
109
+ let(:from) { '2*km' }
110
+ let(:to) { '10*m' }
111
+
112
+ its(:value) { should == 2000.0 }
113
+ its(:unit) { should == 'm' }
114
+ end
115
+
116
+ context "with string value for to" do
117
+ let(:from) { '2*km' }
118
+ let(:final) { 'm' }
119
+
120
+ its(:value) { should == 2000.0 }
121
+ its(:unit) { should == 'm' }
122
+ end
50
123
 
51
- its(:value) { should == 2000.0 }
52
- its(:unit) { should == 'm' }
53
124
  end
54
- end
55
125
 
126
+ end
56
127
  end
@@ -6,12 +6,12 @@ shared_examples_for "unit fractions" do
6
6
 
7
7
  describe "required methods" do
8
8
  subject { described_class.new }
9
- it { should respond_to(:value) }
10
- it { should respond_to(:value=) }
11
- it { should respond_to(:numerator) }
12
- it { should respond_to(:numerator=) }
13
- it { should respond_to(:denominator=) }
14
- it { should respond_to(:denominator=) }
9
+ it { should respond_to(:value) }
10
+ it { should respond_to(:value=) }
11
+ it { should respond_to(:numerator) }
12
+ it { should respond_to(:numerator=) }
13
+ it { should respond_to(:denominator=) }
14
+ it { should respond_to(:denominator=) }
15
15
  end
16
16
 
17
17
  describe "big decimal" do
@@ -37,7 +37,7 @@ shared_examples_for "unit fractions" do
37
37
 
38
38
  its(:unit) { should == '' }
39
39
  its(:value) { should == 5.7 }
40
- its(:expression) { should == '5.7' }
40
+ its(:expression) { should == '5.7' }
41
41
  end
42
42
 
43
43
  context "with number and unit" do
@@ -45,7 +45,7 @@ shared_examples_for "unit fractions" do
45
45
 
46
46
  its(:unit) { should == 'm/s^2' }
47
47
  its(:value) { should == 2.0 }
48
- its(:expression) { should == '2.0*m/s^2' }
48
+ its(:expression) { should == '2.0*m/s^2' }
49
49
  end
50
50
 
51
51
  context "with only unit" do
@@ -53,7 +53,7 @@ shared_examples_for "unit fractions" do
53
53
 
54
54
  its(:unit) { should == 'm/s' }
55
55
  its(:value) { should == 1.0 }
56
- its(:expression) { should == '1.0*m/s' }
56
+ its(:expression) { should == '1.0*m/s' }
57
57
  end
58
58
 
59
59
  context "with no numerator" do
@@ -61,7 +61,7 @@ shared_examples_for "unit fractions" do
61
61
 
62
62
  its(:unit) { should == '1.0/s' }
63
63
  its(:value) { should == 10.0 }
64
- its(:expression) { should == '10.0/s' }
64
+ its(:expression) { should == '10.0/s' }
65
65
  end
66
66
 
67
67
  context 'with too many /' do
@@ -75,25 +75,33 @@ shared_examples_for "unit fractions" do
75
75
  let(:expression) { '/' }
76
76
  its(:unit) { should == '' }
77
77
  its(:value) { should == 0.0 }
78
- its(:expression) { should == '0.0' }
78
+ its(:expression) { should == '0.0' }
79
79
  end
80
80
 
81
81
 
82
82
  context "with spaces" do
83
- let(:expression) { '10 * km / 5 *h ' }
83
+ let(:expression) { '10 * km / 5 *h' }
84
84
 
85
85
  its(:unit) { should == 'km/h' }
86
86
  its(:value) { should == 2.0 }
87
- its(:expression) { should == '2.0*km/h' }
87
+ its(:expression) { should == '2.0*km/h' }
88
88
  end
89
89
 
90
+
91
+ context "with negative exponent" do
92
+ let(:expression) { '2 * s^-2 / m^-1 ' }
93
+
94
+ its(:unit) { should == 'm/s^2' }
95
+ its(:value) { should == 2.0 }
96
+ its(:expression) { should == '2.0*m/s^2' }
97
+ end
90
98
  end
91
99
 
92
100
  describe 'default' do
93
- subject { described_class.new }
101
+ subject { described_class.new }
94
102
  its(:unit) { should == '' }
95
103
  its(:value) { should == 0.0 }
96
- its(:denominator) { should == [] }
104
+ its(:denominator) { should == [] }
97
105
  its(:numerator) { should == [] }
98
106
  end
99
107
 
@@ -110,14 +118,14 @@ shared_examples_for "unit fractions" do
110
118
  let(:expression) { 'kg*s^2/s*kg^3' }
111
119
 
112
120
  its(:unit) { should == 's/kg^2' }
113
- its(:expression) { should == '1.0*s/kg^2' }
121
+ its(:expression) { should == '1.0*s/kg^2' }
114
122
  end
115
123
 
116
124
  context "with empty numerator" do
117
125
  let(:expression) { 'kg/kg^3' }
118
126
 
119
127
  its(:unit) { should == '1.0/kg^2' }
120
- its(:expression) { should == '1.0/kg^2' }
128
+ its(:expression) { should == '1.0/kg^2' }
121
129
  end
122
130
 
123
131
  end
@@ -0,0 +1,26 @@
1
+ # -*- encoding : utf-8 -*-
2
+ shared_examples "simplify units" do
3
+
4
+ describe "converted unit" do
5
+ subject {described_class.new(:expression => expression).simplify }
6
+
7
+
8
+ context "with cancelling units" do
9
+ let(:expression) { '5.7*km^2/m*h' }
10
+
11
+ its(:unit) { should == 'km/h' }
12
+ its(:value) { should be_within(1e-10).of(5700) }
13
+ its(:expression) { should == '5700.0*km/h' }
14
+ end
15
+
16
+ context "with a equivalent derived unit" do
17
+ let(:expression) { '1*kg*m^2/s^2' }
18
+
19
+ its(:unit) { should == 'J' }
20
+ its(:value) { should be_within(1e-10).of(1) }
21
+ its(:expression) { should == '1.0*J' }
22
+ end
23
+
24
+ end
25
+ end
26
+