Class: Unit

Inherits:
Numeric show all
Includes:
Comparable
Defined in:
lib/ruby_units/ruby-units.rb,
lib/ruby_units/units.rb

Overview

Unit.setup

Constant Summary collapse

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}, 1e-1, :prefix],
  '<centi>'  => [%w{c Centi centi}, 1e-2, :prefix],
  '<milli>' =>  [%w{m Milli milli}, 1e-3, :prefix],
  '<micro>'  => [%w{u Micro micro}, 1e-6, :prefix],
  '<nano>'  =>  [%w{n Nano nano}, 1e-9, :prefix],
  '<pico>'  =>  [%w{p Pico pico}, 1e-12, :prefix],
  '<femto>' =>  [%w{f Femto femto}, 1e-15, :prefix],
  '<atto>'  =>  [%w{a Atto atto}, 1e-18, :prefix],
  '<zepto>' =>  [%w{z Zepto zepto}, 1e-21, :prefix],
  '<yocto>' =>  [%w{y Yocto yocto}, 1e-24, :prefix],
  '<1>'     =>  [%w{1},1,:prefix],

  # length units
  '<meter>' =>  [%w{m meter meters metre metres}, 1.0, :length, %w{<meter>} ],
  '<inch>'  =>  [%w{in inch inches "}, 0.0254, :length, %w{<meter>}],
  '<foot>'  =>  [%w{ft foot feet '}, 0.3048, :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}, 1e-10, :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.0, :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 #}, 0.45359237, :mass, %w{<kilogram>}],
  '<ounce>' => [%w{oz ounce ounces}, 0.0283495231, :mass, %w{<kilogram>}],
  '<gram>'    =>  [%w{g gram grams gramme grammes},1e-3,: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}, 0.001, :volume, %w{<meter> <meter> <meter>}],
  '<barrel>' => [%w{bbl bbls barrel barrels}, 0.00852167911, :volume, %w{<meter> <meter> <meter>}],
  '<keg>' => [%w{keg kegs}, 0.0170433582, :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>}],

  #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.0, :temperature, %w{<kelvin>}],
  '<celsius>' => [%w{degC celsius celsius centigrade}, 1.0, :temperature, %w{<kelvin>}],
  '<fahrenheit>' => [%w{degF fahrenheit}, 1/1.8, :temperature, %w{<kelvin>}],
  '<rankine>' => [%w{degR rankine}, 1/1.8, :temperature, %w{<kelvin>}],
  '<temp-K>'  => [%w{tempK}, 1.0, :temperature, %w{<temp-K>}],
  '<temp-C>'  => [%w{tempC}, 1.0, :temperature, %w{<temp-K>}],
  '<temp-F>'  => [%w{tempF}, 1/1.8, :temperature, %w{<temp-K>}],
  '<temp-R>'  => [%w{tempR}, 1/1.8, :temperature, %w{<temp-K>}],
  
  #time
  '<second>'=>  [%w{s sec second seconds}, 1.0, :time, %w{<second>}],
  '<minute>'=>  [%w{min minute minutes}, 60.0, :time, %w{<second>}],  
  '<hour>'=>  [%w{h hr hrs hour hours}, 3600.0, :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.0, :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}, 0.1, :viscosity, %w{<kilogram>},%w{<meter> <second>} ],
  '<stokes>' => [%w{St stokes}, 1e-4, :viscosity, %w{<meter> <meter>}, %w{<second>}],

  #substance
  '<mole>'  =>  [%w{mol mole}, 1.0, :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.0, :activity, %w{<mole>}, %w{<second>}],
  '<unit>'  =>  [%w{U enzUnit}, 16.667e-16, :activity, %w{<mole>}, %w{<second>}],

  #capacitance
  '<farad>' =>  [%w{F farad Farad}, 1.0, :capacitance, %w{<farad>}],

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

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

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

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

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

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

  #magnetism
  '<weber>' => [%w{Wb weber webers}, 1.0, :magnetism, %w{<meter> <meter> <kilogram>}, %w{<second> <second> <ampere>}],
  '<tesla>'  => [%w{T tesla teslas}, 1.0, :magnetism, %w{<kilogram>}, %w{<second> <second> <ampere>}],
  '<gauss>' => [%w{G gauss}, 1e-4, :magnetism,  %w{<kilogram>}, %w{<second> <second> <ampere>}],
  '<maxwell>' => [%w{Mx maxwell maxwells}, 1e-8, :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.0, :energy, %w{<meter> <meter> <kilogram>}, %w{<second> <second>}],
  '<erg>'   =>  [%w{erg ergs}, 1e-7, :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.0, :force, %w{<kilogram> <meter>}, %w{<second> <second>}],
  '<dyne>'  => [%w{dyn dyne}, 1e-5, :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.0, :frequency, %w{<1>}, %{<second>}],

  #angle
  '<radian>' =>[%w{rad radian radian}, 1.0, :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.0, :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.0, :memory, %w{<byte>}],
  '<bit>'  =>[%w{b bit}, 0.125, :memory, %w{<byte>}],

  #currency
  '<dollar>'=>[%w{USD dollar}, 1.0, :currency, %w{<dollar>}],
  '<cents>' =>[%w{cents}, 0.01, :currency, %w{<dollar>}],

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

  #power
  '<watt>'  => [%w{W watt watts}, 1.0, :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.0, :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.0, :radiation, %w{<meter> <meter>}, %w{<second> <second>}],
  '<becquerel>' => [%w{Bq bequerel bequerels}, 1.0, :radiation, %w{<1>},%w{<second>}],
  '<curie>' => [%w{Ci curie curies}, 3.7e10, :radiation, %w{<1>},%w{<second>}],
  
  # rate
  '<cpm>' => [%w{cpm}, 1.0/60.0, :rate, %w{<count>},%w{<second>}],
  '<dpm>' => [%w{dpm}, 1.0/60.0, :rate, %w{<count>},%w{<second>}],
  '<bpm>' => [%w{bpm}, 1.0/60.0, :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.0, :counting, %w{<each>}],
  '<count>' => [%w{count}, 1.0, :counting, %w{<each>}],  
  '<base-pair>'  => [%w{bp}, 1.0, :counting, %w{<each>}],
  '<nucleotide>' => [%w{nt}, 1.0, :counting, %w{<each>}],
  '<molecule>' => [%w{molecule molecules}, 1.0, :counting, %w{<1>}],
  '<dozen>' =>  [%w{doz dz dozen},12.0,:prefix_only, %w{<each>}],
  '<percent>'=> [%w{% percent}, 0.01, :prefix_only, %w{<1>}],
  '<ppm>' =>  [%w{ppm},1e-6,:prefix_only, %w{<1>}],
  '<ppt>' =>  [%w{ppt},1e-9,:prefix_only, %w{<1>}],
  '<gross>' =>  [%w{gr gross},144.0, :prefix_only, %w{<dozen> <dozen>}],
  '<decibel>'  => [%w{dB decibel decibels}, 1.0, :logarithmic, %w{<decibel>}]


}
VERSION =

