Class: Unit

Inherits:
Numeric show all
Includes:
Comparable
Defined in:
lib/rails_units/unit.rb,
lib/rails_units/cache.rb,
lib/rails_units/version.rb,
lib/rails_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, :capacitance]
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>}],
  '<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>}],

  #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>}],
  '<temp-K>'  => [%w{tempK}, 1, :temperature, %w{<temp-K>}],
  '<temp-C>'  => [%w{tempC}, 1, :temperature, %w{<temp-K>}],
  '<temp-F>'  => [%w{tempF}, Rational(1,1.8), :temperature, %w{<temp-K>}],
  '<temp-R>'  => [%w{tempR}, Rational(1,1.8), :temperature, %w{<temp-K>}],

  #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>}],
  '<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{<farad>}],

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


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
258
259
260
261
262
263
264
# File 'lib/rails_units/unit.rb', line 191

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.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def base_denominator
  @base_denominator
end

#base_numeratorObject

Returns the value of attribute base_numerator.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def base_numerator
  @base_numerator
end

#base_scalarObject

Returns the value of attribute base_scalar.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def base_scalar
  @base_scalar
end

#denominatorObject

Returns the value of attribute denominator.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def denominator
  @denominator
end

#numeratorObject

Returns the value of attribute numerator.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def numerator
  @numerator
end

#outputObject

Returns the value of attribute output.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def output
  @output
end

#scalarObject

Returns the value of attribute scalar.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def scalar
  @scalar
end

#signatureObject

Returns the value of attribute signature.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def signature
  @signature
end

#unit_nameObject

Returns the value of attribute unit_name.



143
144
145
# File 'lib/rails_units/unit.rb', line 143

def unit_name
  @unit_name
end

Class Method Details

.base_unit_cacheObject



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

def self.base_unit_cache
  return @@base_unit_cache
end

.cachedObject



270
271
272
# File 'lib/rails_units/unit.rb', line 270

def self.cached
  return @@cached_units
end

.clear_cacheObject



274
275
276
277
278
# File 'lib/rails_units/unit.rb', line 274

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

.parse(input) ⇒ Object

parse strings like “1 minute in seconds”



287
288
289
290
# File 'lib/rails_units/unit.rb', line 287

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



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

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



634
635
636
# File 'lib/rails_units/unit.rb', line 634

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

#*(other) ⇒ Object

Multiply two units.



587
588
589
590
591
592
593
594
595
596
597
598
599
600
# File 'lib/rails_units/unit.rb', line 587

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)


646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
# File 'lib/rails_units/unit.rb', line 646

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



526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/rails_units/unit.rb', line 526

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.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
  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.



557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
# File 'lib/rails_units/unit.rb', line 557

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



868
869
870
871
# File 'lib/rails_units/unit.rb', line 868

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

#/(other) ⇒ Object

Divide two units. Throws an exception if divisor is 0



604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/rails_units/unit.rb', line 604

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.



452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/rails_units/unit.rb', line 452

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.



473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/rails_units/unit.rb', line 473

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



510
511
512
513
514
515
516
517
518
# File 'lib/rails_units/unit.rb', line 510

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/


493
494
495
496
497
498
499
500
501
# File 'lib/rails_units/unit.rb', line 493

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

#absObject



873
874
875
876
# File 'lib/rails_units/unit.rb', line 873

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

#agoObject

‘5 min’.unit.ago



938
939
940
# File 'lib/rails_units/unit.rb', line 938

def ago
  self.before
end

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

‘5 min’.before(time)

Raises:

  • (ArgumentError)


943
944
945
946
947
948
949
950
# File 'lib/rails_units/unit.rb', line 943

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



878
879
880
881
# File 'lib/rails_units/unit.rb', line 878

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



997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/rails_units/unit.rb', line 997

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



155
156
157
158
159
160
161
162
163
# File 'lib/rails_units/unit.rb', line 155

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)


