unite 1.2.0 → 1.3.0

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.
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
+