units-system 0.0.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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +120 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/units-system.rb +634 -0
- data/test/helper.rb +10 -0
- data/test/test_units-system.rb +71 -0
- data/units-system.gemspec +55 -0
- metadata +75 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Javier Goizueta
|
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.rdoc
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
= Ruby Units-System
|
2
|
+
|
3
|
+
Units of measure conversions for Ruby 1.9, using Ruby objects and Ruby syntax rather than text strings.
|
4
|
+
|
5
|
+
There are a number of Ruby units libraries, but I don't think they take this approach (I haven't
|
6
|
+
done much research, though.)
|
7
|
+
|
8
|
+
Currently Ruby 1.9 is required because we use the feature that
|
9
|
+
a +module.clas_eval{...}+ block can reference module's contants without qualification.
|
10
|
+
To make this apt for Ruby 1.8 we should replace expressions such as +u{W}+ by either
|
11
|
+
+u{W()}+, +u{self.W}+ (method invocation) or +u{self::W}+ (qualified constant).
|
12
|
+
|
13
|
+
Also, note that UTF-8 characters are liberally used in identifiers.
|
14
|
+
|
15
|
+
= Usage examples
|
16
|
+
|
17
|
+
To work with units a +units+ block can be used. Beware: in it +self+ is changed,
|
18
|
+
so outer self methods or instance variables are not accessible, unless usigned to
|
19
|
+
local variables.
|
20
|
+
|
21
|
+
require 'units-system'
|
22
|
+
|
23
|
+
Units.units do
|
24
|
+
|
25
|
+
# In the units environment predefined variables are available for all units and they
|
26
|
+
# can b combined arithmetically:
|
27
|
+
x = 3*m/s
|
28
|
+
puts x # => 3.0*m/s
|
29
|
+
# Note that SI prefixes (k for kilo, etc.) can be used as part of the unit names:
|
30
|
+
x += 17*km/h
|
31
|
+
puts x # => 7.72222222222222*m/s
|
32
|
+
puts x.to(km/h) # => 27.8*km/h
|
33
|
+
puts x.magnitude # => 7.72222222222222
|
34
|
+
|
35
|
+
# Let's use some unit powers: convert 3 cubic meters to litres:
|
36
|
+
puts (3*m**3).to(l) # => 3000.0*l
|
37
|
+
|
38
|
+
# Now let's convert som imperial units to SI:
|
39
|
+
puts (100*mi/h).to_si # => 44.704*m/s
|
40
|
+
|
41
|
+
# Note that in is a Ruby keyword, so to use inches you must use self.in:
|
42
|
+
puts (10*cm).to(self.in) # => 3.93700787401575*in
|
43
|
+
# ...or use tha alternative nonstandard name +inch+
|
44
|
+
puts (10*cm).to(self.inch) # => 3.93700787401575*inch
|
45
|
+
|
46
|
+
# Now let's use derived units, e.g. power units:
|
47
|
+
x = 10*kW
|
48
|
+
# show a verbose description of the measure:
|
49
|
+
puts x.describe # => 10.0 kiloWatt
|
50
|
+
# convert to base units
|
51
|
+
puts x.base # => 10000.0*(m**2*kg)/s**3
|
52
|
+
# a more natural notation can be used instead of the default Ruby syntax:
|
53
|
+
puts x.base.abr # => 10000.0 (m^2 kg)/s^3
|
54
|
+
|
55
|
+
# Note that unit names that start with uppercase letters are OK:
|
56
|
+
puts 11*W # => 11.0*W
|
57
|
+
puts (2*Mg).to(kg) # => 2000.0*kg
|
58
|
+
|
59
|
+
# Let's use kilograms-force (kiloponds) (not a SI unit)
|
60
|
+
x = 10*kgf
|
61
|
+
puts x # => 10.0*kgf
|
62
|
+
# conversion to SI units uses the SI unit of force the newton N (which is a derived unit)
|
63
|
+
puts x.to_si # => 98.0665*N
|
64
|
+
# conversion to base units substitutes derived units for base units
|
65
|
+
puts x.base # => 98066.5*(g*m)/s**2
|
66
|
+
# but g (gram) is not a base SI unit, to get SI base units we must:
|
67
|
+
puts x.base.to_si # => 98.0665*(kg*m)/s**2
|
68
|
+
|
69
|
+
# And now, for some trigonometry fun! (note the use of unicode characters)
|
70
|
+
x = 90*°
|
71
|
+
puts x # => 90.0*°
|
72
|
+
puts x.to(rad) # => 1.5707963267949*rad
|
73
|
+
puts sin(x) # => 1.0
|
74
|
+
|
75
|
+
puts sin(45*°+30*′+10*″) # => 0.713284429355996
|
76
|
+
|
77
|
+
puts asin(0.5) # => 0.523598775598299*rad
|
78
|
+
puts asin(0.5).to(°) # => 30.0*°
|
79
|
+
puts asin(0.5).in(°) # => 30.0
|
80
|
+
|
81
|
+
puts atan2(10*cm, 0.1*m).to(°) # => 45.0*°
|
82
|
+
|
83
|
+
# Temperature conversions may be absolute (convert levels of temperature)
|
84
|
+
# or relative (convert differences of temperature)
|
85
|
+
# When a measure has a single unit of temperature, conversion is absolute:
|
86
|
+
puts (20*°C).to(K) # => 293.15*K
|
87
|
+
puts (20*°C).to(°F) # => 67.9999999999999*°F
|
88
|
+
puts (20*mK).to(°C) # => -273.13*°C
|
89
|
+
# In other cases conversion is relative:
|
90
|
+
puts (2*°C/h).to(K/h) # => 2.0*K/h
|
91
|
+
puts (2*°C/h).to(°F/h) # => 3.6*°F/h
|
92
|
+
# To force the relative conversion of a single temperature pass a second argument to to():
|
93
|
+
puts (20*°C).to(K,:relative) # => 20.0*K
|
94
|
+
puts (20*°C).to(°F,:relative) # => 36.0*°F
|
95
|
+
puts (20*mK).to(°C,:relative) # => 0.02*°C
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
For short expressions, the abbreviation +Units.u+ can be used instead of +Units.units+
|
100
|
+
|
101
|
+
include Units
|
102
|
+
puts u{60*km + 10*mi} # => 76.09344*km
|
103
|
+
puts u{sin(45*°)} # => 0.707106781186547
|
104
|
+
x = u{120*km/h}
|
105
|
+
puts x.to(u{mi/h}) # => 74.5645430684801*mi/h
|
106
|
+
|
107
|
+
|
108
|
+
== Note on Patches/Pull Requests
|
109
|
+
|
110
|
+
* Fork the project.
|
111
|
+
* Make your feature addition or bug fix.
|
112
|
+
* Add tests for it. This is important so I don't break it in a
|
113
|
+
future version unintentionally.
|
114
|
+
* Commit, do not mess with rakefile, version, or history.
|
115
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
116
|
+
* Send me a pull request. Bonus points for topic branches.
|
117
|
+
|
118
|
+
== Copyright
|
119
|
+
|
120
|
+
Copyright (c) 2009 Javier Goizueta. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "units-system"
|
8
|
+
gem.summary = %Q{Arithmetic with units of measure}
|
9
|
+
gem.description = %Q{Experimental unit conversion & arithmetic for Ruby 1.9}
|
10
|
+
gem.required_ruby_version = '>= 1.9.1'
|
11
|
+
gem.email = "jgoizueta@gmail.com"
|
12
|
+
gem.homepage = "http://github.com/jgoizueta/units-system"
|
13
|
+
gem.authors = ["Javier Goizueta"]
|
14
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "units-system #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/units-system.rb
ADDED
@@ -0,0 +1,634 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Ruby 1.9 Units-System experiments.
|
4
|
+
|
5
|
+
module Units
|
6
|
+
|
7
|
+
class Measure
|
8
|
+
|
9
|
+
def initialize(mag=1.0, units={})
|
10
|
+
@magnitude = mag # rename to value?
|
11
|
+
case units
|
12
|
+
when Symbol
|
13
|
+
uinfo = Units.unit(units)
|
14
|
+
if uinfo
|
15
|
+
if uinfo.dim
|
16
|
+
@units = {uinfo.dim=>[units,1]}
|
17
|
+
else
|
18
|
+
@magnitude *= uinfo.factor
|
19
|
+
@units = {}
|
20
|
+
end
|
21
|
+
else
|
22
|
+
raise ArgumentError,"Invalid symbol for Measure definition: #{units.inspect} "
|
23
|
+
end
|
24
|
+
when Measure
|
25
|
+
@magnitude *= units.magnitude
|
26
|
+
@units = units.units
|
27
|
+
else
|
28
|
+
@units = units
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :magnitude, :units
|
33
|
+
|
34
|
+
# represent in text using Ruby notation
|
35
|
+
def to_s
|
36
|
+
return @magnitude.to_s if magnitude?
|
37
|
+
u_descr = Units.units_descr(@units)
|
38
|
+
"#{@magnitude}*#{u_descr}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# more verbose description (not grammatically perfect)
|
42
|
+
def describe
|
43
|
+
return @magnitude.to_s if magnitude?
|
44
|
+
u_descr = Units.units_descr(@units, true)
|
45
|
+
"#{@magnitude} #{u_descr}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# more natural concise text representation
|
49
|
+
def abr
|
50
|
+
self.to_s.gsub('**','^').tr('*',' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
# decompose compound units
|
54
|
+
def detailed_units(all_levels = false)
|
55
|
+
mag = @magnitude
|
56
|
+
prev_units = self.units
|
57
|
+
units = {}
|
58
|
+
loop do
|
59
|
+
compound = false
|
60
|
+
prev_units.each_pair do |dim, (unit,mul)|
|
61
|
+
ud = Units.unit(unit)
|
62
|
+
if ud.decomposition
|
63
|
+
compound = true
|
64
|
+
mag *= ud.decomposition.magnitude
|
65
|
+
ud.decomposition.units.each_pair do |d, (u,m)|
|
66
|
+
mag *= self.class.combine(units, d, u, m)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
mag *= self.class.combine(units, dim, unit, mul)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
if all_levels && compound
|
73
|
+
prev_units = units
|
74
|
+
units = {}
|
75
|
+
else
|
76
|
+
break
|
77
|
+
end
|
78
|
+
end
|
79
|
+
Measure.new mag, units end
|
80
|
+
|
81
|
+
# decompose to base units
|
82
|
+
def base
|
83
|
+
detailed_units true
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.combine(units, dim, unit, mult)
|
87
|
+
factor = 1
|
88
|
+
if units[dim]
|
89
|
+
u,m = units[dim]
|
90
|
+
if u!=unit
|
91
|
+
factor *= Units.conversion_factor(unit, u)**mult
|
92
|
+
end
|
93
|
+
units[dim] = [u, m+mult]
|
94
|
+
else
|
95
|
+
units[dim] = [unit, mult]
|
96
|
+
end
|
97
|
+
factor
|
98
|
+
end
|
99
|
+
|
100
|
+
def /(other)
|
101
|
+
self * (other.kind_of?(Numeric) ? 1.0/other : other.inverse)
|
102
|
+
end
|
103
|
+
|
104
|
+
def inspect
|
105
|
+
"Measure(#{@magnitude.inspect}, #{@units.inspect})"
|
106
|
+
end
|
107
|
+
|
108
|
+
def *(other)
|
109
|
+
case other
|
110
|
+
when Numeric
|
111
|
+
mag = self.magnitude*other
|
112
|
+
units = self.units
|
113
|
+
else
|
114
|
+
mag = self.magnitude*other.magnitude
|
115
|
+
units = {}
|
116
|
+
(self.units.keys | other.units.keys).each do |dim|
|
117
|
+
other_u = other.units[dim] || [nil,0]
|
118
|
+
this_u = self.units[dim] || [other_u.first,0]
|
119
|
+
# mag *= Units.conversion_factor(this_u.first, other_u.first) if other_u.first
|
120
|
+
mult = this_u.last+other_u.last
|
121
|
+
mag *= Units.conversion_factor(other_u.first, this_u.first)**(other_u.last) if other_u.first
|
122
|
+
units[dim] = [this_u.first, mult]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
units.reject!{|dim,(u,m)| m==0}
|
126
|
+
Measure.new(mag, units)
|
127
|
+
end
|
128
|
+
|
129
|
+
def +(other)
|
130
|
+
Measure.new self.magnitude+other.to(self.units).magnitude, self.units
|
131
|
+
end
|
132
|
+
|
133
|
+
def -(other)
|
134
|
+
self + (-other)
|
135
|
+
end
|
136
|
+
|
137
|
+
def **(n)
|
138
|
+
raise ArgumentError,"Only integer powers are allowed for magnitudes" unless n.kind_of?(Integer)
|
139
|
+
units = @units.dup
|
140
|
+
units.each_pair do |dim, (u, m)|
|
141
|
+
units[dim] = [u, m*n]
|
142
|
+
end
|
143
|
+
Measure.new @magnitude, units
|
144
|
+
end
|
145
|
+
|
146
|
+
def inverse
|
147
|
+
#Measure.new(1.0/@magnitude, @units.map_hash{|unit,mult| [unit, -mult]})
|
148
|
+
units = {}
|
149
|
+
@units.each_pair do |dim, (unit, mult)|
|
150
|
+
units[dim] = [unit, -mult]
|
151
|
+
end
|
152
|
+
Measure.new(1.0/@magnitude, units)
|
153
|
+
end
|
154
|
+
|
155
|
+
def -@
|
156
|
+
Measure.new(-@magnitude, units)
|
157
|
+
end
|
158
|
+
|
159
|
+
def in(other, mode=:absolute)
|
160
|
+
other = Measure.new(1.0, other) unless other.kind_of?(Measure)
|
161
|
+
other = other.base
|
162
|
+
this = self.base
|
163
|
+
dims = this.units.keys | other.units.keys
|
164
|
+
mag = this.magnitude/other.magnitude
|
165
|
+
dims.each do |dim|
|
166
|
+
if !this.units[dim] || !other.units[dim] ||
|
167
|
+
(this.units[dim].last != other.units[dim].last)
|
168
|
+
raise "Inconsistent units #{Units.units_descr(this.units)} #{Units.units_descr(other.units)}"
|
169
|
+
end
|
170
|
+
this_u, mult = this.units[dim]
|
171
|
+
other_u = other.units[dim].first
|
172
|
+
mag *= Units.conversion_factor(this_u, other_u)**mult
|
173
|
+
end
|
174
|
+
if mode!=:relative && dims.size==1 && this.units[dims.first].last==1
|
175
|
+
# consider "level" conversion for biased units (otherwise consider interval or difference values)
|
176
|
+
mag += Units.conversion_bias(this.units[dims.first].first, other.units[dims.first].first)
|
177
|
+
end
|
178
|
+
mag
|
179
|
+
end
|
180
|
+
|
181
|
+
def to(units, mode=:absolute)
|
182
|
+
units = units.u if units.kind_of?(Measure)
|
183
|
+
Measure.new self.in(units, mode), units
|
184
|
+
end
|
185
|
+
|
186
|
+
def si_units
|
187
|
+
units = {}
|
188
|
+
@units.each_pair do |dim, (unit, mult)|
|
189
|
+
si_unit = SI_UNITS[dim]
|
190
|
+
if si_unit.kind_of?(Measure)
|
191
|
+
si_unit.units.each_pair do |d, (u,m)|
|
192
|
+
self.class.combine(units, d, u, m*mult)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
self.class.combine(units, dim, si_unit, mult)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
units
|
199
|
+
end
|
200
|
+
|
201
|
+
def to_si
|
202
|
+
to(si_units)
|
203
|
+
end
|
204
|
+
|
205
|
+
def coerce(other)
|
206
|
+
[Measure.new(other, {}), self]
|
207
|
+
end
|
208
|
+
|
209
|
+
def u # dimension? unit? only_units? strip_units? units_measure?
|
210
|
+
Measure.new(1.0, self.units)
|
211
|
+
end
|
212
|
+
|
213
|
+
# dimension (quantity)
|
214
|
+
def dimension
|
215
|
+
q = nil
|
216
|
+
u = self.base.si_units
|
217
|
+
SI_UNITS.each_pair do |dim, unit|
|
218
|
+
unit = Measure.new(1.0, unit) unless unit.kind_of?(Measure)
|
219
|
+
if unit.base.si_units == u
|
220
|
+
q = dim
|
221
|
+
break
|
222
|
+
end
|
223
|
+
end
|
224
|
+
q
|
225
|
+
end
|
226
|
+
|
227
|
+
def dimensionless?
|
228
|
+
base.units.reject{|d,(u,m)| m==0}.empty?
|
229
|
+
end
|
230
|
+
|
231
|
+
# less strict dimensionless condition (e.g. an angle is not a pure magnitude in this sense)
|
232
|
+
def magnitude?
|
233
|
+
self.units.reject{|d,(u,m)| m==0}.empty?
|
234
|
+
end
|
235
|
+
|
236
|
+
end # Measure
|
237
|
+
|
238
|
+
def Measure(*args)
|
239
|
+
if args.size==1
|
240
|
+
case args.first
|
241
|
+
when Numeric
|
242
|
+
m = args.first
|
243
|
+
u = {}
|
244
|
+
else
|
245
|
+
m = 1.0
|
246
|
+
u = args.first
|
247
|
+
end
|
248
|
+
args = [m,u]
|
249
|
+
end
|
250
|
+
Units::Measure.new(*args)
|
251
|
+
end
|
252
|
+
module_function :Measure
|
253
|
+
|
254
|
+
PREFIXES = {
|
255
|
+
:y=>[1E-24, 'yocto'],
|
256
|
+
:z=>[1E-21, 'zepto'],
|
257
|
+
:a=>[1E-18, 'atto'],
|
258
|
+
:f=>[1E-15, 'femto'],
|
259
|
+
:p=>[1E-12, 'pico'],
|
260
|
+
:n=>[1E-09, 'nano'],
|
261
|
+
:µ=>[1E-06, 'micro'], # ASCII alternative: u; define it?
|
262
|
+
:m=>[1E-03, 'milli'],
|
263
|
+
:c=>[1E-02, 'centi'],
|
264
|
+
:d=>[1E-01, 'deci'],
|
265
|
+
:da=>[1E1, 'deca'],
|
266
|
+
:h=>[1E02, 'hecto'],
|
267
|
+
:k=>[1E03, 'kilo'],
|
268
|
+
:M=>[1E06, 'mega'],
|
269
|
+
:G=>[1E09, 'giga'],
|
270
|
+
:T=>[1E12, 'tera'],
|
271
|
+
:P=>[1E15, 'peta'],
|
272
|
+
:E=>[1E18, 'exa'],
|
273
|
+
:Z=>[1E21, 'zetta'],
|
274
|
+
:Y=>[1E24, 'yotta']
|
275
|
+
}
|
276
|
+
|
277
|
+
def self.prefix_factor(prefix)
|
278
|
+
pd = PREFIXES[prefix.to_sym]
|
279
|
+
pd && pd.first
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.prefix_name(prefix)
|
283
|
+
pd = PREFIXES[prefix.to_sym]
|
284
|
+
pd && pd.last
|
285
|
+
end
|
286
|
+
|
287
|
+
def self.prefix_factor_and_name(prefix)
|
288
|
+
PREFIXES[prefix]
|
289
|
+
end
|
290
|
+
|
291
|
+
# mathematical functions available to unit blocks are defined here
|
292
|
+
module Math
|
293
|
+
end
|
294
|
+
|
295
|
+
# unit methods and constants are defined here to be injected in units blocks
|
296
|
+
module System
|
297
|
+
|
298
|
+
extend Math
|
299
|
+
|
300
|
+
def self.define(unit)
|
301
|
+
define_var unit
|
302
|
+
PREFIXES.each do |prefix, (factor, name)|
|
303
|
+
define_var "#{prefix}#{unit}".to_sym
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class <<self
|
308
|
+
private
|
309
|
+
def define_var(name)
|
310
|
+
name_initial = name.to_s[0,1]
|
311
|
+
if name_initial==name_initial.upcase && name_initial!=name_initial.downcase
|
312
|
+
# we could define a method with the requested name, but it would
|
313
|
+
# no be usable without qualification (System.X) or brackets (X())
|
314
|
+
# so we define a constant. But this requires Ruby 1.9 to be useful;
|
315
|
+
# otherwise the constant is not accesible without qualification in units blocks.
|
316
|
+
System.const_set name, Units.Measure(name)
|
317
|
+
end
|
318
|
+
self.class_eval do
|
319
|
+
define_method name do
|
320
|
+
Units.Measure(name)
|
321
|
+
end
|
322
|
+
module_function name
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end # Units::System
|
328
|
+
|
329
|
+
def units(&blk)
|
330
|
+
Units::System.class_eval(&blk)
|
331
|
+
end
|
332
|
+
alias :u :units
|
333
|
+
module_function :units, :u
|
334
|
+
|
335
|
+
UnitDefinition = Struct.new(:dim, :factor, :name, :decomposition, :bias)
|
336
|
+
|
337
|
+
UNITS = {} # Hash.new{|h,k| h[k]=UnitDefinition.new()}
|
338
|
+
|
339
|
+
# get unit definition
|
340
|
+
def self.unit(unit_symbol)
|
341
|
+
ud = UNITS[unit_symbol]
|
342
|
+
if ud.nil?
|
343
|
+
factor = 1.0
|
344
|
+
if factor_name = PREFIXES[unit_symbol]
|
345
|
+
ud = UnitDefinition.new(nil, *factor_name)
|
346
|
+
else
|
347
|
+
u = unit_symbol.to_s
|
348
|
+
PREFIXES.each_pair do |prefix, (f,name)|
|
349
|
+
prefix = prefix.to_s
|
350
|
+
if u[0...prefix.length] == prefix
|
351
|
+
factor = f
|
352
|
+
ud = UNITS[u[prefix.length..-1].to_sym].dup
|
353
|
+
if ud
|
354
|
+
ud.name = "#{name}#{ud.name}"
|
355
|
+
break
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
ud.factor *= factor if ud
|
361
|
+
ud.decomposition *= factor if ud && ud.decomposition
|
362
|
+
end
|
363
|
+
raise ArgumentError,"Invalid Units #{unit_symbol}" unless ud
|
364
|
+
ud
|
365
|
+
end
|
366
|
+
|
367
|
+
# Define new units.
|
368
|
+
# Define a base unit (with a factor for conversion to SI units)
|
369
|
+
# Units.define :unit_symbol, 'unit-name', :quantity, si_units_per_this_unit
|
370
|
+
# Define a unit in terms or another (valid for base or derived units)
|
371
|
+
# Units.define :unit_symbol, 'unit-name', value, :in_units
|
372
|
+
# Define a base unit as a measure-expression
|
373
|
+
# Units.define :unit_symbol, 'unit_name', u{...}
|
374
|
+
# Define a derived unit as a measure-expression
|
375
|
+
# Units.define :unit_symbol, 'unit_name', :quantity, u{...}
|
376
|
+
# For base dimensions the SI unit for a quantity must also be stablished with Unit.si_units;
|
377
|
+
# for derived units, SI units are automatically taken to be the first define unit of the quantity
|
378
|
+
# with unitary value in SI base units.
|
379
|
+
def self.define(unit_symbol, name, *args)
|
380
|
+
eqhivalence = nil
|
381
|
+
si_unit = false
|
382
|
+
bias = nil
|
383
|
+
if args.first.kind_of?(Symbol)
|
384
|
+
dim = args.shift
|
385
|
+
if args.first.kind_of?(Numeric)
|
386
|
+
# unidad simple
|
387
|
+
factor = args.shift
|
388
|
+
factor_units = args.shift
|
389
|
+
if factor_units
|
390
|
+
ud = unit(factor_units)
|
391
|
+
if ud.dim != dim
|
392
|
+
raise ArgumentError, "Inconsistent units #{factor_units} in definition of #{unit_symbol}"
|
393
|
+
end
|
394
|
+
# maybe it was not simple after all...
|
395
|
+
equivalence = factor*ud.decomposition if ud.decomposition
|
396
|
+
factor *= ud.factor
|
397
|
+
end
|
398
|
+
else
|
399
|
+
# unidad compuesta
|
400
|
+
equivalence = args.shift
|
401
|
+
factor = equivalence.to_si.magnitude
|
402
|
+
si_unit = (factor==1.0) # TODO: tolerance?
|
403
|
+
end
|
404
|
+
elsif args.first.kind_of?(Numeric)
|
405
|
+
# unidad definida en función de otra
|
406
|
+
factor = args.shift
|
407
|
+
factor_units = args.shift
|
408
|
+
u = unit(factor_units)
|
409
|
+
dim = u.dim
|
410
|
+
factor *= u.factor
|
411
|
+
equivalence = factor*u.decomposition if u.decomposition
|
412
|
+
bias = args.shift
|
413
|
+
else
|
414
|
+
# unidad simple definida en función de una expressión
|
415
|
+
definition = args.shift
|
416
|
+
if definition.units.keys.size!=1
|
417
|
+
raise ArgumentError,"To define a compound unit a dimension must be specified"
|
418
|
+
end
|
419
|
+
dim = definition.units.keys.first
|
420
|
+
factor = definition.to_si.magnitude
|
421
|
+
end
|
422
|
+
unit_def = UnitDefinition.new(dim, factor, name, equivalence, bias)
|
423
|
+
if UNITS.has_key?(unit_symbol)
|
424
|
+
raise "Se ha intentando redefinir #{unit_symbol} (previamente definido como #{UNITS[unit_symbol]}) como #{unit_def}"
|
425
|
+
end
|
426
|
+
UNITS[unit_symbol] = unit_def
|
427
|
+
System.define unit_symbol
|
428
|
+
Units.si_units unit_def.dim, unit_symbol if si_unit && !SI_UNITS.has_key?(unit_def.dim)
|
429
|
+
end
|
430
|
+
|
431
|
+
SI_UNITS = {}
|
432
|
+
|
433
|
+
def self.si_units(dim, unit)
|
434
|
+
SI_UNITS[dim] = unit
|
435
|
+
end
|
436
|
+
|
437
|
+
def self.dimension(u)
|
438
|
+
unit(u).dim
|
439
|
+
end
|
440
|
+
|
441
|
+
def self.conversion_factor(from, to)
|
442
|
+
from_u = unit(from)
|
443
|
+
to_u = unit(to)
|
444
|
+
raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
|
445
|
+
from_u.factor/to_u.factor
|
446
|
+
end
|
447
|
+
|
448
|
+
def self.conversion_bias(from, to)
|
449
|
+
from_u = unit(from)
|
450
|
+
to_u = unit(to)
|
451
|
+
raise ArgumentError,"Inconsistent Units (#{from}, #{to})" if from_u.dim!=to_u.dim
|
452
|
+
factor = from_u.factor/to_u.factor
|
453
|
+
(from_u.bias||0)*factor - (to_u.bias||0)
|
454
|
+
end
|
455
|
+
|
456
|
+
# simple unit name
|
457
|
+
def self.unit_name(u)
|
458
|
+
uinfo = Units.unit(u)
|
459
|
+
uinfo && uinfo.name
|
460
|
+
end
|
461
|
+
|
462
|
+
def self.unit_descr(u, long=false, mult=1)
|
463
|
+
if long
|
464
|
+
u = unit_name(u)
|
465
|
+
if mult!=1
|
466
|
+
case mult
|
467
|
+
when 2
|
468
|
+
"squared #{u}"
|
469
|
+
when 3
|
470
|
+
"cubed #{u}"
|
471
|
+
else
|
472
|
+
"#{u} to the #{mult} power"
|
473
|
+
end
|
474
|
+
else
|
475
|
+
u
|
476
|
+
end
|
477
|
+
else
|
478
|
+
if mult!=1
|
479
|
+
"#{u}**#{mult}"
|
480
|
+
else
|
481
|
+
u.to_s
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def self.units_descr(units, long=false)
|
487
|
+
units = units.values.sort_by{|u,m| -m}
|
488
|
+
pos_units = units.select{|u| u.last>0}
|
489
|
+
neg_units = units.select{|u| u.last<0}
|
490
|
+
times = long ? " " : "*"
|
491
|
+
num = pos_units.map{|u,m| unit_descr(u,long,m)}.join(times)
|
492
|
+
num = "(#{num})" if pos_units.size>1 && !neg_units.empty? && !long
|
493
|
+
den = neg_units.map{|u,m| unit_descr(u,long,-m)}.join("*")
|
494
|
+
den = "(#{den})" if neg_units.size>1 && !long
|
495
|
+
if pos_units.empty?
|
496
|
+
u_descr = "1/#{den}"
|
497
|
+
elsif neg_units.empty?
|
498
|
+
u_descr = num
|
499
|
+
else
|
500
|
+
connector = long ? " per " : "/"
|
501
|
+
u_descr = "#{num}#{connector}#{den}"
|
502
|
+
end
|
503
|
+
u_descr
|
504
|
+
end
|
505
|
+
|
506
|
+
module Math
|
507
|
+
|
508
|
+
[:sin, :cos, :tan].each do |fun|
|
509
|
+
define_method fun do |x|
|
510
|
+
x = Units.u{x.in(rad)} if x.kind_of?(Measure)
|
511
|
+
::Math.send(fun, x)
|
512
|
+
end
|
513
|
+
module_function fun
|
514
|
+
end
|
515
|
+
|
516
|
+
[:asin, :acos, :atan].each do |fun|
|
517
|
+
define_method fun do |x|
|
518
|
+
if x.kind_of?(Measure)
|
519
|
+
raise ArgumentError,"Invalid dimensions for #{fun} argument" unless x.dimensionless?
|
520
|
+
x = x.magnitude
|
521
|
+
end
|
522
|
+
Units.u{::Math.send(fun, x)*rad}
|
523
|
+
end
|
524
|
+
module_function fun
|
525
|
+
end
|
526
|
+
|
527
|
+
module_function
|
528
|
+
def atan2(x,y)
|
529
|
+
if x.kind_of?(Measure)
|
530
|
+
if y.kind_of?(Measure)
|
531
|
+
if x.base.to_si.units != y.base.to_si.units
|
532
|
+
raise ArgumentError,"Inconsistent units for atan2 arguments #{x.u}, #{y.u}"
|
533
|
+
end
|
534
|
+
# or x = x.to_si.magnitude, y=y.to_si.magnitude
|
535
|
+
y = y.in(x.units)
|
536
|
+
x = x.magnitude
|
537
|
+
else
|
538
|
+
raise ArgumentError,"Invalid dimensions for atan2 argument #{x.u}" unless x.dimensionless?
|
539
|
+
x = x.magnitude
|
540
|
+
end
|
541
|
+
elsif y.kind_of?(Measure)
|
542
|
+
raise ArgumentError,"Invalid dimensions for atan2 argument #{y.u}" unless y.dimensionless?
|
543
|
+
y = y.magnitude
|
544
|
+
end
|
545
|
+
Units.u{::Math.atan2(x,y)*rad}
|
546
|
+
end
|
547
|
+
|
548
|
+
end
|
549
|
+
|
550
|
+
# Units definitions
|
551
|
+
|
552
|
+
# declare SI base units
|
553
|
+
si_units :mass, :kg # m
|
554
|
+
si_units :length, :m # l
|
555
|
+
si_units :time, :s # t
|
556
|
+
si_units :electric_current, :A # I
|
557
|
+
si_units :temperature, :K # T
|
558
|
+
si_units :luminous_intensity, :cd # Iv
|
559
|
+
si_units :amount_of_substance, :mol # n
|
560
|
+
|
561
|
+
# base units definitions
|
562
|
+
define :g, 'gram', :mass, 1E-3
|
563
|
+
define :m, 'meter', :length, 1.0
|
564
|
+
define :s, 'second', :time, 1.0
|
565
|
+
define :A, 'ampere', :electric_current, 1.0
|
566
|
+
define :K, 'kelvin', :temperature, 1.0
|
567
|
+
define :cd,'candela',:luminous_intensity, 1.0
|
568
|
+
define :mol,'mole', :amount_of_substance, 1.0
|
569
|
+
|
570
|
+
define :min, 'minute', 60, :s
|
571
|
+
define :h, 'hour', 60, :min
|
572
|
+
define :d, 'day', 24, :h
|
573
|
+
define :mi, 'mile', 1.609344, :km
|
574
|
+
define :in, 'inch', 2.54, :cm
|
575
|
+
define :ft, 'foot', 0.3048, :m
|
576
|
+
# in is a Ruby keyword; to avoid having to use self.in we'll define:
|
577
|
+
define :inch, 'inch', 1, :in
|
578
|
+
|
579
|
+
# declare derived quantities without named units
|
580
|
+
si_units :speed, u{m/s}
|
581
|
+
si_units :acceleration, u{m/s**2}
|
582
|
+
si_units :area, u{m**2}
|
583
|
+
si_units :volume, u{m**3}
|
584
|
+
|
585
|
+
# named derived units
|
586
|
+
define :W, 'Watt', :power, u{kg*m**2/s**3} # J/s
|
587
|
+
define :Hz, 'herz', :frequency, u{1/s}
|
588
|
+
define :N, 'newton', :force, u{m*kg/s**2}
|
589
|
+
define :Pa, 'pascal', :pressure, u{N/m**2}
|
590
|
+
define :J, 'joule', :energy, u{N*m}
|
591
|
+
define :C, 'coulomb', :electric_charge, u{s*A}
|
592
|
+
define :V, 'volt', :voltage, u{W/A}
|
593
|
+
define :F, 'farad', :electric_capacitance, u{C/V}
|
594
|
+
define :Ω, 'ohm', :electric_resistance, u{V/A} # ohm: Ω omega: Ω
|
595
|
+
define :S, 'siemens', :electric_condctance, u{1/Ω}
|
596
|
+
define :Wb,'weber', :magnetic_flux, u{J/A}
|
597
|
+
define :T, 'tesla', :magnetic_field, u{N/(A*m)}
|
598
|
+
define :H, 'henry', :inductance, u{Wb/A}
|
599
|
+
define :rad, 'radian', :angle, u{m/m} # maybe more useful as base unit: define :rad, 'radian', :angle, 1.0
|
600
|
+
define :sr, 'steradian', :solid_angle, u{m**2/m**2}
|
601
|
+
define :lm, 'lumen', :luminous_flux, u{cd*sr}
|
602
|
+
define :lx, 'lux', :illuminance, u{lm/m**2}
|
603
|
+
define :Bq, 'bequerel', :radioactivity, u{1/s}
|
604
|
+
define :Gy, 'gray', :absorbed_dose, u{J/kg}
|
605
|
+
define :Sv, 'sievert', :equivalent_dose, u{J/kg}
|
606
|
+
define :kat, 'katal', :catalytic_activity, u{mol/s}
|
607
|
+
|
608
|
+
define :°C, 'degree Celsius', 1, :K, +273.15
|
609
|
+
define :°F, 'degree Fahrenheit', 5.0/9.0, :K, +459.67
|
610
|
+
define :R, 'rankine', 5.0/9.0, :K
|
611
|
+
|
612
|
+
define :l, 'litre', :volume, u{dm**3}
|
613
|
+
define :L, 'litre', 1, :l
|
614
|
+
|
615
|
+
define :°, 'degree', ::Math::PI/180.0, :rad
|
616
|
+
define :′, 'arc-minute', ::Math::PI/180.0/60.0, :rad
|
617
|
+
define :″, 'arc-second', ::Math::PI/180.0/3600.0, :rad
|
618
|
+
# not so cool, but easier to type alternatives:
|
619
|
+
define :deg, 'degree', 1, :°
|
620
|
+
define :arcmin, 'arc-minute', 1, :′
|
621
|
+
define :arcsec, 'arc-second', 1, :″
|
622
|
+
|
623
|
+
define :g0, 'standard gravity', :acceleration, u{9.80665*m/s**2}
|
624
|
+
|
625
|
+
define :bar, 'bar', :pressure, u{1E5*Pa}
|
626
|
+
define :atm, 'atmosphere', :pressure, u{101325.0*Pa}
|
627
|
+
define :mWC, 'meters of water column', :pressure, u{1E3*kg*g0/m**2}
|
628
|
+
define :Torr, 'torricelli', :pressure, u{atm/760}
|
629
|
+
define :mHg, 'mHg', :pressure, u{13.5951E3*kg*g0/m**2}
|
630
|
+
|
631
|
+
# define :kp, 'kilopond', :force, u{kg*g0} # or define pond?
|
632
|
+
define :gf, 'gram-force', :force, u{g*g0} # kilopond kp = kgf
|
633
|
+
|
634
|
+
end # Units
|
data/test/helper.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestUnitsSystem < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "be possible to define Measures with a units block" do
|
6
|
+
assert_equal Units::Measure, Units.units{m}.class
|
7
|
+
assert_equal 1.0, Units.units{m}.magnitude
|
8
|
+
assert_equal [:m, 1], Units.units{m}.units[:length]
|
9
|
+
end
|
10
|
+
|
11
|
+
should "be possible to define Measures with a shorthand u block" do
|
12
|
+
assert_equal Units::Measure, Units.u{m}.class
|
13
|
+
assert_equal 1.0, Units.u{m}.magnitude
|
14
|
+
assert_equal [:m, 1], Units.u{m}.units[:length]
|
15
|
+
end
|
16
|
+
|
17
|
+
should "determine units dimension correctly" do
|
18
|
+
assert_equal :length, Units.u{m}.dimension
|
19
|
+
assert_equal :length, Units.u{mi}.dimension
|
20
|
+
assert_equal :mass, Units.u{g}.dimension
|
21
|
+
assert_equal :time, Units.u{s}.dimension
|
22
|
+
assert_equal :electric_current, Units.u{A}.dimension
|
23
|
+
assert_equal :temperature, Units.u{K}.dimension
|
24
|
+
assert_equal :luminous_intensity, Units.u{cd}.dimension
|
25
|
+
assert_equal :amount_of_substance, Units.u{mol}.dimension
|
26
|
+
assert_equal :power, Units.u{W}.dimension
|
27
|
+
assert_equal :pressure, Units.u{Pa}.dimension
|
28
|
+
assert_equal :mass, Units.u{kg}.dimension
|
29
|
+
assert_equal :length, Units.u{km}.dimension
|
30
|
+
assert_equal :length, Units.u{mm}.dimension
|
31
|
+
assert_equal :length, Units.u{Mm}.dimension
|
32
|
+
end
|
33
|
+
|
34
|
+
should "allow unit arithmetic" do
|
35
|
+
assert_equal :speed, Units.u{m/s}.dimension
|
36
|
+
assert_equal :length, Units.u{m**2/m}.dimension
|
37
|
+
assert_equal :length, Units.u{(m*m)/m}.dimension
|
38
|
+
assert_equal :volume, Units.u{m*m**2}.dimension
|
39
|
+
assert_equal :force, Units.u{m*kg/s**2}.dimension
|
40
|
+
end
|
41
|
+
|
42
|
+
should "convert simple units correctly" do
|
43
|
+
assert_equal 1E5, Units.u{100*km.in(m)}
|
44
|
+
assert_equal 10, Units.u{10000*m.in(km)}
|
45
|
+
assert_equal 254, Units.u{100*self.in.in(cm)}
|
46
|
+
end
|
47
|
+
|
48
|
+
should "convert compound units correctly" do
|
49
|
+
assert_equal 75, Units.u{(270*km/h).in(m/s)}
|
50
|
+
assert_equal 270, Units.u{(75*m/s).in(km/h)}
|
51
|
+
end
|
52
|
+
|
53
|
+
should "add units correctly" do
|
54
|
+
assert_equal 5, Units.u{3*m+2*m}.magnitude
|
55
|
+
assert_equal 5, Units.u{3*kg+2*kg}.magnitude
|
56
|
+
end
|
57
|
+
|
58
|
+
should "add units correctly with implied conversions" do
|
59
|
+
assert_equal 5, Units.u{3*m+200*cm}.magnitude
|
60
|
+
assert_equal [:m,1],Units.u{3*m+200*cm}.units[:length]
|
61
|
+
assert_equal 85, Units.u{10*m/s + 270*km/h}.magnitude
|
62
|
+
end
|
63
|
+
|
64
|
+
should "choke on inconsistent units" do
|
65
|
+
assert_raise(RuntimeError){Units.units{m+kg}}
|
66
|
+
assert_raise(RuntimeError){Units.units{m/s+m/s**2}}
|
67
|
+
assert_raise(RuntimeError){Units.units{(m).to(kg)}}
|
68
|
+
assert_raise(RuntimeError){Units.units{(m/s).to(m/s**2)}}
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{units-system}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Javier Goizueta"]
|
12
|
+
s.date = %q{2009-12-18}
|
13
|
+
s.description = %q{Experimental unit conversion & arithmetic for Ruby 1.9}
|
14
|
+
s.email = %q{jgoizueta@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/units-system.rb",
|
27
|
+
"test/helper.rb",
|
28
|
+
"test/test_units-system.rb",
|
29
|
+
"units-system.gemspec"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/jgoizueta/units-system}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.required_ruby_version = Gem::Requirement.new(">= 1.9.1")
|
35
|
+
s.rubygems_version = %q{1.3.5}
|
36
|
+
s.summary = %q{Arithmetic with units of measure}
|
37
|
+
s.test_files = [
|
38
|
+
"test/helper.rb",
|
39
|
+
"test/test_units-system.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
50
|
+
end
|
51
|
+
else
|
52
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: units-system
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Javier Goizueta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-18 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thoughtbot-shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Experimental unit conversion & arithmetic for Ruby 1.9
|
26
|
+
email: jgoizueta@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- lib/units-system.rb
|
42
|
+
- test/helper.rb
|
43
|
+
- test/test_units-system.rb
|
44
|
+
- units-system.gemspec
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/jgoizueta/units-system
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 1.9.1
|
59
|
+
version:
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.5
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Arithmetic with units of measure
|
73
|
+
test_files:
|
74
|
+
- test/helper.rb
|
75
|
+
- test/test_units-system.rb
|