Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Experimental unit conversion for Ruby 1.9
Ruby

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
test
.document
.gitignore Ignore tmp files
Gemfile Use bundler for dependencies
Gemfile.lock
LICENSE
README.rdoc Fix examples which used the bogus G value
Rakefile
VERSION
units-system.gemspec

README.rdoc

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 either u{W()}, u{self.W} (method invocation) or u{self::W} (qualified constant). Units defined in text form, such as u('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.

Something went wrong with that request. Please try again.