unitfy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +4 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +89 -0
  5. data/Guardfile +15 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README +0 -0
  8. data/Rakefile +3 -0
  9. data/lib/unity.rb +20 -0
  10. data/lib/unity/arithmetic.rb +64 -0
  11. data/lib/unity/comparison.rb +33 -0
  12. data/lib/unity/conversion.rb +46 -0
  13. data/lib/unity/dimension.rb +37 -0
  14. data/lib/unity/dimension/integer.rb +34 -0
  15. data/lib/unity/dimension/vector.rb +43 -0
  16. data/lib/unity/fraction.rb +118 -0
  17. data/lib/unity/lookup.rb +102 -0
  18. data/lib/unity/lookup/definitions.rb +50 -0
  19. data/lib/unity/lookup/derived_unit.rb +24 -0
  20. data/lib/unity/lookup/property.rb +21 -0
  21. data/lib/unity/lookup/simple_unit.rb +26 -0
  22. data/lib/unity/quantity.rb +20 -0
  23. data/lib/unity/version.rb +3 -0
  24. data/spec/arithmetic_spec.rb +6 -0
  25. data/spec/comparison_spec.rb +6 -0
  26. data/spec/conversion_spec.rb +8 -0
  27. data/spec/dimension/integer_spec.rb +7 -0
  28. data/spec/dimension/vector_spec.rb +7 -0
  29. data/spec/dimension_spec.rb +7 -0
  30. data/spec/fabricators/unit_fabricator.rb +14 -0
  31. data/spec/fraction_spec.rb +7 -0
  32. data/spec/lookup/derived_unit_spec.rb +44 -0
  33. data/spec/lookup/property_spec.rb +18 -0
  34. data/spec/lookup/simple_unit_spec.rb +41 -0
  35. data/spec/lookup_spec.rb +135 -0
  36. data/spec/quantity_spec.rb +16 -0
  37. data/spec/spec_helper.rb +28 -0
  38. data/spec/support/load_debugger.rb +7 -0
  39. data/spec/support/shared_examples/units/arithmetic.rb +127 -0
  40. data/spec/support/shared_examples/units/comparison.rb +69 -0
  41. data/spec/support/shared_examples/units/conversion.rb +49 -0
  42. data/spec/support/shared_examples/units/dimension/integer.rb +41 -0
  43. data/spec/support/shared_examples/units/dimension/vector.rb +10 -0
  44. data/spec/support/shared_examples/units/fractions.rb +131 -0
  45. data/unitfy.gemspec +24 -0
  46. metadata +133 -0