pre-generate hashes from unit definitions for performance.

'1.2.0.a'
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>']
SIGNATURE_VECTOR =
[:length, :time, :temperature, :mass, :current, :substance, :luminosity, :currency, :memory, :angle, :capacitance]
@@USER_DEFINITIONS =
{}
@@PREFIX_VALUES =
{}
@@PREFIX_MAP =
{}
@@UNIT_MAP =
{}
@@UNIT_VALUES =
{}
@@OUTPUT_MAP =
{}
@@BASE_UNITS =
['<meter>','<kilogram>','<second>','<mole>', '<farad>', '<ampere>','<radian>','<kelvin>','<temp-K>','<byte>','<dollar>','<candela>','<each>','<steradian>','<decibel>']
@@KINDS =
{
-312058=>:resistance, 
-312038=>:inductance, 
-152040=>:magnetism, 
-152038=>:magnetism, 
-152058=>:potential, 
-39=>:acceleration,
-38=>:radiation, 
-20=>:frequency, 
-19=>:speed, 
-18=>:viscosity, 
0=>:unitless, 
1=>:length, 
2=>:area, 
3=>:volume, 
20=>:time, 
400=>:temperature, 
7942=>:power, 
7959=>:pressure, 
7962=>:energy, 
7979=>:viscosity, 
7961=>:force, 
7997=>:mass_concentration,
8000=>:mass, 
159999=>:magnetism, 
160000=>:current, 
160020=>:charge, 
312058=>:resistance, 
3199980=>:activity, 
3199997=>:molar_concentration, 
3200000=>:substance, 
63999998=>:illuminance, 
64000000=>:luminous_power, 
1280000000=>:currency, 
25600000000=>:memory,
511999999980=>:angular_velocity, 
512000000000=>:angle, 
10240000000000=>:capacitance, 
}
@@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, or a hash 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)


190
191
192
193
194
195
196
197
198
199
200
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
# File 'lib/ruby_units/ruby-units.rb', line 190

