unite 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +4 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +89 -0
- data/Guardfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README +0 -0
- data/Rakefile +3 -0
- data/lib/unity/arithmetic.rb +64 -0
- data/lib/unity/comparison.rb +33 -0
- data/lib/unity/conversion.rb +46 -0
- data/lib/unity/dimension/integer.rb +34 -0
- data/lib/unity/dimension/vector.rb +43 -0
- data/lib/unity/dimension.rb +37 -0
- data/lib/unity/fraction.rb +118 -0
- data/lib/unity/lookup/definitions.rb +50 -0
- data/lib/unity/lookup/derived_unit.rb +24 -0
- data/lib/unity/lookup/property.rb +21 -0
- data/lib/unity/lookup/simple_unit.rb +26 -0
- data/lib/unity/lookup.rb +102 -0
- data/lib/unity/quantity.rb +20 -0
- data/lib/unity/version.rb +3 -0
- data/lib/unity.rb +20 -0
- data/spec/arithmetic_spec.rb +6 -0
- data/spec/comparison_spec.rb +6 -0
- data/spec/conversion_spec.rb +8 -0
- data/spec/dimension/integer_spec.rb +7 -0
- data/spec/dimension/vector_spec.rb +7 -0
- data/spec/dimension_spec.rb +7 -0
- data/spec/fabricators/unit_fabricator.rb +14 -0
- data/spec/fraction_spec.rb +7 -0
- data/spec/lookup/derived_unit_spec.rb +44 -0
- data/spec/lookup/property_spec.rb +18 -0
- data/spec/lookup/simple_unit_spec.rb +41 -0
- data/spec/lookup_spec.rb +135 -0
- data/spec/quantity_spec.rb +16 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/load_debugger.rb +7 -0
- data/spec/support/shared_examples/units/arithmetic.rb +127 -0
- data/spec/support/shared_examples/units/comparison.rb +69 -0
- data/spec/support/shared_examples/units/conversion.rb +49 -0
- data/spec/support/shared_examples/units/dimension/integer.rb +41 -0
- data/spec/support/shared_examples/units/dimension/vector.rb +10 -0
- data/spec/support/shared_examples/units/fractions.rb +131 -0
- data/unite.gemspec +24 -0
- metadata +133 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in unity.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'SystemTimer', :platform => :mri_18
|
8
|
+
gem 'ruby-debug', :platform => :mri_18
|
9
|
+
gem 'linecache19', '>= 0.5.13', :platform => :mri_19
|
10
|
+
gem 'ruby-debug-base19', '>= 0.11.26', :platform => :mri_19
|
11
|
+
gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
|
12
|
+
gem 'mocha'
|
13
|
+
gem 'rspec'
|
14
|
+
gem 'shoulda-matchers', :git => 'git://github.com/thoughtbot/shoulda-matchers.git', :require => 'false'
|
15
|
+
gem 'fabrication'
|
16
|
+
gem 'guard-rspec'
|
17
|
+
gem 'guard-bundler'
|
18
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/thoughtbot/shoulda-matchers.git
|
3
|
+
revision: 4fdb82707c252e3bdf6079a8597f2ace350b5d2d
|
4
|
+
specs:
|
5
|
+
shoulda-matchers (1.1.0)
|
6
|
+
activesupport (>= 3.0.0)
|
7
|
+
|
8
|
+
PATH
|
9
|
+
remote: .
|
10
|
+
specs:
|
11
|
+
unite (1.0.0)
|
12
|
+
activemodel (>= 3.0.0)
|
13
|
+
activesupport (>= 3.0.0)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: http://rubygems.org/
|
17
|
+
specs:
|
18
|
+
SystemTimer (1.2.3)
|
19
|
+
activemodel (3.1.3)
|
20
|
+
activesupport (= 3.1.3)
|
21
|
+
builder (~> 3.0.0)
|
22
|
+
i18n (~> 0.6)
|
23
|
+
activesupport (3.1.3)
|
24
|
+
multi_json (~> 1.0)
|
25
|
+
archive-tar-minitar (0.5.2)
|
26
|
+
builder (3.0.0)
|
27
|
+
columnize (0.3.6)
|
28
|
+
diff-lcs (1.1.3)
|
29
|
+
fabrication (1.2.0)
|
30
|
+
ffi (1.0.11)
|
31
|
+
guard (0.10.0)
|
32
|
+
ffi (>= 0.5.0)
|
33
|
+
thor (~> 0.14.6)
|
34
|
+
guard-bundler (0.1.3)
|
35
|
+
bundler (>= 1.0.0)
|
36
|
+
guard (>= 0.2.2)
|
37
|
+
guard-rspec (0.6.0)
|
38
|
+
guard (>= 0.10.0)
|
39
|
+
i18n (0.6.0)
|
40
|
+
linecache (0.46)
|
41
|
+
rbx-require-relative (> 0.0.4)
|
42
|
+
linecache19 (0.5.13)
|
43
|
+
ruby_core_source (>= 0.1.4)
|
44
|
+
metaclass (0.0.1)
|
45
|
+
mocha (0.10.1)
|
46
|
+
metaclass (~> 0.0.1)
|
47
|
+
multi_json (1.3.4)
|
48
|
+
rbx-require-relative (0.0.5)
|
49
|
+
rspec (2.8.0)
|
50
|
+
rspec-core (~> 2.8.0)
|
51
|
+
rspec-expectations (~> 2.8.0)
|
52
|
+
rspec-mocks (~> 2.8.0)
|
53
|
+
rspec-core (2.8.0)
|
54
|
+
rspec-expectations (2.8.0)
|
55
|
+
diff-lcs (~> 1.1.2)
|
56
|
+
rspec-mocks (2.8.0)
|
57
|
+
ruby-debug (0.10.4)
|
58
|
+
columnize (>= 0.1)
|
59
|
+
ruby-debug-base (~> 0.10.4.0)
|
60
|
+
ruby-debug-base (0.10.4)
|
61
|
+
linecache (>= 0.3)
|
62
|
+
ruby-debug-base19 (0.11.26)
|
63
|
+
columnize (>= 0.3.1)
|
64
|
+
linecache19 (>= 0.5.11)
|
65
|
+
ruby_core_source (>= 0.1.4)
|
66
|
+
ruby-debug19 (0.11.6)
|
67
|
+
columnize (>= 0.3.1)
|
68
|
+
linecache19 (>= 0.5.11)
|
69
|
+
ruby-debug-base19 (>= 0.11.19)
|
70
|
+
ruby_core_source (0.1.5)
|
71
|
+
archive-tar-minitar (>= 0.5.2)
|
72
|
+
thor (0.14.6)
|
73
|
+
|
74
|
+
PLATFORMS
|
75
|
+
ruby
|
76
|
+
|
77
|
+
DEPENDENCIES
|
78
|
+
SystemTimer
|
79
|
+
fabrication
|
80
|
+
guard-bundler
|
81
|
+
guard-rspec
|
82
|
+
linecache19 (>= 0.5.13)
|
83
|
+
mocha
|
84
|
+
rspec
|
85
|
+
ruby-debug
|
86
|
+
ruby-debug-base19 (>= 0.11.26)
|
87
|
+
ruby-debug19
|
88
|
+
shoulda-matchers!
|
89
|
+
unite!
|
data/Guardfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
7
|
+
watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rspec', :cli => '--backtrace', :version => 2 do
|
11
|
+
watch(%r{^spec/.+_spec\.rb$})
|
12
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
13
|
+
watch('spec/spec_helper.rb') { "spec" }
|
14
|
+
end
|
15
|
+
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Scott Ellard
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
module Arithmetic
|
4
|
+
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
include Conversion
|
7
|
+
|
8
|
+
included do
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def + other
|
17
|
+
other = ensure_not_numeric other
|
18
|
+
self.compatible! other
|
19
|
+
other = other.convert_to(self)
|
20
|
+
self.class.new(:value => (self.value + other.value), :numerator => self.numerator, :denominator => self.denominator)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def - other
|
25
|
+
other = ensure_not_numeric other
|
26
|
+
self.compatible! other
|
27
|
+
other = other.convert_to(self)
|
28
|
+
self.class.new(:value => (self.value - other.value), :numerator => self.numerator, :denominator => self.denominator)
|
29
|
+
end
|
30
|
+
|
31
|
+
def / other
|
32
|
+
other = ensure_not_numeric other
|
33
|
+
other = other.convert_to(self) if self.compatible? other
|
34
|
+
self * other.inverse
|
35
|
+
end
|
36
|
+
|
37
|
+
def * other
|
38
|
+
other = ensure_not_numeric other
|
39
|
+
other = other.convert_to(self) if self.compatible? other
|
40
|
+
new_numerator = self.numerator + other.numerator
|
41
|
+
new_denominator = self.denominator + other.denominator
|
42
|
+
self.class.new(:value => (self.value * other.value), :numerator => new_numerator, :denominator => new_denominator).tap(&:reduce)
|
43
|
+
end
|
44
|
+
|
45
|
+
def coerce(other)
|
46
|
+
case other
|
47
|
+
when Numeric
|
48
|
+
return convert_numeric(other), self
|
49
|
+
else
|
50
|
+
raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def convert_numeric other
|
57
|
+
self.class.new :expression => other.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def ensure_not_numeric other
|
61
|
+
other.is_a?(Numeric) ? convert_numeric(other) : other
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class IncompatibleError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Comparison
|
7
|
+
|
8
|
+
extend ::ActiveSupport::Concern
|
9
|
+
include Dimension::Integer
|
10
|
+
|
11
|
+
included do
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def compatible? other
|
20
|
+
self.dimension_int == other.dimension_int
|
21
|
+
end
|
22
|
+
|
23
|
+
def compatible! other
|
24
|
+
compatible?(other) ? true : raise(IncompatibleError)
|
25
|
+
end
|
26
|
+
|
27
|
+
def == other
|
28
|
+
self.compatible?(other) && self.convert_to(other).value == other.value
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class IncompatibleError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Conversion
|
7
|
+
|
8
|
+
extend ::ActiveSupport::Concern
|
9
|
+
include Comparison
|
10
|
+
include Fraction
|
11
|
+
|
12
|
+
included do
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def convert_to other
|
21
|
+
other = other.clone.tap{|o| o.value = 1.0 } #this is so that only the unit is used for the conversion
|
22
|
+
new_value = self.converted_value(other)
|
23
|
+
return nil if new_value.nil?
|
24
|
+
self.class.new :value => new_value, :numerator => other.numerator, :denominator => other.denominator
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def converted_value other
|
30
|
+
return nil unless self.compatible?(other)
|
31
|
+
self.si_factor / other.si_factor
|
32
|
+
end
|
33
|
+
|
34
|
+
def si_factor
|
35
|
+
value * (get_si_factor(numerator) / get_si_factor(denominator))
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_si_factor unit_array
|
39
|
+
expand_unit_array(unit_array).map do |element|
|
40
|
+
Lookup.find!(element).si_factor
|
41
|
+
end.inject(1.0) {|product, factor| product*factor}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
module Dimension
|
4
|
+
module Integer
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def dimension_int
|
15
|
+
generate_dimension_integer
|
16
|
+
end
|
17
|
+
|
18
|
+
def property_name
|
19
|
+
property.nil? ? nil : property.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def property
|
23
|
+
Lookup.find_property(dimension_int)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def generate_dimension_integer
|
29
|
+
LIST.length.times.inject(0){|sum, i| sum + (DIMVALS[i] * dimension_vector[i])}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
module Dimension
|
4
|
+
module Vector
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
include Unity::Fraction
|
7
|
+
|
8
|
+
included do
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def dimension_vector
|
16
|
+
generate_dimension_vector
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def generate_dimension_vector
|
22
|
+
subtract_vectors numerator_dimension_vector, denominator_dimension_vector
|
23
|
+
end
|
24
|
+
|
25
|
+
def numerator_dimension_vector
|
26
|
+
self.expanded_numerator.inject(Dimension.blank_dimension_vector){|v, unit| add_vectors(v, Lookup.find!(unit).dimension_vector) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def denominator_dimension_vector
|
30
|
+
self.expanded_denominator.inject(Dimension.blank_dimension_vector){|v, unit| add_vectors(v, Lookup.find!(unit).dimension_vector) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_vectors v1, v2
|
34
|
+
LIST.length.times.map{|i| v1[i] + v2[i]}
|
35
|
+
end
|
36
|
+
|
37
|
+
def subtract_vectors v1, v2
|
38
|
+
LIST.length.times.map{|i| v1[i] - v2[i]}
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
module Dimension
|
4
|
+
extend self
|
5
|
+
|
6
|
+
#for a description of this algorithm refer to http://www.cs.utexas.edu/users/novak/units95.html
|
7
|
+
|
8
|
+
# 0 length meter
|
9
|
+
# 1 time second
|
10
|
+
# 2 temperature kelvin
|
11
|
+
# 3 mass kilogram
|
12
|
+
# 4 current ampere
|
13
|
+
# 5 substance mole
|
14
|
+
# 6 luminosity candela
|
15
|
+
# 7 money pound
|
16
|
+
|
17
|
+
LIST = [:length, :time, :temperature, :mass, :current, :substance, :luminosity, :money]
|
18
|
+
UNITS = ['meter', 'second', 'kelvin', 'kilogram', 'ampere', 'mole', 'candela', 'pound']
|
19
|
+
INDICIES = Hash[*LIST.each_with_index{|d,i| [d,i]}.flatten]
|
20
|
+
|
21
|
+
DIMSIZES = [20, 20, 20, 10, 10, 10, 10, 10]
|
22
|
+
DIMVALS = [1, 20, 400, 8000, 80000, 800000, 8000000, 80000000]
|
23
|
+
|
24
|
+
def si_unit dimension
|
25
|
+
UNITS[INDICIES[dimension.to_sym]]
|
26
|
+
end
|
27
|
+
|
28
|
+
def blank_dimension_vector
|
29
|
+
Array.new(LIST.length, 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'unity/dimension/integer'
|
36
|
+
require 'unity/dimension/vector'
|
37
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class InvalidFormat < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
module Fraction
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize *args
|
18
|
+
self.value ||= 0.0
|
19
|
+
self.numerator ||= []
|
20
|
+
self.denominator ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def expression=string
|
25
|
+
unless string.blank?
|
26
|
+
num_denom = string.split('/')
|
27
|
+
raise(InvalidFormat, string) if num_denom.length > 2
|
28
|
+
self.numerator = num_denom[0].split('*').map(&:strip)
|
29
|
+
self.denominator = (num_denom[1].nil? ? [] : num_denom[1].split('*')).map(&:strip)
|
30
|
+
self.value = extract_value!(:numerator) / extract_value!(:denominator)
|
31
|
+
reduce
|
32
|
+
else
|
33
|
+
self.value = 0.0
|
34
|
+
self.numerator = []
|
35
|
+
self.denominator = []
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def expression
|
40
|
+
numertor_string = ([value] + (numerator || [])).join('*')
|
41
|
+
denominator.empty? ? numertor_string : "#{numertor_string}/#{denominator.join('*')}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def unit
|
45
|
+
numertor_string = (numerator.blank? && !denominator.blank?) ? '1.0' : (numerator || []).join('*')
|
46
|
+
denominator.blank? ? numertor_string : "#{numertor_string}/#{denominator.join('*')}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def expanded_numerator
|
50
|
+
expand_unit_array numerator
|
51
|
+
end
|
52
|
+
|
53
|
+
def expanded_denominator
|
54
|
+
expand_unit_array denominator
|
55
|
+
end
|
56
|
+
|
57
|
+
def inverse
|
58
|
+
new_value = value == 0.0 ? 0.0 : 1.0 / value
|
59
|
+
self.class.new :value => new_value, :numerator => denominator, :denominator => numerator
|
60
|
+
end
|
61
|
+
|
62
|
+
def reduce
|
63
|
+
expand
|
64
|
+
|
65
|
+
self.denominator.delete_if do |unit|
|
66
|
+
if nindex = self.numerator.index(unit)
|
67
|
+
self.numerator.delete_at(nindex)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
self.numerator = reduce_unit_array(self.numerator)
|
72
|
+
self.denominator = reduce_unit_array(self.denominator)
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def expand
|
79
|
+
self.numerator = expanded_numerator
|
80
|
+
self.denominator = expanded_denominator
|
81
|
+
end
|
82
|
+
|
83
|
+
def seperate! array
|
84
|
+
[].tap{|rejected| array.delete_if { |v| yield(v) && rejected << v }}
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_value! method
|
88
|
+
number_regex = /\A[-+]?\d*\.?\d+([eE][-+]?\d+)?\Z/
|
89
|
+
seperate!(self.send(method)){|x| number_regex =~ x }.map(&:to_f).
|
90
|
+
inject(1.0){|product, number| product*number }
|
91
|
+
end
|
92
|
+
|
93
|
+
def reduce_unit_array array
|
94
|
+
old_array = array.clone
|
95
|
+
[].tap do |new_array|
|
96
|
+
while !old_array.empty?
|
97
|
+
unit = old_array[0]
|
98
|
+
count = old_array.count(unit)
|
99
|
+
old_array.delete(unit)
|
100
|
+
count = (count == 1) ? '' : "^#{count}"
|
101
|
+
new_array.push("#{unit}#{count}")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def expand_unit_array array
|
107
|
+
array.collect do |unit|
|
108
|
+
if /\A([^\^\s]+)(\^(\d+))?\Z/ =~ unit
|
109
|
+
unit = Regexp.last_match(1)
|
110
|
+
power = Regexp.last_match(3).blank? ? 1 : Regexp.last_match(3).to_i
|
111
|
+
Array.new(power, unit)
|
112
|
+
else
|
113
|
+
raise InvalidFormat, "Invalid format for Unit '#{unit}'"
|
114
|
+
end
|
115
|
+
end.flatten.compact
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module Unity
|
4
|
+
|
5
|
+
Lookup.clear!
|
6
|
+
|
7
|
+
#SIMPLE UNITS
|
8
|
+
|
9
|
+
#TIME
|
10
|
+
Lookup.add SimpleUnit.new(:name => "s", :si_factor => 1, :dimension => :time)
|
11
|
+
Lookup.add SimpleUnit.new(:name => "h", :si_factor => 3600, :dimension => :time)
|
12
|
+
|
13
|
+
Lookup.add_property Property.new(:name => :time, :expression => 'h')
|
14
|
+
|
15
|
+
#LENGTH
|
16
|
+
Lookup.add SimpleUnit.new(:name => "m", :si_factor => 1, :dimension => :length)
|
17
|
+
Lookup.add SimpleUnit.new(:name => "km", :si_factor => 1000, :dimension => :length)
|
18
|
+
|
19
|
+
Lookup.add_property Property.new(:name => :distance, :expression => 'km')
|
20
|
+
|
21
|
+
|
22
|
+
#MASS
|
23
|
+
Lookup.add SimpleUnit.new(:name => "g", :si_factor => 1, :dimension => :mass)
|
24
|
+
Lookup.add SimpleUnit.new(:name => "kg", :si_factor => 1000, :dimension => :mass)
|
25
|
+
Lookup.add SimpleUnit.new(:name => "tonne", :si_factor => 1000000, :dimension => :mass)
|
26
|
+
|
27
|
+
Lookup.add_property Property.new(:name => :mass, :expression => 'tonne')
|
28
|
+
|
29
|
+
|
30
|
+
#DERIVED UNITS
|
31
|
+
|
32
|
+
#ENERGY
|
33
|
+
Lookup.add DerivedUnit.new(:name => "J", :expression => 'kg*m^2/s^2')
|
34
|
+
Lookup.add DerivedUnit.new(:name => "kWh", :expression => '3.6e6*J')
|
35
|
+
|
36
|
+
Lookup.add_property Property.new(:name => :energy, :expression => 'kWh')
|
37
|
+
|
38
|
+
#AREA
|
39
|
+
|
40
|
+
Lookup.add DerivedUnit.new(:name => "hectare", :expression => '10000*m^2')
|
41
|
+
Lookup.add DerivedUnit.new(:name => "acre", :expression => '4046.85642*m^2')
|
42
|
+
|
43
|
+
Lookup.add_property Property.new(:name => :area, :expression => 'hectare')
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class DerivedUnit
|
4
|
+
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include Dimension::Vector
|
7
|
+
include Conversion
|
8
|
+
|
9
|
+
attr_accessor :name, :numerator, :denominator, :value
|
10
|
+
|
11
|
+
alias :to_s :name
|
12
|
+
validates_presence_of :name
|
13
|
+
validates_format_of :name, :with => /\A[^\s*\/\^]+\Z/, :message => "cannot contain *, / or ^"
|
14
|
+
|
15
|
+
def initialize(attributes = {})
|
16
|
+
attributes.each do |name, value|
|
17
|
+
send("#{name}=", value)
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class Property
|
4
|
+
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include Dimension::Vector
|
7
|
+
include Conversion
|
8
|
+
|
9
|
+
attr_accessor :name, :numerator, :denominator, :value
|
10
|
+
|
11
|
+
validates_presence_of :name
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
attributes.each do |name, value|
|
15
|
+
send("#{name}=", value)
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Unity
|
3
|
+
class SimpleUnit
|
4
|
+
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include Dimension::Integer
|
7
|
+
|
8
|
+
attr_accessor :dimension, :name, :si_factor
|
9
|
+
alias :to_s :name
|
10
|
+
|
11
|
+
validates_inclusion_of :dimension, :in => Dimension::LIST
|
12
|
+
validates_presence_of :si_factor, :dimension, :name
|
13
|
+
validates_format_of :name, :with => /\A[^\s*\/\^]+\Z/, :message => "cannot contain *, / or ^"
|
14
|
+
|
15
|
+
def dimension_vector
|
16
|
+
@dimension_vector ||= Dimension::LIST.map{|d| d == dimension ? 1 : 0 }
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(attributes = {})
|
20
|
+
attributes.each do |name, value|
|
21
|
+
send("#{name}=", value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|