@@ -0,0 +1,44 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'unity/lookup/derived_unit.rb'
4
+
5
+ module Unity
6
+ describe DerivedUnit do
7
+ it { should validate_presence_of(:name)}
8
+
9
+ %w{ F J Hz N }.each do |unit|
10
+ it "should allow valid unit #{unit}" do
11
+ Fabricate.build(:derived_unit, :name => unit).should be_valid
12
+ end
13
+ end
14
+
15
+ %w{km^3 m/g km*tonne}.each do |unit|
16
+ it "should not allow invalid unit #{unit}" do
17
+ Fabricate.build(:derived_unit, :name => unit).should_not be_valid
18
+ end
19
+ end
20
+
21
+ describe 'based off of simple units' do
22
+ subject { DerivedUnit.new(:name => "J", :expression => 'kg*m^2/s^2') }
23
+
24
+ its(:dimension_vector) { should == [2, -2, 0, 1, 0, 0, 0, 0] }
25
+ its(:property_name) { should == :energy }
26
+ end
27
+
28
+ describe 'based off of other derived unit' do
29
+
30
+ subject { DerivedUnit.new(:name => "kWh", :expression => '3.6e5*J') }
31
+
32
+ its(:dimension_vector) { should == [2, -2, 0, 1, 0, 0, 0, 0] }
33
+ its(:property_name) { should == :energy }
34
+
35
+ end
36
+
37
+ include_examples "comparable unit"
38
+ include_examples "dimension integer"
39
+ include_examples "convertable value"
40
+ include_examples "dimension vectors"
41
+ include_examples "unit fractions"
42
+
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'unity/lookup/property.rb'
4
+
5
+ module Unity
6
+ describe Property do
7
+ it { should validate_presence_of(:name)}
8
+
9
+
10
+ include_examples "comparable unit"
11
+ include_examples "dimension integer"
12
+ include_examples "convertable value"
13
+ include_examples "dimension vectors"
14
+ include_examples "unit fractions"
15
+
16
+ end
17
+ end
18
+
@@ -0,0 +1,41 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'unity/lookup/simple_unit.rb'
4
+
5
+ module Unity
6
+ describe SimpleUnit do
7
+ it { should ensure_inclusion_of(:dimension).in_array(Dimension::LIST) }
8
+ it { should validate_presence_of(:si_factor)}
9
+ it { should validate_presence_of(:name)}
10
+ it { should validate_presence_of(:dimension)}
11
+
12
+ %w{ $ £ € m }.each do |unit|
13
+ it "should allow valid unit #{unit}" do
14
+ Fabricate.build(:simple_unit, :name => unit).should be_valid
15
+ end
16
+ end
17
+
18
+ %w{km^3 m/g km*tonne}.each do |unit|
19
+ it "should not allow invalid unit #{unit}" do
20
+ Fabricate.build(:simple_unit, :name => unit).should_not be_valid
21
+ end
22
+ end
23
+
24
+ describe "instance methods" do
25
+ let(:dimension) { :length }
26
+ subject { Fabricate.build :simple_unit, :name => "m", :si_factor => 1, :dimension => :length }
27
+
28
+ its(:dimension_vector) { should == Dimension::LIST.map{|d| d == dimension ? 1 : 0}}
29
+ its(:property_name) { should == :distance }
30
+
31
+ end
32
+
33
+ describe "class methods" do
34
+ subject { SimpleUnit }
35
+
36
+ end
37
+
38
+ include_examples "dimension integer"
39
+
40
+ end
41
+ end
@@ -0,0 +1,135 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module Unity
5
+ describe Lookup do
6
+ subject { Lookup }
7
+
8
+
9
+ describe "adding and finding" do
10
+
11
+ def unit_stub stubs_hash
12
+ stub({:valid? => true, :errors => {} , :dimension_int => 30}.merge(stubs_hash))
13
+ end
14
+
15
+ def property_stub stubs_hash
16
+ stub({:name => :quasar, :expression => 'qu', :valid? => true, :errors => {} , :dimension_int => 30}.merge(stubs_hash))
17
+ end
18
+
19
+
20
+ after do
21
+ subject.reload!
22
+ end
23
+
24
+ describe "units" do
25
+ let(:name) { 'scott' }
26
+ let(:unit_object) { unit_stub(:valid? => valid, :name => name) }
27
+ context "with invalid object" do
28
+ let(:valid) { false }
29
+
30
+ it "should raise an error" do
31
+ lambda { subject.add(unit_object) }.should raise_exception(Lookup::Invalid)
32
+ end
33
+
34
+ end
35
+
36
+ context "with valid object" do
37
+ let(:valid) { true }
38
+ before do
39
+ subject.add(unit_object)
40
+ end
41
+
42
+ it 'should be able to find the unit by name' do
43
+ subject.find!(name).should == unit_object
44
+ end
45
+
46
+
47
+ it "should raise not found if unit is undefined" do
48
+ lambda { subject.find!(name+"undefined") }.should raise_exception(Lookup::Undefined)
49
+ end
50
+
51
+ context "with non uniq name" do
52
+ let(:duplicate_name) { name }
53
+ let(:duplicate_unit_object) { unit_stub(:name => duplicate_name) }
54
+
55
+ it "should raise an error" do
56
+ lambda { subject.add(duplicate_unit_object) }.should raise_exception(Lookup::Duplicate)
57
+ end
58
+ end
59
+
60
+ describe "compatible units" do
61
+ let(:compatible_unit_object) { unit_stub(:name => "unit_name1", :dimension_int => unit_object.dimension_int) }
62
+ let(:incompatible_unit_object) { unit_stub(:name => "unit_name2", :dimension_int => (unit_object.dimension_int + 10)) }
63
+
64
+ before do
65
+ subject.add(compatible_unit_object)
66
+ subject.add(incompatible_unit_object)
67
+ end
68
+
69
+ describe "by integer" do
70
+ it "should find compatible units" do
71
+ subject.compatible_units(unit_object.dimension_int).should =~ [compatible_unit_object, unit_object]
72
+ end
73
+ end
74
+
75
+ describe "by property" do
76
+ let(:property_object) { property_stub(:dimension_int => unit_object.dimension_int) }
77
+ before do
78
+ subject.add_property(property_object)
79
+ end
80
+
81
+ it "should find compatible units" do
82
+ subject.compatible_units(property_object.name).should =~ [compatible_unit_object, unit_object]
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+
89
+ end
90
+
91
+ describe "property" do
92
+ let(:propert_object) { property_stub(:name => :quasar, :expression => 'qu', :valid? => valid)}
93
+
94
+ context 'with invalid object' do
95
+ let(:valid) { false }
96
+ it "should raise an error" do
97
+ lambda { subject.add_property(propert_object) }.should raise_exception(Lookup::Invalid)
98
+ end
99
+ end
100
+
101
+ context 'with valid object' do
102
+ let(:valid) { true }
103
+ before do
104
+ subject.add_property(propert_object)
105
+ end
106
+
107
+ it "should give a list of properties" do
108
+ subject.property_names.should include(propert_object.name)
109
+ end
110
+
111
+ it "should return nil if not found" do
112
+ subject.find_property("undefined#{propert_object.name}").should == nil
113
+ end
114
+
115
+ it "should raise exception if already defined" do
116
+ lambda {subject.add_property(propert_object)}.should raise_exception Lookup::Duplicate
117
+ end
118
+
119
+ it 'should be able to find by dimension id' do
120
+ subject.find_property(propert_object.dimension_int).should == propert_object
121
+ end
122
+
123
+ it 'should be able to find by name' do
124
+ subject.find_property(propert_object.name).should == propert_object
125
+ end
126
+
127
+
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module Unity
5
+ describe Quantity do
6
+
7
+ subject { Quantity.new :expression => expression }
8
+
9
+ include_examples "unit arithmetic"
10
+ include_examples "comparable unit"
11
+ include_examples "dimension integer"
12
+ include_examples "convertable value"
13
+ include_examples "dimension vectors"
14
+ include_examples "unit fractions"
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+
7
+ require 'rspec'
8
+ require 'shoulda/matchers/active_model'
9
+ require 'mocha'
10
+ require 'unity'
11
+ require 'fabrication'
12
+
13
+ # Requires supporting files with custom matchers and macros, etc,
14
+ # in ./support/ and its subdirectories.
15
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
16
+
17
+ RSpec.configure do |config|
18
+
19
+ config.mock_with :mocha
20
+
21
+ config.before(:each) do
22
+ end
23
+
24
+ config.include(Shoulda::Matchers::ActiveModel)
25
+
26
+ config.filter_run :focus => true
27
+ config.run_all_when_everything_filtered = true
28
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+ begin
3
+ require 'ruby-debug'
4
+ puts "** USING THE RUBY DEBUGGER **"
5
+ rescue LoadError
6
+ puts "!!! RUBY DEBUGGER NOT INSTALLED !!!"
7
+ end
@@ -0,0 +1,127 @@
1
+ # -*- encoding : utf-8 -*-
2
+ shared_examples "unit arithmetic" do
3
+
4
+ shared_examples "addition operation" do
5
+
6
+ context "addition result" do
7
+ subject { object1 + object2 }
8
+ let(:addition_value) { addition_result.first }
9
+ let(:addition_unit) { addition_result.last }
10
+
11
+ its(:value) { should == addition_value }
12
+ its(:unit) { should == addition_unit }
13
+ end
14
+
15
+ end
16
+ shared_examples "subtraction operation" do
17
+
18
+ context "subtraction result" do
19
+ subject { object1 - object2 }
20
+ let(:subtraction_value) { subtraction_result.first }
21
+ let(:subtraction_unit) { subtraction_result.last }
22
+
23
+ its(:value) { should == subtraction_value }
24
+ its(:unit) { should == subtraction_unit }
25
+ end
26
+
27
+ end
28
+ shared_examples "multiplication operation" do
29
+
30
+ context "multiplication result" do
31
+ subject { object1 * object2 }
32
+ let(:multiplication_value) { multiplication_result.first }
33
+ let(:multiplication_unit) { multiplication_result.last }
34
+
35
+ its(:value) { should == multiplication_value }
36
+ its(:unit) { should == multiplication_unit }
37
+ end
38
+
39
+ end
40
+ shared_examples "division operation" do
41
+
42
+ context "division result" do
43
+ subject { object1 / object2 }
44
+ let(:division_value) { division_result.first }
45
+ let(:division_unit) { division_result.last }
46
+
47
+ its(:value) { should == division_value }
48
+ its(:unit) { should == division_unit }
49
+ end
50
+
51
+ end
52
+
53
+ describe "arithmetic" do
54
+ describe 'with no units' do
55
+ let(:expression1) { 100.0 }
56
+ let(:expression2) { 10.0 }
57
+
58
+ let(:addition_result) { [110.0, ''] }
59
+ let(:subtraction_result) { [90.0, ''] }
60
+ let(:division_result) { [10.0, ''] }
61
+ let(:multiplication_result) { [1000.0, ''] }
62
+
63
+ context "with an numeric as the first object" do
64
+ let(:object1) { expression1.to_f }
65
+ let(:object2) { described_class.new :expression => expression2.to_s }
66
+
67
+ include_examples "addition operation"
68
+ include_examples "subtraction operation"
69
+ include_examples "multiplication operation"
70
+ include_examples "division operation"
71
+ end
72
+
73
+
74
+ context "with an numeric as the second object" do
75
+ let(:object1) { described_class.new :expression => expression1.to_s }
76
+ let(:object2) { expression2.to_f }
77
+
78
+ include_examples "addition operation"
79
+ include_examples "subtraction operation"
80
+ include_examples "multiplication operation"
81
+ include_examples "division operation"
82
+ end
83
+
84
+ end
85
+
86
+ context "with units" do
87
+ let(:object1) { described_class.new :expression => expression1 }
88
+ let(:object2) { described_class.new :expression => expression2 }
89
+
90
+ context "that are compatible" do
91
+ let(:expression1) { "10*km" }
92
+ let(:expression2) { "1000*m" }
93
+
94
+ let(:addition_result) { [11.0, 'km'] }
95
+ let(:subtraction_result) { [9.0, 'km'] }
96
+ let(:division_result) { [10.0, ''] }
97
+ let(:multiplication_result) { [10.0, 'km^2'] }
98
+
99
+ include_examples "addition operation"
100
+ include_examples "subtraction operation"
101
+ include_examples "multiplication operation"
102
+ include_examples "division operation"
103
+ end
104
+
105
+ context "that are incompatible " do
106
+ let(:expression1) { "10*kg" }
107
+ let(:expression2) { "10*m" }
108
+
109
+ let(:division_result) { [1.0, 'kg/m'] }
110
+ let(:multiplication_result) { [100.0, 'kg*m'] }
111
+
112
+ include_examples "multiplication operation"
113
+ include_examples "division operation"
114
+
115
+ it "should raise error for subtraction" do
116
+ lambda { object1 - object2 }.should raise_exception Unity::IncompatibleError
117
+ end
118
+ it "should raise error for addition" do
119
+ lambda { object1 + object2 }.should raise_exception Unity::IncompatibleError
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+
@@ -0,0 +1,69 @@
1
+ # -*- encoding : utf-8 -*-
2
+ shared_examples_for "comparable unit" do
3
+ let(:comparible1) { described_class.new :expression => expression1 }
4
+ let(:comparible2) { described_class.new :expression => expression2 }
5
+
6
+ describe "#compatible?" do
7
+ subject { comparible1.compatible? comparible2 }
8
+
9
+ context "with compatible units" do
10
+ let(:expression1) { "km" }
11
+ let(:expression2) { "m" }
12
+
13
+ it { should be_true }
14
+ end
15
+
16
+
17
+ context "with non compatible units" do
18
+ let(:expression1) { "km" }
19
+ let(:expression2) { "g" }
20
+
21
+ it { should be_false }
22
+ end
23
+ end
24
+
25
+ describe "#compatible!" do
26
+ subject { comparible1.compatible! comparible2 }
27
+
28
+ context "with compatible units" do
29
+ let(:expression1) { "km" }
30
+ let(:expression2) { "m" }
31
+
32
+ it { should be_true }
33
+ end
34
+
35
+
36
+ context "with non compatible units" do
37
+ let(:expression1) { "km" }
38
+ let(:expression2) { "g" }
39
+
40
+ it "should raise exception" do
41
+ lambda { subject }.should raise_exception Unity::IncompatibleError
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "equality #==" do
47
+ subject { comparible1 == comparible2 }
48
+
49
+ context "with comparible units" do
50
+ let(:expression1) { "1*km" }
51
+
52
+ context "and same value" do
53
+ let(:expression2) { "1000*m" }
54
+ it { should be_true }
55
+ end
56
+
57
+ context "and different value" do
58
+ let(:expression2) { "1*m" }
59
+ it { should be_false }
60
+ end
61
+ end
62
+
63
+ context "with non comparible units" do
64
+ let(:expression1) { "1*m" }
65
+ let(:expression2) { "1*g" }
66
+ it { should be_false }
67
+ end
68
+ end
69
+ end