def initialize(*options)
  @scalar = nil
  @base_scalar = nil
  @unit_name = nil
  @signature = nil
  @output = {}
  if options.size == 2
    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
    @scalar = options[0].ajd
    @numerator = ['<day>']
    @denominator = UNITY_ARRAY
  when ""
    raise ArgumentError, "No Unit Specified"
  when String
    parse(options[0])   
  else
    raise ArgumentError, "Invalid Unit Format"
  end
  self.update_base_scalar
  raise ArgumentError, "Temperature out of range" if self.is_temperature? &&  self.base_scalar < 0

  unary_unit = self.units || ""
  opt_units = options[0].scan(NUMBER_REGEX)[0][1] if String === options[0]
  unless @@cached_units.keys.include?(opt_units) || (opt_units =~ /(temp|deg(C|K|R|F))|(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
  unless @@cached_units.keys.include?(unary_unit) || (unary_unit =~ /(temp|deg)(C|K|R|F)/) 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.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def base_denominator
  @base_denominator
end

#base_numeratorObject

Returns the value of attribute base_numerator.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def base_numerator
  @base_numerator
end

#base_scalarObject

Returns the value of attribute base_scalar.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def base_scalar
  @base_scalar
end

#denominatorObject

Returns the value of attribute denominator.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def denominator
  @denominator
end

#numeratorObject

Returns the value of attribute numerator.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def numerator
  @numerator
end

#outputObject

Returns the value of attribute output.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def output
  @output
end

#scalarObject

Returns the value of attribute scalar.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def scalar
  @scalar
end

#signatureObject

Returns the value of attribute signature.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def signature
  @signature
end

#unit_nameObject

Returns the value of attribute unit_name.



142
143
144
# File 'lib/ruby_units/ruby-units.rb', line 142

def unit_name
  @unit_name
end

Class Method Details

.base_unit_cacheObject



273
274
275
# File 'lib/ruby_units/ruby-units.rb', line 273

def self.base_unit_cache
  return @@base_unit_cache
end

.cachedObject



263
264
265
# File 'lib/ruby_units/ruby-units.rb', line 263

def self.cached
  return @@cached_units
end

.clear_cacheObject



267
268
269
270
271
# File 'lib/ruby_units/ruby-units.rb', line 267

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

.parse(input) ⇒ Object



277
278
279
280
# File 'lib/ruby_units/ruby-units.rb', line 277

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

.setupObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ruby_units/ruby-units.rb', line 115

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

Multiply two units.



547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/ruby_units/ruby-units.rb', line 547

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)


601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
# File 'lib/ruby_units/ruby-units.rb', line 601

def **(other)
  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
  if Numeric === other
    return Unit("1") if other.zero?
    return self if other == 1
    return self.inverse if other == -1
  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)
  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



488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/ruby_units/ruby-units.rb', line 488

def +(other)   
  if Unit === other 
    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.to(self.temperature_scale).scalar), :numerator => @numerator, :denominator=>@denominator, :signature => @signature)
        else
          Unit.new(:scalar => (other.scalar + self.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
  elsif Time === other
    other + self
  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.



518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/ruby_units/ruby-units.rb', line 518

def -(other)
  if Unit === other 
    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).to(self.temperature_scale) 
        when self.is_temperature?
          Unit.new(:scalar => (self.base_scalar - other.base_scalar), :numerator  => ['<temp-K>'], :denominator => UNITY_ARRAY, :signature => @signature).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
  elsif Time === other
    other - self
  else
      x,y = coerce(other)
      y-x
  end
end

#-@Object

negates the scalar of the Unit



796
797
798
799
# File 'lib/ruby_units/ruby-units.rb', line 796

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

#/(other) ⇒ Object

Divide two units. Throws an exception if divisor is 0



564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
# File 'lib/ruby_units/ruby-units.rb', line 564

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.



434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/ruby_units/ruby-units.rb', line 434

def <=>(other)
  case  
  when other.zero? && !self.is_temperature?
    return self.base_scalar <=> 0
  when Unit === other
    raise ArgumentError, "Incompatible Units" 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



472
473
474
475
476
477
478
479
480
# File 'lib/ruby_units/ruby-units.rb', line 472

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/


454
455
456
457
458
459
460
461
462
463
# File 'lib/ruby_units/ruby-units.rb', line 454

def =~(other)
  return true if self == 0 || other == 0
  case other
  when Unit
    self.signature == other.signature
  else
    x,y = coerce(other)
    x =~ y
  end 
end

#absObject

returns abs of scalar, without the units



802
803
804
# File 'lib/ruby_units/ruby-units.rb', line 802

def abs
  return @scalar.abs
end

#agoObject

‘5 min’.unit.ago



855
856
857
# File 'lib/ruby_units/ruby-units.rb', line 855

def ago
  self.before
end

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

‘5 min’.before(time)

Raises:

  • (ArgumentError)


860
861
862
863
864
865
866
867
# File 'lib/ruby_units/ruby-units.rb', line 860

def before(time_point = ::Time.now)
  raise ArgumentError, "Must specify a Time" unless time_point
  if String === time_point
    time_point.time - self rescue time_point.datetime - self
  else
    time_point - self rescue time_point.to_datetime - self
  end
end

#ceilObject



806
807
808
809
# File 'lib/ruby_units/ruby-units.rb', line 806

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



920
921
922
923
924
925
926
927
928
929
930
# File 'lib/ruby_units/ruby-units.rb', line 920

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

#copy(from) ⇒ Object



154
155
156
157
158
159
160
161
162
# File 'lib/ruby_units/ruby-units.rb', line 154

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)


