units-system

Experimental unit conversion for Ruby 1.9

MIT License

Downloads
45.4K
Stars
3
Committers
1

= 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{60km + 10mi} # => 76.09344km puts u{sin(45°)} # => 0.707106781186547 x = u{120km/h} puts x.to(u{mi/h}) # => 74.5645430684801mi/h

Text strings can also be used to define units:

puts Units.u('60km + 10mi') # => 76.09344km puts Units.u('sin(45°)') # => 0.707106781186547 x = Units.u('120km/h') puts x.to(Units.u('mi/h')) # => 74.5645430684801mi/h

And also as the right operand of binary arithmetic operators:

puts Units.u('20km')/'h' # => 20.0km/h

New units can be defined with +Units.define+

Units.define :kph, 1, Units.u{km/h} puts Units.u{270kph.to(m/s)} # => 75.0m/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{gConst.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){kgg} # => 9.80665(kg*m)/s**2

1 GeV mass

puts Units.with_constants(:c){1GeV/c**2}.to(:kg) # => 1.782661844855044e-27kg

Planck units

Units.with_constants :c, :G, :hbar do puts sqrt(hbarG/c**3) # => 1.6161992557033346e-35m puts sqrt(hbarc/G) # => 2.176509252445312e-08kg puts sqrt(hbarG/c**5) # => 5.391060423886096e-44s 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 (c) 2009 Javier Goizueta. See LICENSE for details.