624
625
626
627
628
629
630
631
# File 'lib/rails_units/unit.rb', line 624

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



883
884
885
886
# File 'lib/rails_units/unit.rb', line 883

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)


982
983
984
985
986
987
988
989
# File 'lib/rails_units/unit.rb', line 982

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’



419
420
421
422
# File 'lib/rails_units/unit.rb', line 419

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

#inverseObject

returns inverse of Unit (1/unit)



715
716
717
# File 'lib/rails_units/unit.rb', line 715

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)


298
299
300
301
302
303
304
305
306
# File 'lib/rails_units/unit.rb', line 298

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)


431
432
433
# File 'lib/rails_units/unit.rb', line 431

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)


425
426
427
# File 'lib/rails_units/unit.rb', line 425

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

#kindObject



266
267
268
# File 'lib/rails_units/unit.rb', line 266

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)


151
152
153
# File 'lib/rails_units/unit.rb', line 151

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)


671
672
673
674
675
676
677
678
679
680
681
682
# File 'lib/rails_units/unit.rb', line 671

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)


908
909
910
911
# File 'lib/rails_units/unit.rb', line 908

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)


686
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
# File 'lib/rails_units/unit.rb', line 686

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



888
889
890
891
# File 'lib/rails_units/unit.rb', line 888

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

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

‘min’.since(time)



954
955
956
957
958
959
960
961
962
963
964
965
# File 'lib/rails_units/unit.rb', line 954

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


900
901
902
903
# File 'lib/rails_units/unit.rb', line 900

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’



438
439
440
441
442
# File 'lib/rails_units/unit.rb', line 438

def temperature_scale
  return nil unless self.is_temperature?
  self.units =~ /temp([CFRK])/
  "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.



734
735
736
737
738
739
740
741
742
743
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
798
799
# File 'lib/rails_units/unit.rb', line 734

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[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
    
    # eliminate common terms
    
    (_numerator1 & _denominator2).each do |common|
      _numerator1.delete(common)
      _denominator2.delete(common)
    end
    
    (_numerator2 & _denominator1).each do |common|
      _numerator1.delete(common)
      _denominator2.delete(common)
    end
          
    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

#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



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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/rails_units/unit.rb', line 311

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.to('tempK')
    when self.is_degree?
      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)


810
811
812
813
# File 'lib/rails_units/unit.rb', line 810

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



927
928
929
# File 'lib/rails_units/unit.rb', line 927

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



923
924
925
# File 'lib/rails_units/unit.rb', line 923

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)


804
805
806
807
# File 'lib/rails_units/unit.rb', line 804

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)


816
817
818
819
# File 'lib/rails_units/unit.rb', line 816

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)


823
824
825
826
# File 'lib/rails_units/unit.rb', line 823

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



376
377
378
379
380
381
382
383
384
385
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
# File 'lib/rails_units/unit.rb', line 376

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.



915
916
917
# File 'lib/rails_units/unit.rb', line 915

def to_time
  Time.at(self)
end

#to_unitObject Also known as: unit



292
293
294
# File 'lib/rails_units/unit.rb', line 292

def to_unit
  self
end

#to_yaml(opts = {}) ⇒ Object



168
169
170
171
172
173
174
175
176
# File 'lib/rails_units/unit.rb', line 168

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



145
146
147
# File 'lib/rails_units/unit.rb', line 145

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

#truncateObject



893
894
895
896
# File 'lib/rails_units/unit.rb', line 893

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)


446
447
448
# File 'lib/rails_units/unit.rb', line 446

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

#unitsObject

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



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
864
865
# File 'lib/rails_units/unit.rb', line 829

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)



968
969
970
971
972
973
974
975
976
977
978
979
# File 'lib/rails_units/unit.rb', line 968

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)


933
934
935
# File 'lib/rails_units/unit.rb', line 933

def zero?
  return self.base_scalar.zero?
end