584
585
586
587
588
589
590
591
# File 'lib/ruby_units/ruby-units.rb', line 584

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



811
812
813
814
# File 'lib/ruby_units/ruby-units.rb', line 811

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

#from(time_point = ::Time.now) ⇒ Object Also known as: after, from_now

‘5 min’.from(time)

Raises:

  • (ArgumentError)


899
900
901
902
903
904
905
906
# File 'lib/ruby_units/ruby-units.rb', line 899

def from(time_point = ::Time.now)
  raise ArgumentError, "Must specify a Time" unless time_point
  if String === time_point
    time_point.time + self rescue time_point.datetime + self
  else
    time_point + self rescue time_point.to_datetime + self
  end
end

#inspect(option = nil) ⇒ Object

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



401
402
403
404
# File 'lib/ruby_units/ruby-units.rb', line 401

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

#inverseObject

returns inverse of Unit (1/unit)



668
669
670
# File 'lib/ruby_units/ruby-units.rb', line 668

def inverse
  Unit("1") / self
end

#is_base?Boolean

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

Returns:

  • (Boolean)


288
289
290
291
292
293
294
295
296
# File 'lib/ruby_units/ruby-units.rb', line 288

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)


413
414
415
# File 'lib/ruby_units/ruby-units.rb', line 413

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)


407
408
409
# File 'lib/ruby_units/ruby-units.rb', line 407

def is_temperature?
  self.is_degree? && self.units =~ /temp/
end

#kindObject



259
260
261
# File 'lib/ruby_units/ruby-units.rb', line 259

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)


150
151
152
# File 'lib/ruby_units/ruby-units.rb', line 150

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)


624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/ruby_units/ruby-units.rb', line 624

def power(n)
  raise ArgumentError, "Cannot raise a temperature to a power" if self.is_temperature?
  raise ArgumentError, "Can only use Integer exponenents" unless Integer === n
  return self if n == 1
  return Unit("1") if n == 0
  return self.inverse 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

#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)


639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
# File 'lib/ruby_units/ruby-units.rb', line 639

def root(n)
  raise ArgumentError, "Cannot take the root of a temperature" if self.is_temperature?
  raise ArgumentError, "Exponent must an Integer" unless Integer === n
  raise ArgumentError, "0th root undefined" if n == 0
  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



844
845
846
847
# File 'lib/ruby_units/ruby-units.rb', line 844

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

#since(time_point = ::Time.now) ⇒ Object

‘min’.since(time)



871
872
873
874
875
876
877
878
879
880
881
882
# File 'lib/ruby_units/ruby-units.rb', line 871

def since(time_point = ::Time.now)
  case time_point
  when Time
    (Time.now - time_point).unit('s').to(self)
  when DateTime, Date
    (DateTime.now - time_point).unit('d').to(self)
  when String    
    (DateTime.now - time_point.to_datetime(:context=>:past)).unit('d').to(self)
  else
    raise ArgumentError, "Must specify a Time, DateTime, or String" 
  end
end

#succObject

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

Raises:

  • (ArgumentError)


912
913
914
915
916
# File 'lib/ruby_units/ruby-units.rb', line 912

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

#temperature_scaleObject

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



420
421
422
423
424
# File 'lib/ruby_units/ruby-units.rb', line 420

def temperature_scale
  return nil unless self.is_temperature?
  self.units =~ /temp(C|F|R|K)/
  "deg#{$1}"
