Ruby Units-System¶ ↑
Units of measure conversions for Ruby, using Ruby objects and Ruby syntax rather than text strings.
There are a number of Ruby units libraries, but I don’t think they take this approach (I haven’t done much research, though.)
There’s a couple of caveats for using this module from Ruby 1.8:
-
To use unit names that start with uppercase letters, the UseBlocks module must be included in the scope (either global scope or a module or class definitions) where the expressions are goign to be used. Alternatively, expressions such as
u{W}
could be replaced by eitheru{W()}
,u{self.W}
(method invocation) oru{self::W}
(qualified constant). Units defined in text form, such asu('W*h')
will work regardless of whether UseBlocks is included or not. -
UTF-8 characters are liberally used in identifiers, so the code must be executed with the
-Ku
option (or setting$KCODE='UTF8'
before requiring this library.)
Usage examples¶ ↑
Ruby 1.9¶ ↑
For use with Ruby 1.9, this gem can be used simply by requiring:
require 'units-system'
For versions other than 1.9.1 this is needed as in Ruby 1.8:
include Units::UseBlocks # allow access to capitalized unit names from units/u blocks
Ruby 1.8¶ ↑
This library has been designed for Ruby 1.9; when using it under older versions of Ruby there’s a couple of precautions to be taken to use it (which can be used with Ruby 1.9 too):
$KCODE = 'UTF8' # avoid errors when parsing the required library under Ruby 1.8 require 'units-system' include Units::UseBlocks # allow access to capitalized unit names from units/u blocks
Depending on you installation you may have to “require ‘rubygems’ first.
The following examples use UTF-8 code; so they can should be used with a “encoding: utf-8” comment at the top of the file for Ruby 1.9, and/or with the ruby command line “-Ku” option for Ruby 1.8.
To work with units a units
block can be used. Beware: in it self
is changed, so outer self methods or instance variables are not accessible, unless assigned to local variables.
require 'units-system' Units.units do # In the units environment predefined variables are available for all units and they # can be combined arithmetically: x = 3*m/s puts x # => 3.0*m/s # Note that SI prefixes (k for kilo, etc.) can be used as part of the unit names: x += 17*km/h puts x # => 7.72222222222222*m/s puts x.to(km/h) # => 27.8*km/h puts x.magnitude # => 7.72222222222222 # Let's use some unit powers: convert 3 cubic meters to litres: puts (3*m**3).to(l) # => 3000.0*l # Now let's convert some imperial units to SI: puts (100*mi/h).to_si # => 44.704*m/s # Note that +in+ is a Ruby keyword, so to use inches you must use +self.in+: puts (10*cm).to(self.in) # => 3.93700787401575*in # ...or use the alternative nonstandard name +inch+ puts (10*cm).to(inch) # => 3.93700787401575*inch # Now let's use derived units, e.g. power units: x = 10*kW # show a verbose description of the measure: puts x.describe # => 10.0 kiloWatt # convert to base units puts x.base # => 10000.0*(m**2*kg)/s**3 # a more natural notation can be used instead of the default Ruby syntax: puts x.base.abr # => 10000.0 (m^2 kg)/s^3 # Note that unit names that start with uppercase letters are OK: # (but see the notes on UseBlocks above if this doesn't work) puts 11*W # => 11.0*W puts (2*Mg).to(kg) # => 2000.0*kg # Let's use kilograms-force (kiloponds) (not a SI unit) x = 10*kgf puts x # => 10.0*kgf # conversion to SI units uses the SI unit of force the newton N (which is a derived unit) puts x.to_si # => 98.0665*N # conversion to base units substitutes derived units for base units puts x.base # => 98066.5*(g*m)/s**2 # but g (gram) is not a base SI unit, to get SI base units we must: puts x.base.to_si # => 98.0665*(kg*m)/s**2 # And now, for some trigonometry fun! (note the use of unicode characters) x = 90*° puts x # => 90.0*° puts x.to(rad) # => 1.5707963267949*rad puts sin(x) # => 1.0 puts sin(45*°+30*′+10*″) # => 0.713284429355996 puts asin(0.5) # => 0.523598775598299*rad puts asin(0.5).to(°) # => 30.0*° puts asin(0.5).in(°) # => 30.0 puts atan2(10*cm, 0.1*m).to(°) # => 45.0*° # Temperature conversions may be absolute (convert levels of temperature) # or relative (convert differences of temperature) # When a measure has a single unit of temperature, conversion is absolute: puts (20*°C).to(K) # => 293.15*K puts (20*°C).to(°F) # => 67.9999999999999*°F puts (20*mK).to(°C) # => -273.13*°C # In other cases conversion is relative: puts (2*°C/h).to(K/h) # => 2.0*K/h puts (2*°C/h).to(°F/h) # => 3.6*°F/h # To force the relative conversion of a single temperature pass a second argument to to(): puts (20*°C).to(K,:relative) # => 20.0*K puts (20*°C).to(°F,:relative) # => 36.0*°F puts (20*mK).to(°C,:relative) # => 0.02*°C end
For short expressions, the abbreviation Units.u
can be used instead of Units.units
include Units puts u{60*km + 10*mi} # => 76.09344*km puts u{sin(45*°)} # => 0.707106781186547 x = u{120*km/h} puts x.to(u{mi/h}) # => 74.5645430684801*mi/h
Text strings can also be used to define units:
puts Units.u('60*km + 10*mi') # => 76.09344*km puts Units.u('sin(45*°)') # => 0.707106781186547 x = Units.u('120*km/h') puts x.to(Units.u('mi/h')) # => 74.5645430684801*mi/h
And also as the right operand of binary arithmetic operators:
puts Units.u('20*km')/'h' # => 20.0*km/h
New units can be defined with Units.define
Units.define :kph, 1, Units.u{km/h} puts Units.u{270*kph.to(m/s)} # => 75.0*m/s
Constants¶ ↑
Constants could be define practically as units, but to avoid introducing too much noise in the units namespace, they can be defined separately with:
Units.constant :g, 'standard gravity', u{9.80665*m/s**2}
A constant can be used anywhere with the Units::Const prefix:
puts Units::Const.g # => 9.80665*m/s**2 # gram-force: puts u{g*Const.g} # => 9.80665*(g*m)/s**2
To avoid using the prefix, constants to be used unprefixed can be declared with a with_constants
; Note in the first example, that by introducing a constant named g
we’re hiding the gram units and would not be able to use it in the block.
# kilopond: puts Units.with_constants(:g){kg*g} # => 9.80665*(kg*m)/s**2 # 1 GeV mass puts Units.with_constants(:c){1*GeV/c**2}.to(:kg) # => 1.782661844855044e-27*kg # Planck units Units.with_constants :c, :G, :hbar do puts sqrt(hbar*G/c**3) # => 1.6161992557033346e-35*m puts sqrt(hbar*c/G) # => 2.176509252445312e-08*kg puts sqrt(hbar*G/c**5) # => 5.391060423886096e-44*s end
Caveat¶ ↑
Note that Ruby variable definition rules imply that this:
m = Units.u{m}
Results is a nil value (the outer m assignment defines a local m variable even before executing the block, so the m in the block refers to that, yet-unassigned, variable and not to the meter unit)
Note on Patches/Pull Requests¶ ↑
-
Fork the project.
-
Make your feature addition or bug fix.
-
Add tests for it. This is important so I don’t break it in a future version unintentionally.
-
Commit, do not mess with rakefile, version, or history. (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)
-
Send me a pull request. Bonus points for topic branches.
Copyright¶ ↑
Copyright © 2009 Javier Goizueta. See LICENSE for details.