Class: Unit

Inherits:
Numeric show all
Includes:
Comparable
Defined in:
lib/ruby_units/unit.rb,
lib/ruby_units/cache.rb,
lib/ruby_units/version.rb,
lib/ruby_units/unit_definitions.rb

Overview

Unit.setup

Defined Under Namespace

Modules: Version Classes: Cache

Constant Summary collapse

VERSION =

pre-generate hashes from unit definitions for performance.

Unit::Version::STRING
UNITY =
'<1>'
UNITY_ARRAY =
[UNITY]
FEET_INCH_REGEX =
/(\d+)\s*(?:'|ft|feet)\s*(\d+)\s*(?:"|in|inches)/
TIME_REGEX =
/(\d+)*:(\d+)*:*(\d+)*[:,]*(\d+)*/
LBS_OZ_REGEX =
/(\d+)\s*(?:#|lbs|pounds|pound-mass)+[\s,]*(\d+)\s*(?:oz|ounces)/
SCI_NUMBER =
%r{([+-]?\d*[.]?\d+(?:[Ee][+-]?)?\d*)}
RATIONAL_NUMBER =
/([+-]?\d+)\/(\d+)/
COMPLEX_NUMBER =
/#{SCI_NUMBER}?#{SCI_NUMBER}i\b/
NUMBER_REGEX =
/#{SCI_NUMBER}*\s*(.+)?/
UNIT_STRING_REGEX =
/#{SCI_NUMBER}*\s*([^\/]*)\/*(.+)*/
TOP_REGEX =
/([^ \*]+)(?:\^|\*\*)([\d-]+)/
BOTTOM_REGEX =
/([^* ]+)(?:\^|\*\*)(\d+)/
UNCERTAIN_REGEX =
/#{SCI_NUMBER}\s*\+\/-\s*#{SCI_NUMBER}\s(.+)/
COMPLEX_REGEX =
/#{COMPLEX_NUMBER}\s?(.+)?/
RATIONAL_REGEX =
/#{RATIONAL_NUMBER}\s?(.+)?/
KELVIN =
['<kelvin>']
FAHRENHEIT =
['<fahrenheit>']
RANKINE =
['<rankine>']
CELSIUS =
['<celsius>']
TEMP_REGEX =
/(?:temp|deg)[CFRK]/
SIGNATURE_VECTOR =
[:length, :time, :temperature, :mass, :current, :substance, :luminosity, :currency, :memory, :angle]
UNIT_DEFINITIONS =
{ 
  # prefixes
  '<googol>' => [%w{googol}, 1e100, :prefix],
  '<kibi>'  =>  [%w{Ki Kibi kibi}, 2**10, :prefix],
  '<mebi>'  =>  [%w{Mi Mebi mebi}, 2**20, :prefix],
  '<gibi>'  =>  [%w{Gi Gibi gibi}, 2**30, :prefix],
  '<tebi>'  =>  [%w{Ti Tebi tebi}, 2**40, :prefix],
  '<pebi>'  =>  [%w{Pi Pebi pebi}, 2**50, :prefix],
  '<exi>'   =>  [%w{Ei Exi exi}, 2**60, :prefix],
  '<zebi>'  =>  [%w{Zi Zebi zebi}, 2**70, :prefix],
  '<yebi>'  =>  [%w{Yi Yebi yebi}, 2**80, :prefix],
  '<yotta>' =>  [%w{Y Yotta yotta}, 1e24, :prefix],
  '<zetta>' =>  [%w{Z Zetta zetta}, 1e21, :prefix],
  '<exa>'   =>  [%w{E Exa exa}, 1e18, :prefix],
  '<peta>'  =>  [%w{P Peta peta}, 1e15, :prefix],
  '<tera>'  =>  [%w{T Tera tera}, 1e12, :prefix],
  '<giga>'  =>  [%w{G Giga giga}, 1e9, :prefix],
  '<mega>'  =>  [%w{M Mega mega}, 1e6, :prefix],
  '<kilo>'  =>  [%w{k kilo}, 1e3, :prefix],
  '<hecto>' =>  [%w{h Hecto hecto}, 1e2, :prefix],
  '<deca>'  =>  [%w{da Deca deca deka}, 1e1, :prefix],
  '<deci>'  =>  [%w{d Deci deci}, Rational(1,1e1), :prefix],
  '<centi>'  => [%w{c Centi centi}, Rational(1,1e2), :prefix],
  '<milli>' =>  [%w{m Milli milli}, Rational(1,1e3), :prefix],
  '<micro>'  => [%w{u Micro micro}, Rational(1,1e6), :prefix],
  '<nano>'  =>  [%w{n Nano nano}, Rational(1,1e9), :prefix],
  '<pico>'  =>  [%w{p Pico pico}, Rational(1,1e12), :prefix],
  '<femto>' =>  [%w{f Femto femto}, Rational(1,1e15), :prefix],
  '<atto>'  =>  [%w{a Atto atto}, Rational(1,1e18), :prefix],
  '<zepto>' =>  [%w{z Zepto zepto}, Rational(1,1e21), :prefix],
  '<yocto>' =>  [%w{y Yocto yocto}, Rational(1,1e24), :prefix],
  '<1>'     =>  [%w{1},1,:prefix],

  # length units
  '<meter>' =>  [%w{m meter meters metre metres}, 1, :length, %w{<meter>} ],
  '<inch>'  =>  [%w{in inch inches "}, Rational(254,10_000), :length, %w{<meter>}],
  '<foot>'  =>  [%w{ft foot feet '}, Rational(3048,10_000), :length, %w{<meter>}],
  '<yard>'  =>  [%w{yd yard yards}, 0.9144, :length, %w{<meter>}],
  '<mile>'  =>  [%w{mi mile miles}, 1609.344, :length, %w{<meter>}],
  '<naut-mile>' => [%w{nmi}, 1852, :length, %w{<meter>}],
  '<league>'=>  [%w{league leagues}, 4828, :length, %w{<meter>}],
  '<furlong>'=> [%w{furlong furlongs}, 201.2, :length, %w{<meter>}],
  '<rod>'   =>  [%w{rd rod rods}, 5.029, :length, %w{<meter>}],
  '<mil>'   =>  [%w{mil mils}, 0.0000254, :length, %w{<meter>}],
  '<angstrom>'  =>[%w{ang angstrom angstroms}, Rational(1,1e10), :length, %w{<meter>}],
  '<fathom>' => [%w{fathom fathoms}, 1.829, :length, %w{<meter>}],  
  '<pica>'  => [%w{pica picas}, 0.004217, :length, %w{<meter>}],
  '<point>' => [%w{pt point points}, 0.0003514, :length, %w{<meter>}],
  '<redshift>' => [%w{z red-shift}, 1.302773e26, :length, %w{<meter>}],
  '<AU>'    => [%w{AU astronomical-unit}, 149597900000, :length, %w{<meter>}],
  '<light-second>'=>[%w{ls light-second}, 299792500, :length, %w{<meter>}],
  '<light-minute>'=>[%w{lmin light-minute}, 17987550000, :length, %w{<meter>}],
  '<light-year>' => [%w{ly light-year}, 9460528000000000, :length, %w{<meter>}],
  '<parsec>'  => [%w{pc parsec parsecs}, 30856780000000000, :length, %w{<meter>}],

  #mass
  '<kilogram>' => [%w{kg kilogram kilograms}, 1, :mass, %w{<kilogram>}],
  '<AMU>' => [%w{u AMU amu}, 6.0221415e26, :mass, %w{<kilogram>}],
  '<dalton>' => [%w{Da Dalton Daltons dalton daltons}, 6.0221415e26, :mass, %w{<kilogram>}],
  '<slug>' => [%w{slug slugs}, 14.5939029, :mass, %w{<kilogram>}],
  '<short-ton>' => [%w{tn ton}, 907.18474, :mass, %w{<kilogram>}],
  '<metric-ton>'=>[%w{tonne}, 1000, :mass, %w{<kilogram>}],
  '<carat>' => [%w{ct carat carats}, 0.0002, :mass, %w{<kilogram>}],
  '<pound>' => [%w{lbs lb pound pounds #}, Rational(8171193714040401,18014398509481984), :mass, %w{<kilogram>}],
  '<ounce>' => [%w{oz ounce ounces}, Rational(8171193714040401,288230376151711744), :mass, %w{<kilogram>}],
  '<gram>'    =>  [%w{g gram grams gramme grammes},Rational(1,1e3),:mass, %w{<kilogram>}],

  #area
  '<hectare>'=>[%w{hectare}, 10000, :area, %w{<meter> <meter>}],
  '<acre>'=>[%w(acre acres), 4046.85642, :area, %w{<meter> <meter>}],
  '<sqft>'=>[%w(sqft), 1, :area, %w{<feet> <feet>}],

  #volume
  '<liter>' => [%w{l L liter liters litre litres}, Rational(1,1e3), :volume, %w{<meter> <meter> <meter>}],
  '<gallon>'=>  [%w{gal gallon gallons}, 0.0037854118, :volume, %w{<meter> <meter> <meter>}],
  '<quart>'=>  [%w{qt quart quarts}, 0.00094635295, :volume, %w{<meter> <meter> <meter>}],
  '<pint>'=>  [%w{pt pint pints}, 0.000473176475, :volume, %w{<meter> <meter> <meter>}],
  '<cup>'=>  [%w{cu cup cups}, 0.000236588238, :volume, %w{<meter> <meter> <meter>}],
  '<fluid-ounce>'=>  [%w{floz fluid-ounce}, 2.95735297e-5, :volume, %w{<meter> <meter> <meter>}],
  '<tablespoon>'=>  [%w{tbs tablespoon tablespoons}, 1.47867648e-5, :volume, %w{<meter> <meter> <meter>}],
  '<teaspoon>'=>  [%w{tsp teaspoon teaspoons}, 4.92892161e-6, :volume, %w{<meter> <meter> <meter>}],

  #volumetric flow
  '<cfm>' => [%w{cfm CFM CFPM}, (18435447/39062500000), :volumetric_flow, %w{<meter> <meter> <meter>}, %w{<second>}],

  #speed
  '<kph>' => [%w{kph}, 0.277777778, :speed, %w{<meter>}, %w{<second>}],
  '<mph>' => [%w{mph}, 0.44704, :speed, %w{<meter>}, %w{<second>}],
  '<knot>' => [%w{kt kn kts knot knots}, 0.514444444, :speed, %w{<meter>}, %w{<second>}],
  '<fps>'  => [%w{fps}, 0.3048, :speed, %w{<meter>}, %w{<second>}],
  
  #acceleration
  '<gee>' => [%w{gee}, 9.80655, :acceleration, %w{<meter>}, %w{<second> <second>}],
  

  #temperature_difference
  '<kelvin>' => [%w{degK kelvin}, 1, :temperature, %w{<kelvin>}],
  '<celsius>' => [%w{degC celsius celsius centigrade}, 1, :temperature, %w{<kelvin>}],
  '<fahrenheit>' => [%w{degF fahrenheit}, Rational(1,1.8), :temperature, %w{<kelvin>}],
  '<rankine>' => [%w{degR rankine}, Rational(1,1.8), :temperature, %w{<kelvin>}],
  '<tempK>'  => [%w{tempK}, 1, :temperature, %w{<tempK>}],
  '<tempC>'  => [%w{tempC}, 1, :temperature, %w{<tempK>}],
  '<tempF>'  => [%w{tempF}, Rational(1,1.8), :temperature, %w{<tempK>}],
  '<tempR>'  => [%w{tempR}, Rational(1,1.8), :temperature, %w{<tempK>}],
  
  #time
  '<second>'=>  [%w{s sec second seconds}, 1, :time, %w{<second>}],
  '<minute>'=>  [%w{min minute minutes}, 60, :time, %w{<second>}],  
  '<hour>'=>  [%w{h hr hrs hour hours}, 3600, :time, %w{<second>}],  
  '<day>'=>  [%w{d day days}, 3600*24, :time, %w{<second>}],
  '<week>'=>  [%w{wk week weeks}, 7*3600*24, :time, %w{<second>}],
  '<fortnight>'=> [%w{fortnight fortnights}, 1209600, :time, %W{<second>}],
  '<year>'=>  [%w{y yr year years annum}, 31556926, :time, %w{<second>}],
  '<decade>'=>[%w{decade decades}, 315569260, :time, %w{<second>}],
  '<century>'=>[%w{century centuries}, 3155692600, :time, %w{<second>}],

  #pressure
  '<pascal>' => [%w{Pa pascal Pascal}, 1, :pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<bar>' => [%w{bar bars}, 100000, :pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<mmHg>' => [%w{mmHg}, 133.322368,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<inHg>' => [%w{inHg}, 3386.3881472,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<torr>' => [%w{torr}, 133.322368,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<bar>' => [%w{bar}, 100000,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<atm>' => [%w{atm ATM atmosphere atmospheres}, 101325,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<psi>' => [%w{psi}, 6894.76,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<cmh2o>' => [%w{cmH2O}, 98.0638,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  '<inh2o>' => [%w{inH2O}, 249.082052,:pressure, %w{<kilogram>},%w{<meter> <second> <second>}],
  
  #viscosity
  '<poise>'  => [%w{P poise}, Rational(1,10), :viscosity, %w{<kilogram>},%w{<meter> <second>} ],
  '<stokes>' => [%w{St stokes}, Rational(1,1e4), :viscosity, %w{<meter> <meter>}, %w{<second>}],

  #substance
  '<mole>'  =>  [%w{mol mole}, 1, :substance, %w{<mole>}],

  #concentration
  '<molar>' => [%w{M molar}, 1000, :concentration, %w{<mole>}, %w{<meter> <meter> <meter>}],
  '<wtpercent>'  => [%w{wt% wtpercent}, 10, :concentration, %w{<kilogram>}, %w{<meter> <meter> <meter>}],

  #activity
  '<katal>' =>  [%w{kat katal Katal}, 1, :activity, %w{<mole>}, %w{<second>}],
  '<unit>'  =>  [%w{U enzUnit}, 16.667e-16, :activity, %w{<mole>}, %w{<second>}],

  #capacitance
  '<farad>' =>  [%w{F farad Farad}, 1, :capacitance, %w{<second> <second> <second> <second> <ampere> <ampere>}, %w{<kilogram> <meter> <meter>}],

  #charge
  '<coulomb>' =>  [%w{C coulomb Coulomb}, 1, :charge, %w{<ampere> <second>}],

  #current
  '<ampere>'  =>  [%w{A Ampere ampere amp amps}, 1, :current, %w{<ampere>}],

  #conductance
  '<siemens>' => [%w{S Siemens siemens}, 1, :resistance, %w{<second> <second> <second> <ampere> <ampere>}, %w{<kilogram> <meter> <meter>}],

  #inductance
  '<henry>' =>  [%w{H Henry henry}, 1, :inductance, %w{<meter> <meter> <kilogram>}, %w{<second> <second> <ampere> <ampere>}],

  #potential
  '<volt>'  =>  [%w{V Volt volt volts}, 1, :potential, %w{<meter> <meter> <kilogram>}, %w{<second> <second> <second> <ampere>}],

  #resistance
  '<ohm>' =>  [%w{Ohm ohm}, 1, :resistance, %w{<meter> <meter> <kilogram>},%w{<second> <second> <second> <ampere> <ampere>}],

  #magnetism
  '<weber>' => [%w{Wb weber webers}, 1, :magnetism, %w{<meter> <meter> <kilogram>}, %w{<second> <second> <ampere>}],
  '<tesla>'  => [%w{T tesla teslas}, 1, :magnetism, %w{<kilogram>}, %w{<second> <second> <ampere>}],
  '<gauss>' => [%w{G gauss}, Rational(1,1e4), :magnetism,  %w{<kilogram>}, %w{<second> <second> <ampere>}],
  '<maxwell>' => [%w{Mx maxwell maxwells}, Rational(1,1e8), :magnetism, %w{<meter> <meter> <kilogram>}, %w{<second> <second> <ampere>}],
  '<oersted>'  => [%w{Oe oersted oersteds}, 250.0/Math::PI, :magnetism, %w{<ampere>}, %w{<meter>}],

  #energy
  '<joule>' =>  [%w{J joule Joule joules}, 1, :energy, %w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<erg>'   =>  [%w{erg ergs}, Rational(1,1e7), :energy, %w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<btu>'   =>  [%w{BTU btu BTUs}, 1055.056, :energy, %w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<calorie>' =>  [%w{cal calorie calories}, 4.18400, :energy,%w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<Calorie>' =>  [%w{Cal Calorie Calories}, 4184.00, :energy,%w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<therm-US>' => [%w{th therm therms Therm}, 105480400, :energy,%w{<meter> <meter> <kilogram>}, %w{<second> <second>}],

  #force
  '<newton>'  => [%w{N Newton newton}, 1, :force, %w{<kilogram> <meter>}, %w{<second> <second>}],
  '<dyne>'  => [%w{dyn dyne}, Rational(1,1e5), :force, %w{<kilogram> <meter>}, %w{<second> <second>}],
  '<pound-force>'  => [%w{lbf pound-force}, 4.448222, :force, %w{<kilogram> <meter>}, %w{<second> <second>}],

  #frequency
  '<hertz>' => [%w{Hz hertz Hertz}, 1, :frequency, %w{<1>}, %{<second>}],

  #angle
  '<radian>' =>[%w{rad radian radian radians}, 1, :angle, %w{<radian>}],
  '<degree>' =>[%w{deg degree degrees}, Math::PI / 180.0, :angle, %w{<radian>}],
  '<grad>'   =>[%w{grad gradian grads}, Math::PI / 200.0, :angle, %w{<radian>}],
  '<steradian>'  => [%w{sr steradian steradians}, 1, :solid_angle, %w{<steradian>}],

  #rotation
  '<rotation>' => [%w{rotation}, 2.0*Math::PI, :angle, %w{<radian>}],
  '<rpm>'   =>[%w{rpm}, 2.0*Math::PI / 60.0, :angular_velocity, %w{<radian>}, %w{<second>}],

  #memory
  '<byte>'  =>[%w{B byte}, 1, :memory, %w{<byte>}],
  '<bit>'  =>[%w{b bit}, 0.125, :memory, %w{<byte>}],

  #currency
  '<dollar>'=>[%w{USD dollar}, 1, :currency, %w{<dollar>}],
  '<cents>' =>[%w{cents}, Rational(1,100), :currency, %w{<dollar>}],

  #luminosity
  '<candela>' => [%w{cd candela}, 1, :luminosity, %w{<candela>}],
  '<lumen>' => [%w{lm lumen}, 1, :luminous_power, %w{<candela> <steradian>}],
  '<lux>' =>[%w{lux}, 1, :illuminance, %w{<candela> <steradian>}, %w{<meter> <meter>}],

  #power
  '<watt>'  => [%w{W watt watts}, 1, :power, %w{<kilogram> <meter> <meter>}, %w{<second> <second> <second>}],
  '<horsepower>'  =>  [%w{hp horsepower}, 745.699872, :power, %w{<kilogram> <meter> <meter>}, %w{<second> <second> <second>}],

  #radiation
  '<gray>' => [%w{Gy gray grays}, 1, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
  '<roentgen>' => [%w{R roentgen}, 0.009330, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
  '<sievert>' => [%w{Sv sievert sieverts}, 1, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
  '<becquerel>' => [%w{Bq bequerel bequerels}, 1, :radiation, %w{<1>},%w{<second>}],
  '<curie>' => [%w{Ci curie curies}, 3.7e10, :radiation, %w{<1>},%w{<second>}],
  
  # rate
  '<cpm>' => [%w{cpm}, Rational(1,60), :rate, %w{<count>},%w{<second>}],
  '<dpm>' => [%w{dpm}, Rational(1,60), :rate, %w{<count>},%w{<second>}],
  '<bpm>' => [%w{bpm}, Rational(1,60), :rate, %w{<count>},%w{<second>}],

  #resolution / typography
  '<dot>' => [%w{dot dots}, 1, :resolution, %w{<each>}],
  '<pixel>' => [%w{pixel px}, 1, :resolution, %w{<each>}],
  '<ppi>' => [%w{ppi}, 1, :resolution, %w{<pixel>}, %w{<inch>}],
  '<dpi>' => [%w{dpi}, 1, :typography, %w{<dot>}, %w{<inch>}],
  '<pica>' => [%w{pica}, 0.00423333333 , :typography, %w{<meter>}],
  '<point>' => [%w{point pt}, 0.000352777778, :typography, %w{<meter>}],

  #other
  '<cell>' => [%w{cells cell}, 1, :counting, %w{<each>}],
  '<each>' => [%w{each}, 1, :counting, %w{<each>}],
  '<count>' => [%w{count}, 1, :counting, %w{<each>}],  
  '<base-pair>'  => [%w{bp}, 1, :counting, %w{<each>}],
  '<nucleotide>' => [%w{nt}, 1, :counting, %w{<each>}],
  '<molecule>' => [%w{molecule molecules}, 1, :counting, %w{<1>}],
  '<dozen>' =>  [%w{doz dz dozen},12,:prefix_only, %w{<each>}],
  '<percent>'=> [%w{% percent}, Rational(1,100), :prefix_only, %w{<1>}],
  '<ppm>' =>  [%w{ppm},Rational(1,1e6),:prefix_only, %w{<1>}],
  '<ppt>' =>  [%w{ppt},Rational(1,1e9),:prefix_only, %w{<1>}],
  '<gross>' =>  [%w{gr gross},144, :prefix_only, %w{<dozen> <dozen>}],
  '<decibel>'  => [%w{dB decibel decibels}, 1, :logarithmic, %w{<decibel>}]


}
@@USER_DEFINITIONS =
{}
@@PREFIX_VALUES =
{}
@@PREFIX_MAP =
{}
@@UNIT_MAP =
{}
@@UNIT_VALUES =
{}
@@OUTPUT_MAP =
{}
@@BASE_UNITS =
['<meter>','<kilogram>','<second>','<mole>', '<ampere>','<radian>','<kelvin>','<tempK>','<byte>','<dollar>','<candela>','<each>','<steradian>','<decibel>']
@@KINDS =
{
-312078 => :elastance,
-312058=>:resistance,
-312038=>:inductance, 
-152040=>:magnetism, 
-152038=>:magnetism, 
-152058=>:potential,
-7997 => :specific_volume,
-79 => :snap,
-59 => :jolt, 
-39=>:acceleration,
-38=>:radiation, 
-20=>:frequency, 
-19=>:speed, 
-18=>:viscosity, 
-17 => :volumetric_flow,
-1 => :wavenumber,
0=>:unitless, 
1=>:length, 
2=>:area, 
3=>:volume, 
20=>:time, 
400=>:temperature, 
7941 => :yank,
7942=>:power, 
7959=>:pressure, 
7962=>:energy, 
7979=>:viscosity, 
7961=>:force, 
7981 => :momentum,
7982 => :angular_momentum,
7997 => :density,
7998 => :area_density,
8000=>:mass, 
159999=>:magnetism, 
160000=>:current, 
160020=>:charge, 
312058=>:resistance,
312078=>:capacitance,
3199980=>:activity, 
3199997=>:molar_concentration, 
3200000=>:substance, 
63999998=>:illuminance, 
64000000=>:luminous_power, 
1280000000=>:currency, 
25600000000=>:memory,
511999999980=>:angular_velocity, 
512000000000=>:angle
}
@@cached_units =
{}
@@base_unit_cache =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*options) ⇒ Unit

Create a new Unit object. Can be initialized using a String, a Hash, an Array, Time, DateTime Valid formats include:

"5.6 kg*m/s^2"
"5.6 kg*m*s^-2"
"5.6 kilogram*meter*second^-2"
"2.2 kPa"
"37 degC"
"1"  -- creates a unitless constant with value 1
"GPa"  -- creates a unit with scalar 1 with units 'GPa'
6'4"  -- recognized as 6 feet + 4 inches 
8 lbs 8 oz -- recognized as 8 lbs + 8 ounces

Raises:

  • (ArgumentError)


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/ruby_units/unit.rb', line 201

def initialize(*options)
  @scalar = nil
  @base_scalar = nil
  @unit_name = nil
  @signature = nil
  @output = {}
  if options.size == 2
    # options[0] is the scalar
    # options[1] is a unit string
    begin
      cached = @@cached_units[options[1]] * options[0]
      copy(cached)
    rescue 
      initialize("#{options[0]} #{(options[1].units rescue options[1])}")
    end
    return
  end
  if options.size == 3
    options[1] = options[1].join if options[1].kind_of?(Array)
    options[2] = options[2].join if options[2].kind_of?(Array)
    begin
      cached = @@cached_units["#{options[1]}/#{options[2]}"] * options[0] 
      copy(cached)
    rescue 
      initialize("#{options[0]} #{options[1]}/#{options[2]}")
    end
    return
  end
  
  case options[0]
  when Hash
    @scalar = options[0][:scalar] || 1
    @numerator = options[0][:numerator] || UNITY_ARRAY
    @denominator = options[0][:denominator] || UNITY_ARRAY
    @signature = options[0][:signature]
  when Array
    initialize(*options[0])
    return
  when Numeric
    @scalar = options[0]
    @numerator = @denominator = UNITY_ARRAY
  when Time
    @scalar = options[0].to_f
    @numerator = ['<second>']
    @denominator = UNITY_ARRAY
  when DateTime, Date
    @scalar = options[0].ajd
    @numerator = ['<day>']
    @denominator = UNITY_ARRAY
  when /^\s*$/
    raise ArgumentError, "No Unit Specified"
  when String
    parse(options[0])
  else
    raise ArgumentError, "Invalid Unit Format"
  end
  self.update_base_scalar
  raise ArgumentError, "Temperatures must not be less than absolute zero" if self.is_temperature? &&  self.base_scalar < 0

  unary_unit = self.units || ""
  if options.first.instance_of?(String)
    opt_scalar, opt_units = Unit.parse_into_numbers_and_units(options[0])
    unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(#{TEMP_REGEX})|(pounds|lbs[ ,]\d+ ounces|oz)|('\d+")|(ft|feet[ ,]\d+ in|inch|inches)|%|(#{TIME_REGEX})|i\s?(.+)?|&plusmn;|\+\/-/)
      @@cached_units[opt_units] = (self.scalar == 1 ? self : opt_units.unit) if opt_units && !opt_units.empty?
    end
  end

    unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /#{TEMP_REGEX}/) then
      @@cached_units[unary_unit] = (self.scalar == 1 ? self : unary_unit.unit)
    end

  [@scalar, @numerator, @denominator, @base_scalar, @signature, @is_base].each {|x| x.freeze}
  self
end

Instance Attribute Details

#base_denominatorObject

Returns the value of attribute base_denominator.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def base_denominator
  @base_denominator
end

#base_numeratorObject

Returns the value of attribute base_numerator.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def base_numerator
  @base_numerator
end

#base_scalarObject

Returns the value of attribute base_scalar.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def base_scalar
  @base_scalar
end

#denominatorObject

Returns the value of attribute denominator.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def denominator
  @denominator
end

#numeratorObject

Returns the value of attribute numerator.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def numerator
  @numerator
end

#outputObject

Returns the value of attribute output.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def output
  @output
end

#scalarObject

Returns the value of attribute scalar.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def scalar
  @scalar
end

#signatureObject

Returns the value of attribute signature.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def signature
  @signature
end

#unit_nameObject

Returns the value of attribute unit_name.



153
154
155
# File 'lib/ruby_units/unit.rb', line 153

def unit_name
  @unit_name
end

Class Method Details

.base_unit_cacheObject



290
291
292
# File 'lib/ruby_units/unit.rb', line 290

def self.base_unit_cache
  return @@base_unit_cache
end

.cachedObject



280
281
282
# File 'lib/ruby_units/unit.rb', line 280

def self.cached
  return @@cached_units
end

.clear_cacheObject



284
285
286
287
288
# File 'lib/ruby_units/unit.rb', line 284

def self.clear_cache
  @@cached_units = {}
  @@base_unit_cache = {}
  Unit.new(1)
end

.parse(input) ⇒ Object

parse strings like “1 minute in seconds”



297
298
299
300
# File 'lib/ruby_units/unit.rb', line 297

def self.parse(input)
  first, second = input.scan(/(.+)\s(?:in|to|as)\s(.+)/i).first
  second.nil? ? first.unit : first.unit.convert_to(second)
end

.setupObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/ruby_units/unit.rb', line 126

def self.setup
  @@ALL_UNIT_DEFINITIONS = UNIT_DEFINITIONS.merge!(@@USER_DEFINITIONS)
  for unit in (@@ALL_UNIT_DEFINITIONS) do
    key, value = unit
    if value[2] == :prefix then
      @@PREFIX_VALUES[key]=value[1]
      for name in value[0] do
        @@PREFIX_MAP[name]=key
      end    
    else
      @@UNIT_VALUES[key]={}
      @@UNIT_VALUES[key][:scalar]=value[1]
      @@UNIT_VALUES[key][:numerator]=value[3] if value[3]
      @@UNIT_VALUES[key][:denominator]=value[4] if value[4]
      for name in value[0] do
        @@UNIT_MAP[name]=key
      end
    end
    @@OUTPUT_MAP[key]=value[0][0]        
  end
  @@PREFIX_REGEX = @@PREFIX_MAP.keys.sort_by {|prefix| [prefix.length, prefix]}.reverse.join('|')
  @@UNIT_REGEX = @@UNIT_MAP.keys.sort_by {|unit_name| [unit_name.length, unit]}.reverse.join('|')
  @@UNIT_MATCH_REGEX = /(#{@@PREFIX_REGEX})*?(#{@@UNIT_REGEX})\b/    
  Unit.new(1)
end

Instance Method Details

#%(other) ⇒ Object

perform a modulo on a unit, will raise an exception if the units are not compatible



644
645
646
# File 'lib/ruby_units/unit.rb', line 644

def %(other)
  self.divmod(other).last
end

#*(other) ⇒ Object

Multiply two units.



597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/ruby_units/unit.rb', line 597

def *(other)
  case other
  when Unit
    raise ArgumentError, "Cannot multiply by temperatures" if [other,self].any? {|x| x.is_temperature?}
    opts = Unit.eliminate_terms(@scalar*other.scalar, @numerator + other.numerator ,@denominator + other.denominator)
    opts.merge!(:signature => @signature + other.signature)
    Unit.new(opts)      
  when Numeric
    Unit.new(:scalar=>@scalar*other, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
  else
    x,y = coerce(other)
    x * y
  end
end

#**(other) ⇒ Object

Exponentiate. Only takes integer powers. Note that anything raised to the power of 0 results in a Unit object with a scalar of 1, and no units. Throws an exception if exponent is not an integer. Ideally this routine should accept a float for the exponent It should then convert the float to a rational and raise the unit by the numerator and root it by the denominator but, sadly, floats can’t be converted to rationals.

For now, if a rational is passed in, it will be used, otherwise we are stuck with integers and certain floats < 1

Raises:

  • (ArgumentError)


656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
# File 'lib/ruby_units/unit.rb', line 656

def **(other)
  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
  if other.kind_of?(Numeric)
    return self.inverse if other == -1
    return self if other == 1
    return 1 if other.zero?
  end
  case other
  when Rational
    self.power(other.numerator).root(other.denominator)
  when Integer
    self.power(other)
  when Float
    return self**(other.to_i) if other == other.to_i
    valid = (1..9).map {|x| 1/x}
    raise ArgumentError, "Not a n-th root (1..9), use 1/n" unless valid.include? other.abs
    self.root((1/other).to_int)
  when Complex
    raise ArgumentError, "exponentiation of complex numbers is not yet supported."
  else
    raise ArgumentError, "Invalid Exponent"
  end
end

#+(other) ⇒ Object

Add two units together. Result is same units as receiver and scalar and base_scalar are updated appropriately throws an exception if the units are not compatible. It is possible to add Time objects to units of time



536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/ruby_units/unit.rb', line 536

def +(other)
  case other
  when Unit
    case
    when self.zero?
      other.dup
    when self =~ other
      raise ArgumentError, "Cannot add two temperatures" if ([self, other].all? {|x| x.is_temperature?})
      if [self, other].any? {|x| x.is_temperature?}
        if self.is_temperature?
          Unit.new(:scalar => (self.scalar + other.convert_to(self.temperature_scale).scalar), :numerator => @numerator, :denominator=>@denominator, :signature => @signature)
        else
          Unit.new(:scalar => (other.scalar + self.convert_to(other.temperature_scale).scalar), :numerator => other.numerator, :denominator=>other.denominator, :signature => other.signature)
        end
      else
        @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.to_base.scalar))
        Unit.new(:scalar=>(self.base_scalar + other.base_scalar)*@q, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)
      end
    else
      raise ArgumentError,  "Incompatible Units ('#{self}' not compatible with '#{other}')"
    end
  when Date, Time
    raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be added to a Unit"
  else
    x,y = coerce(other)
    y + x
  end
end

#-(other) ⇒ Object

Subtract two units. Result is same units as receiver and scalar and base_scalar are updated appropriately throws an exception if the units are not compatible.



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
# File 'lib/ruby_units/unit.rb', line 567

def -(other)
  case other
    when Unit
      case
      when self.zero?
        -other.dup
      when self =~ other
        case
          when [self, other].all? {|x| x.is_temperature?}
            Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator  => KELVIN, :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self.temperature_scale) 
          when self.is_temperature?
            Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator  => ['<tempK>'], :denominator => UNITY_ARRAY, :signature => @signature).convert_to(self) 
          when other.is_temperature?
            raise ArgumentError, "Cannot subtract a temperature from a differential degree unit"
          else
            @q ||= ((@@cached_units[self.units].scalar / @@cached_units[self.units].base_scalar) rescue (self.units.unit.scalar/self.units.unit.to_base.scalar))
            Unit.new(:scalar=>(self.base_scalar - other.base_scalar)*@q, :numerator=>@numerator, :denominator=>@denominator, :signature=>@signature)            
        end
      else
         raise ArgumentError,  "Incompatible Units ('#{self}' not compatible with '#{other}')"
      end
  when Time
    raise ArgumentError, "Date and Time objects represent fixed points in time and cannot be subtracted from to a Unit, which can only represent time spans"
  else
      x,y = coerce(other)
      y-x
  end
end

#-@Object

negates the scalar of the Unit



866
867
868
869
# File 'lib/ruby_units/unit.rb', line 866

def -@
  return -@scalar if self.unitless?
  self.dup * -1
end

#/(other) ⇒ Object

Divide two units. Throws an exception if divisor is 0



614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
# File 'lib/ruby_units/unit.rb', line 614

def /(other)
  case other
  when Unit 
    raise ZeroDivisionError if other.zero?
    raise ArgumentError, "Cannot divide with temperatures" if [other,self].any? {|x| x.is_temperature?}
    opts = Unit.eliminate_terms(@scalar/other.scalar, @numerator + other.denominator ,@denominator + other.numerator)
    opts.merge!(:signature=> @signature - other.signature)
    Unit.new(opts)      
  when Numeric
    raise ZeroDivisionError if other.zero?
    Unit.new(:scalar=>@scalar/other, :numerator=>@numerator, :denominator=>@denominator, :signature => @signature)      
  else
    x,y = coerce(other)
    y / x
  end
end

#<=>(other) ⇒ Object

Compare two Unit objects. Throws an exception if they are not of compatible types. Comparisons are done based on the value of the unit in base SI units.



462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/ruby_units/unit.rb', line 462

def <=>(other)
  case
  when !self.base_scalar.respond_to?(:<=>)
    raise NoMethodError, "undefined method `<=>' for #{self.base_scalar.inspect}"
  when !self.is_temperature? && other.zero?
    return self.base_scalar <=> 0
  when other.instance_of?(Unit)
    raise ArgumentError, "Incompatible Units (#{self.units} !~ #{other.units})" unless self =~ other
    return self.base_scalar <=> other.base_scalar
  else
    x,y = coerce(other)
    return x <=> y
  end
end

#==(other) ⇒ Object

Compare Units for equality this is necessary mostly for Complex units. Complex units do not have a <=> operator so we define this one here so that we can properly check complex units for equality. Units of incompatible types are not equal, except when they are both zero and neither is a temperature Equality checks can be tricky since round off errors may make essentially equivalent units appear to be different.



483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/ruby_units/unit.rb', line 483

def ==(other)
  case
  when other.respond_to?(:zero?) && other.zero?
    return self.zero?
  when other.instance_of?(Unit)
    return false unless self =~ other
    return self.base_scalar == other.base_scalar
  else
    x,y = coerce(other)
    return x == y
  end      
end

#===(other) ⇒ Object Also known as: same?, same_as?

Compare two units. Returns true if quantities and units match

Unit(“100 cm”) === Unit(“100 cm”) # => true Unit(“100 cm”) === Unit(“1 m”) # => false



520
521
522
523
524
525
526
527
528
# File 'lib/ruby_units/unit.rb', line 520

def ===(other)
  case other
  when Unit
    (self.scalar == other.scalar) && (self.units == other.units)
  else
    x,y = coerce(other)
    x === y
  end
end

#=~(other) ⇒ Object Also known as: compatible?, compatible_with?

check to see if units are compatible, but not the scalar part this check is done by comparing signatures for performance reasons if passed a string, it will create a unit object with the string and then do the comparison this permits a syntax like:

unit =~ "mm"

if you want to do a regexp on the unit string do this …

unit.units =~ /regexp/


503
504
505
506
507
508
509
510
511
# File 'lib/ruby_units/unit.rb', line 503

def =~(other)
  case other
  when Unit
    self.signature == other.signature
  else
    x,y = coerce(other)
    x =~ y
  end 
end

#absObject



871
872
873
874
# File 'lib/ruby_units/unit.rb', line 871

def abs
  return @scalar.abs if self.unitless?
  Unit.new(@scalar.abs, @numerator, @denominator)
end

#agoObject

‘5 min’.unit.ago



936
937
938
# File 'lib/ruby_units/unit.rb', line 936

def ago
  self.before
end

#before(time_point = ::Time.now) ⇒ Object Also known as: before_now

‘5 min’.before(time)



941
942
943
944
945
946
947
948
# File 'lib/ruby_units/unit.rb', line 941

def before(time_point = ::Time.now)
  case time_point
  when Time, Date, DateTime
    time_point - self rescue time_point.to_datetime - self
  else
    raise ArgumentError, "Must specify a Time, Date, or DateTime"
  end
end

#ceilObject



876
877
878
879
# File 'lib/ruby_units/unit.rb', line 876

def ceil
  return @scalar.ceil if self.unitless?
  Unit.new(@scalar.ceil, @numerator, @denominator)    
end

#coerce(other) ⇒ Object

automatically coerce objects to units when possible if an object defines a ‘to_unit’ method, it will be coerced using that method



991
992
993
994
995
996
997
998
999
1000
1001
# File 'lib/ruby_units/unit.rb', line 991

def coerce(other)
  if other.respond_to? :to_unit
    return [other.to_unit, self]
  end
  case other
  when Unit
    [other, self]
  else 
    [Unit.new(other), self]
  end
end

#convert_to(other) ⇒ Object Also known as: >>, to

convert to a specified unit string or to the same units as another Unit

unit >> "kg"  will covert to kilograms
unit1 >> unit2 converts to same units as unit2 object

To convert a Unit object to match another Unit object, use:

unit1 >>= unit2

Throws an exception if the requested target units are incompatible with current Unit.

Special handling for temperature conversions is supported. If the Unit object is converted from one temperature unit to another, the proper temperature offsets will be used. Supports Kelvin, Celsius, fahrenheit, and Rankine scales.

Note that if temperature is part of a compound unit, the temperature will be treated as a differential and the units will be scaled appropriately.



744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
# File 'lib/ruby_units/unit.rb', line 744

def convert_to(other)
  return self if other.nil? 
  return self if TrueClass === other
  return self if FalseClass === other
  if (Unit === other && other.is_temperature?) || (String === other && other =~ /temp[CFRK]/) 
    raise ArgumentError, "Receiver is not a temperature unit" unless self.degree?
    start_unit = self.units
    target_unit = other.units rescue other
    unless @base_scalar
      @base_scalar = case start_unit
      when 'tempC'
        @scalar + 273.15
      when 'tempK'
        @scalar
      when 'tempF'
        (@scalar+459.67)*Rational(5,9)
      when 'tempR'
        @scalar*Rational(5,9)
      end
    end
    q=  case target_unit
    when 'tempC'
      @base_scalar - 273.15
    when 'tempK'
      @base_scalar 
    when 'tempF'
      @base_scalar * Rational(9,5) - 459.67
    when 'tempR'
      @base_scalar * Rational(9,5)
    end
    Unit.new("#{q} #{target_unit}")
  else
    case other
    when Unit
      return self if other.units == self.units
      target = other
    when String
      target = Unit.new(other)
    else
      raise ArgumentError, "Unknown target units"
    end
    raise ArgumentError,  "Incompatible Units" unless self =~ target
    _numerator1 = @numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
    _denominator1 = @denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
    _numerator2 = target.numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
    _denominator2 = target.denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
    
    q = @scalar * ( (_numerator1 + _denominator2).inject(1) {|product,n| product*n} ) / 
        ( (_numerator2 + _denominator1).inject(1) {|product,n| product*n} ) 
    
    
    Unit.new(:scalar=>q, :numerator=>target.numerator, :denominator=>target.denominator, :signature => target.signature)
  end
end

#copy(from) ⇒ Object



165
166
167
168
169
170
171
172
173
# File 'lib/ruby_units/unit.rb', line 165

def copy(from)
  @scalar = from.scalar
  @numerator = from.numerator
  @denominator = from.denominator
  @is_base = from.is_base?
  @signature = from.signature
  @base_scalar = from.base_scalar
  @unit_name = from.unit_name rescue nil
end

#divmod(other) ⇒ Object

divide two units and return quotient and remainder when both units are in the same units we just use divmod on the raw scalars otherwise we use the scalar of the base unit which will be a float

Raises:

  • (ArgumentError)


634
635
636
637
638
639
640
641
# File 'lib/ruby_units/unit.rb', line 634

def divmod(other)
  raise ArgumentError, "Incompatible Units" unless self =~ other
  if self.units == other.units
    return self.scalar.divmod(other.scalar)
  else
    return self.to_base.scalar.divmod(other.to_base.scalar)
  end
end

#floorObject



881
882
883
884
# File 'lib/ruby_units/unit.rb', line 881

def floor
  return @scalar.floor if self.unitless?
  Unit.new(@scalar.floor, @numerator, @denominator)    
end

#from(time_point) ⇒ Object Also known as: after, from_now

‘5 min’.from(time)



976
977
978
979
980
981
982
983
# File 'lib/ruby_units/unit.rb', line 976

def from(time_point)
  case time_point
  when Time, DateTime, Date
    time_point + self rescue time_point.to_datetime + self
  else
    raise ArgumentError, "Must specify a Time, Date, or DateTime"       
  end
end

#inspect(option = nil) ⇒ Object

Normally pretty prints the unit, but if you really want to see the guts of it, pass ‘:dump’



429
430
431
432
# File 'lib/ruby_units/unit.rb', line 429

def inspect(option=nil)
  return super() if option == :dump
  self.to_s
end

#inverseObject

returns inverse of Unit (1/unit)



725
726
727
# File 'lib/ruby_units/unit.rb', line 725

def inverse
  Unit("1") / self
end

#is_base?Boolean Also known as: base?

Returns ‘true’ if the Unit is represented in base units

Returns:

  • (Boolean)


308
309
310
311
312
313
314
315
316
# File 'lib/ruby_units/unit.rb', line 308

def is_base?
  return @is_base if defined? @is_base
  return @is_base=true if self.degree? && self.numerator.size == 1 && self.denominator == UNITY_ARRAY && self.units =~ /(?:deg|temp)K/
  n = @numerator + @denominator
  for x in n.compact do 
    return @is_base=false unless x == UNITY || (@@BASE_UNITS.include?((x)))
  end
  return @is_base = true
end

#is_degree?Boolean Also known as: degree?

true if a degree unit or equivalent.

Returns:

  • (Boolean)


441
442
443
# File 'lib/ruby_units/unit.rb', line 441

def is_degree?
  self.kind == :temperature
end

#is_temperature?Boolean Also known as: temperature?

true if unit is a ‘temperature’, false if a ‘degree’ or anything else

Returns:

  • (Boolean)


435
436
437
# File 'lib/ruby_units/unit.rb', line 435

def is_temperature?
  self.is_degree? && (!(self.units =~ /temp[CFRK]/).nil?)
end

#kindObject



276
277
278
# File 'lib/ruby_units/unit.rb', line 276

def kind
  return @@KINDS[self.signature]
end

#kind_of?(klass) ⇒ Boolean

needed to make complex units play nice – otherwise not detected as a complex_generic

Returns:

  • (Boolean)


161
162
163
# File 'lib/ruby_units/unit.rb', line 161

def kind_of?(klass)
  self.scalar.kind_of?(klass)
end

#power(n) ⇒ Object

returns the unit raised to the n-th power. Integers only

Raises:

  • (ArgumentError)


681
682
683
684
685
686
687
688
689
690
691
692
# File 'lib/ruby_units/unit.rb', line 681

def power(n)
  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
  raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
  return self.inverse if n == -1
  return 1 if n.zero?
  return self if n == 1
  if n > 0 then 
    (1..(n-1).to_i).inject(self) {|product, x| product * self}
  else
    (1..-(n-1).to_i).inject(self) {|product, x| product / self}
  end
end

#predObject

returns next unit in a range. ‘1 mm’.unit.succ #=> ‘2 mm’.unit only works when the scalar is an integer

Raises:

  • (ArgumentError)


906
907
908
909
# File 'lib/ruby_units/unit.rb', line 906

def pred
  raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
  Unit.new(@scalar.to_i.pred, @numerator, @denominator)
end

#root(n) ⇒ Object

Calculates the n-th root of a unit, where n = (1..9) if n < 0, returns 1/unit^(1/n)

Raises:

  • (ArgumentError)


696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/ruby_units/unit.rb', line 696

def root(n)
  raise ArgumentError, "Cannot take the root of a temperature" if self.is_temperature?
  raise ArgumentError, "Exponent must an Integer" unless n.kind_of?(Integer)
  raise ArgumentError, "0th root undefined" if n.zero?
  return self if n == 1
  return self.root(n.abs).inverse if n < 0
  
  vec = self.unit_signature_vector
  vec=vec.map {|x| x % n}      
  raise ArgumentError, "Illegal root" unless vec.max == 0
  num = @numerator.dup
  den = @denominator.dup
    
  for item in @numerator.uniq do
    x = num.find_all {|i| i==item}.size
    r = ((x/n)*(n-1)).to_int
    r.times {|y| num.delete_at(num.index(item))}
  end
  
  for item in @denominator.uniq do 
    x = den.find_all {|i| i==item}.size
    r = ((x/n)*(n-1)).to_int
    r.times {|y| den.delete_at(den.index(item))}
  end
  q = @scalar < 0 ? (-1)**Rational(1,n) * (@scalar.abs)**Rational(1,n) : @scalar**Rational(1,n)
  Unit.new(:scalar=>q,:numerator=>num,:denominator=>den)    
end

#roundObject



886
887
888
889
# File 'lib/ruby_units/unit.rb', line 886

def round
  return @scalar.round if self.unitless?
  Unit.new(@scalar.round, @numerator, @denominator)    
end

#since(time_point) ⇒ Object

‘min’.since(time)



952
953
954
955
956
957
958
959
960
961
# File 'lib/ruby_units/unit.rb', line 952

def since(time_point)
  case time_point
  when Time
    (Time.now - time_point).unit('s').convert_to(self)
  when DateTime, Date
    (DateTime.now - time_point).unit('d').convert_to(self)
  else
    raise ArgumentError, "Must specify a Time, Date, or DateTime" 
  end
end

#succObject Also known as: next

returns next unit in a range. ‘1 mm’.unit.succ #=> ‘2 mm’.unit only works when the scalar is an integer

Raises:

  • (ArgumentError)


898
899
900
901
# File 'lib/ruby_units/unit.rb', line 898

def succ
  raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i
  Unit.new(@scalar.to_i.succ, @numerator, @denominator)
end

#temperature_scaleObject

returns the ‘degree’ unit associated with a temperature unit ‘100 tempC’.unit.temperature_scale #=> ‘degC’



448
449
450
451
452
# File 'lib/ruby_units/unit.rb', line 448

def temperature_scale
  return nil unless self.is_temperature?
  self.units =~ /temp([CFRK])/
  "deg#{$1}"
end

#to_baseObject Also known as: base

convert to base SI units results of the conversion are cached so subsequent calls to this will be fast



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/ruby_units/unit.rb', line 321

def to_base
  return self if self.is_base?
  if self.units =~ /\A(?:temp|deg)[CRF]\Z/
    if RUBY_VERSION < "1.9"
      @signature = @@KINDS.index(:temperature)
    else
      #:nocov:
      @signature = @@KINDS.key(:temperature)
      #:nocov:
    end
    base = case 
    when self.is_temperature?
      self.convert_to('tempK')
    when self.is_degree?
      self.convert_to('degK')
    end
    return base
  end

  cached = ((@@base_unit_cache[self.units] * self.scalar) rescue nil)
  return cached if cached

  num = []
  den = []
  q = 1
  for unit in @numerator.compact do
    if @@PREFIX_VALUES[unit]
      q *= @@PREFIX_VALUES[unit]
    else
      q *= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
      num << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
      den << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
    end
  end
  for unit in @denominator.compact do
    if @@PREFIX_VALUES[unit]
      q /= @@PREFIX_VALUES[unit]
    else
      q /= @@UNIT_VALUES[unit][:scalar] if @@UNIT_VALUES[unit]
      den << @@UNIT_VALUES[unit][:numerator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:numerator]
      num << @@UNIT_VALUES[unit][:denominator] if @@UNIT_VALUES[unit] && @@UNIT_VALUES[unit][:denominator]
    end
  end

  num = num.flatten.compact
  den = den.flatten.compact
  num = UNITY_ARRAY if num.empty?
  base= Unit.new(Unit.eliminate_terms(q,num,den))
  @@base_unit_cache[self.units]=base
  return base * @scalar
end

#to_cObject

converts the unit back to a complex if it is unitless. Otherwise raises an exception

Raises:

  • (RuntimeError)


808
809
810
811
# File 'lib/ruby_units/unit.rb', line 808

def to_c
  return Complex(@scalar) if self.unitless?
  raise RuntimeError, "Cannot convert '#{self.to_s}' to Complex unless unitless.  Use Unit#scalar"
end

#to_dateObject



925
926
927
# File 'lib/ruby_units/unit.rb', line 925

def to_date
  Date.new0(self.convert_to('d').scalar)
end

#to_datetimeObject

convert a duration to a DateTime. This will work so long as the duration is the duration from the zero date defined by DateTime



921
922
923
# File 'lib/ruby_units/unit.rb', line 921

def to_datetime
  DateTime.new!(self.convert_to('d').scalar)
end

#to_fObject

converts the unit back to a float if it is unitless. Otherwise raises an exception

Raises:

  • (RuntimeError)


802
803
804
805
# File 'lib/ruby_units/unit.rb', line 802

def to_f
  return @scalar.to_f if self.unitless?
  raise RuntimeError, "Cannot convert '#{self.to_s}' to Float unless unitless.  Use Unit#scalar"
end

#to_iObject Also known as: to_int

if unitless, returns an int, otherwise raises an error

Raises:

  • (RuntimeError)


814
815
816
817
# File 'lib/ruby_units/unit.rb', line 814

def to_i
  return @scalar.to_int if self.unitless?
  raise RuntimeError, "Cannot convert '#{self.to_s}' to Integer unless unitless.  Use Unit#scalar"
end

#to_rObject

if unitless, returns a Rational, otherwise raises an error

Raises:

  • (RuntimeError)


821
822
823
824
# File 'lib/ruby_units/unit.rb', line 821

def to_r
  return @scalar.to_r if self.unitless?
  raise RuntimeError, "Cannot convert '#{self.to_s}' to Rational unless unitless.  Use Unit#scalar"
end

#to_s(target_units = nil) ⇒ Object

Generate human readable output. If the name of a unit is passed, the unit will first be converted to the target unit before output. some named conversions are available

:ft - outputs in feet and inches (e.g., 6'4")
:lbs - outputs in pounds and ounces (e.g, 8 lbs, 8 oz)

You can also pass a standard format string (i.e., ‘%0.2f’) or a strftime format string.

output is cached so subsequent calls for the same format will be fast



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/ruby_units/unit.rb', line 386

def to_s(target_units=nil)
  out = @output[target_units]
  if out
    return out
  else
    case target_units
    when :ft
      inches = self.convert_to("in").scalar.to_int
      out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
    when :lbs
      ounces = self.convert_to("oz").scalar.to_int
      out = "#{(ounces / 16).truncate} lbs, #{(ounces % 16).round} oz"
    when String
      out = case target_units
      when /(%[\-+\.\w#]+)\s*(.+)*/       #format string like '%0.2f in'
        begin
          if $2 #unit specified, need to convert
            self.convert_to($2).to_s($1)
          else 
            "#{$1 % @scalar} #{$2 || self.units}".strip
          end
        rescue
          (DateTime.new(0) + self).strftime(target_units)  
        end
      when /(\S+)/ #unit only 'mm' or '1/mm'
        "#{self.convert_to($1).to_s}"
      else
        raise "unhandled case"
      end
    else
      out = case @scalar
      when Rational
        "#{@scalar} #{self.units}"
      else
        "#{'%g' % @scalar} #{self.units}"
      end.strip
    end
    @output[target_units] = out
    return out
  end    
end

#to_timeObject Also known as: time

Tries to make a Time object from current unit. Assumes the current unit hold the duration in seconds from the epoch.



913
914
915
# File 'lib/ruby_units/unit.rb', line 913

def to_time
  Time.at(self)
end

#to_unitObject Also known as: unit



302
303
304
# File 'lib/ruby_units/unit.rb', line 302

def to_unit
  self
end

#to_yaml(opts = {}) ⇒ Object



178
179
180
181
182
183
184
185
186
# File 'lib/ruby_units/unit.rb', line 178

def to_yaml( opts = {} )
  YAML::quick_emit( object_id, opts ) do |out|
    out.map( taguri, to_yaml_style ) do |map|
      for m in to_yaml_properties do 
        map.add( m[1..-1], instance_variable_get( m ) )
      end
    end
  end
end

#to_yaml_propertiesObject



155
156
157
# File 'lib/ruby_units/unit.rb', line 155

def to_yaml_properties
  %w{@scalar @numerator @denominator @signature @base_scalar}
end

#truncateObject



891
892
893
894
# File 'lib/ruby_units/unit.rb', line 891

def truncate
  return @scalar.truncate if self.unitless?
  Unit.new(@scalar.truncate, @numerator, @denominator)    
end

#unitless?Boolean

returns true if no associated units false, even if the units are “unitless” like ‘radians, each, etc’

Returns:

  • (Boolean)


456
457
458
# File 'lib/ruby_units/unit.rb', line 456

def unitless?
  (@numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY)
end

#unitsObject

returns the ‘unit’ part of the Unit object without the scalar



827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
# File 'lib/ruby_units/unit.rb', line 827

def units
  return "" if @numerator == UNITY_ARRAY && @denominator == UNITY_ARRAY
  return @unit_name unless @unit_name.nil?
  output_n = []
  output_d =[] 
  num = @numerator.clone.compact
  den = @denominator.clone.compact
  if @numerator == UNITY_ARRAY
    output_n << "1"
  else
    num.each_with_index do |token,index|
      if token && @@PREFIX_VALUES[token] then
        output_n << "#{@@OUTPUT_MAP[token]}#{@@OUTPUT_MAP[num[index+1]]}"
        num[index+1]=nil
      else
        output_n << "#{@@OUTPUT_MAP[token]}" if token
      end
    end
  end
  if @denominator == UNITY_ARRAY
    output_d = ['1']
  else
    den.each_with_index do |token,index|
      if token && @@PREFIX_VALUES[token] then
        output_d << "#{@@OUTPUT_MAP[token]}#{@@OUTPUT_MAP[den[index+1]]}"
        den[index+1]=nil
      else
        output_d << "#{@@OUTPUT_MAP[token]}" if token
      end
    end
  end
  on = output_n.reject {|x| x.empty?}.map {|x| [x, output_n.find_all {|z| z==x}.size]}.uniq.map {|x| ("#{x[0]}".strip+ (x[1] > 1 ? "^#{x[1]}" : ''))}
  od = output_d.reject {|x| x.empty?}.map {|x| [x, output_d.find_all {|z| z==x}.size]}.uniq.map {|x| ("#{x[0]}".strip+ (x[1] > 1 ? "^#{x[1]}" : ''))}
  out = "#{on.join('*')}#{od == ['1'] ? '': '/'+od.join('*')}".strip    
  @unit_name = out unless self.kind == :temperature
  return out
end

#until(time_point) ⇒ Object

‘min’.until(time)



964
965
966
967
968
969
970
971
972
973
# File 'lib/ruby_units/unit.rb', line 964

def until(time_point)
  case time_point
  when Time
    (time_point - Time.now).unit('s').convert_to(self)
  when DateTime, Date
    (time_point - DateTime.now).unit('d').convert_to(self)
  else
    raise ArgumentError, "Must specify a Time, Date, or DateTime" 
  end
end

#zero?Boolean

true if scalar is zero

Returns:

  • (Boolean)


931
932
933
# File 'lib/ruby_units/unit.rb', line 931

def zero?
  return self.base_scalar.zero?
end