end

#to(other) ⇒ Object Also known as: >>, convert_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.



687
688
689
690
691
692
693
694
695
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
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
# File 'lib/ruby_units/ruby-units.rb', line 687

def 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(K|C|R|F)/) 
    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)*(5.0/9.0)
      when 'tempR'
        @scalar*(5.0/9.0)
      end
    end
    q=  case target_unit
    when 'tempC'
      @base_scalar - 273.15
    when 'tempK'
      @base_scalar 
    when 'tempF'
      @base_scalar * (9.0/5.0) - 459.67
    when 'tempR'
      @base_scalar * (9.0/5.0) 
    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
    one = @numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
    two = @denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|i| i.kind_of?(Numeric) ? i : @@UNIT_VALUES[i][:scalar] }.compact
    v = one.inject(1) {|product,n| product*n} / two.inject(1) {|product,n| product*n}
    one = target.numerator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
    two = target.denominator.map {|x| @@PREFIX_VALUES[x] ? @@PREFIX_VALUES[x] : x}.map {|x| x.kind_of?(Numeric) ? x : @@UNIT_VALUES[x][:scalar] }.compact
    y = one.inject(1) {|product,n| product*n} / two.inject(1) {|product,n| product*n}
    q = @scalar * v/y
    Unit.new(:scalar=>q, :numerator=>target.numerator, :denominator=>target.denominator, :signature => target.signature)
  end
end

#to_baseObject

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



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/ruby_units/ruby-units.rb', line 300

def to_base
  return self if self.is_base?
  if self.units =~ /\A(deg|temp)(C|F|K|C)\Z/
    @signature = 400
    base = case self.units
    when /temp/
      self.to('tempK')
    when /deg/
      self.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)


751
752
753
754
# File 'lib/ruby_units/ruby-units.rb', line 751

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

#to_dateObject



840
841
842
# File 'lib/ruby_units/ruby-units.rb', line 840

def to_date
  Date.new0(self.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



836
837
838
# File 'lib/ruby_units/ruby-units.rb', line 836

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

#to_fObject

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

Raises:

  • (RuntimeError)


744
745
746
747
# File 'lib/ruby_units/ruby-units.rb', line 744

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

#to_iObject Also known as: to_int

if unitless, returns an int, otherwise raises an error

Raises:

  • (RuntimeError)


817
818
819
820
# File 'lib/ruby_units/ruby-units.rb', line 817

def to_i
  return @scalar.to_int if self.unitless?
  raise RuntimeError, 'Cannot convert to Integer unless unitless'
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



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/ruby_units/ruby-units.rb', line 358

def to_s(target_units=nil)
  out = @output[target_units]
  if out
    return out
  else
    case target_units
    when :ft
      inches = self.to("in").scalar.to_int
      out = "#{(inches / 12).truncate}\'#{(inches % 12).round}\""
    when :lbs
      ounces = self.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.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.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.



824
825
826
# File 'lib/ruby_units/ruby-units.rb', line 824

def to_time
  Time.at(self)
end

#to_unitObject Also known as: unit



282
283
284
# File 'lib/ruby_units/ruby-units.rb', line 282

def to_unit
  self
end

#to_yaml(opts = {}) ⇒ Object



167
168
169
170
171
172
173
174
175
# File 'lib/ruby_units/ruby-units.rb', line 167

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



144
145
146
# File 'lib/ruby_units/ruby-units.rb', line 144

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

#truncateObject



829
830
831
832
# File 'lib/ruby_units/ruby-units.rb', line 829

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)


428
429
430
# File 'lib/ruby_units/ruby-units.rb', line 428

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

#unitsObject

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



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
# File 'lib/ruby_units/ruby-units.rb', line 757

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 = ::Time.now) ⇒ Object

‘min’.until(time)



885
886
887
888
889
890
891
892
893
894
895
896
# File 'lib/ruby_units/ruby-units.rb', line 885

def until(time_point = ::Time.now)
  case time_point
  when Time
    (time_point - Time.now).unit('s').to(self)
  when DateTime, Date
    (time_point - DateTime.now).unit('d').to(self)
  when String
    (time_point.to_datetime(:context=>:future) - DateTime.now).unit('d').to(self)
  else
    raise ArgumentError, "Must specify a Time, DateTime, or String" 
  end
end

#zero?Boolean

true if scalar is zero

Returns:

  • (Boolean)


850
851
852
# File 'lib/ruby_units/ruby-units.rb', line 850

def zero?
  return self.to_base.scalar.zero?
end