class Quantify::Unit::Base

Attributes

acts_as_alternative_unit[R]
acts_as_equivalent_unit[R]
base_unit[RW]
dimensions[R]
factor[R]
label[R]
name[R]
prefix[RW]
symbol[R]

Public Class Methods

configure(&block) click to toggle source

Syntactic sugar for defining the units known to the system, enabling the required associated units to be loaded at runtime, e.g.

Unit::[Base|SI|NonSI].configure do |config|

  load :name => :metre, :physical_quantity => :length
  load :name => 'hectare', :physical_quantity => :area, :factor => 10000
  load :name => :watt, :physical_quantity => :power, :symbol => 'W'

end
# File lib/quantify/unit/base_unit.rb, line 70
def self.configure(&block)
  class_eval(&block) if block
end
construct(unit,&block) click to toggle source

Define a new unit in terms of an already instantiated compound unit. This unit becomes a representation of the compound - without explicitly holding the base units, e.g.

Unit::Base.construct(Unit.m**2).name           #=> "square metre"

Unit::Base.construct(Unit.m**3) do |unit|
  unit.name = "metres cubed"
end.name                                       #=> "metres cubed"
# File lib/quantify/unit/base_unit.rb, line 53
def self.construct(unit,&block)
  new_unit = self.new unit.to_hash
  block.call(new_unit) if block_given?
  return new_unit
end
construct_and_load(unit,&block) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 17
def self.construct_and_load(unit,&block)
  self.construct(unit, &block).load
end
initialize_prefixed_version(prefix,unit) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 21
def self.initialize_prefixed_version(prefix,unit)
  prefix, unit = Prefix.for(prefix), Unit.for(unit)
  raise Exceptions::InvalidArgumentError, "Prefix is not known" if prefix.nil?
  raise Exceptions::InvalidArgumentError, "Unit is not known" if unit.nil?
  raise Exceptions::InvalidArgumentError, "Cannot add prefix where one already exists: #{unit.prefix.name}" if unit.prefix
  self.new &self.block_for_prefixed_version(prefix,unit)
end
load(options=nil,&block) click to toggle source

Create a new instance of self (i.e. Base or an inherited class) and load into the system of known units. See initialize for details of options

# File lib/quantify/unit/base_unit.rb, line 13
def self.load(options=nil,&block)
  self.new(options,&block).load
end
new(options=nil,&block) click to toggle source

Create a new Unit::Base instance.

Valid options are: :name => The unit name, e.g. :kilometre

:dimensions        => The physical quantity represented
                      by the unit (e.g. force, mass).
                      This must be recognised as a member
                      of the Dimensions.dimensions array

:physical_quantity => Alias for :dimensions

:symbol            => The unit symbol, e.g. 'kg'

:factor            => The factor which relates the unit
                      to the SI unit for the same physical
                      quantity. For example the :factor for
                      a foot would be 0.3048, since a foot
                      = 0.3048 m (metre is the SI unit of
                      length). If no factor is set, it is
                      assumed to be 1 - which represents
                      an SI benchmark unit.

:scaling           => A scaling factor, used only by NonSI
                      temperature units

:label             => The label used by JScience for the
                      unit

The physical quantity option is used to locate the corresponding dimensional representation in the Dimensions class. This dimensions attribute is to provide much of the unit functionality

# File lib/quantify/unit/base_unit.rb, line 110
def initialize(options=nil,&block)
  @acts_as_alternative_unit = true
  @acts_as_equivalent_unit = false
  self.factor = 1.0
  self.symbol = nil
  self.label = nil
  self.name = nil
  self.base_unit = nil
  self.prefix = nil
  if options.is_a? Hash
    self.dimensions = options[:dimensions] || options[:physical_quantity] if options[:dimensions] || options[:physical_quantity] 
    self.factor = options[:factor] if options[:factor]
    self.name = options[:name] if options[:name]
    self.symbol = options[:symbol] if options[:symbol]
    self.label = options[:label] if options[:label]
  end
  block.call(self) if block_given?
  valid?
end
prefix_and_load(prefixes,units) click to toggle source

Mass load prefixed units. First argument is a single or array of units. Second argument is a single or array of prefixes. All specfied units will be loaded with all specified prefixes.

# File lib/quantify/unit/base_unit.rb, line 33
def self.prefix_and_load(prefixes,units)
  [units].flatten.each do |unit|
    unit = Unit.for(unit)
    [prefixes].flatten.each do |prefix|
      prefixed_unit = unit.with_prefix(prefix) rescue unit
      prefixed_unit.load unless prefixed_unit.loaded?
    end
  end
end

Private Class Methods

block_for_prefixed_version(prefix,unit) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 552
def self.block_for_prefixed_version(prefix,unit)
  return Proc.new do |new_unit| 
    new_unit.base_unit  = unit.clone
    new_unit.prefix     = prefix
    new_unit.dimensions = unit.dimensions.clone
    new_unit.name       = "#{prefix.name}#{unit.name}"
    new_unit.factor     = prefix.factor * unit.factor
    new_unit.symbol     = "#{prefix.symbol}#{unit.symbol}"
    new_unit.label      = "#{prefix.symbol}#{unit.label}"
  end
end

Public Instance Methods

*(other)
Alias for: multiply
**(power)
Alias for: pow
/(other)
Alias for: divide
==(other)
acts_as_alternative_unit=(value) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 255
def acts_as_alternative_unit=(value)
  @acts_as_alternative_unit = (value == (true||false) ? value : false)
  make_canonical
end
acts_as_equivalent_unit=(value) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 260
def acts_as_equivalent_unit=(value)
  @acts_as_equivalent_unit = (value == (true||false) ? value : false)
  make_canonical
end
alternatives(by=nil) click to toggle source

List the alternative units for self, i.e. the other units which share the same dimensions.

The list can be returned containing the alternative unit names, symbols or JScience labels by providing the required format as a symbolized argument.

If no format is provide, the full unit objects for all alternative units are returned within the array

# File lib/quantify/unit/base_unit.rb, line 411
def alternatives(by=nil)
  @dimensions.units(nil).reject do |unit|
    unit.is_equivalent_to?(self) || !unit.acts_as_alternative_unit
  end.map(&by).to_a
end
canonical_label=(new_label) click to toggle source

Set the canonical unit label - the unique unit identifier - to a new value

# File lib/quantify/unit/base_unit.rb, line 249
def canonical_label=(new_label)
  unload if loaded?
  self.label = new_label
  load
end
coerce(object) click to toggle source

Enables shorthand for reciprocal of a unit, e.g.

unit = Unit.m

(1/unit).symbol                     #=> "m^-1"
# File lib/quantify/unit/base_unit.rb, line 529
def coerce(object)
  if object.kind_of?(Numeric) && object == 1
    return Unit.unity, self
  else
    raise Exceptions::InvalidArgumentError, "Cannot coerce #{self.class} into #{object.class}"
  end
end
configure(&block) click to toggle source

Permits a block to be used, operating on self. This is useful for modifying the attributes of an already instantiated unit, especially when defining units on the basis of operation on existing units for adding specific (rather than derived) names or symbols, e.g.

(Unit.pound_force/(Unit.in**2)).configure do |unit|
  unit.symbol = 'psi'
  unit.label = 'psi'
  unit.name = 'pound per square inch'
end
# File lib/quantify/unit/base_unit.rb, line 206
def configure(&block)
  block.call(self) if block_given?
  return self if valid?
end
configure_as_canonical(&block) click to toggle source

Similar to configure but makes the new unit configuration the canonical unit for self.label

# File lib/quantify/unit/base_unit.rb, line 214
def configure_as_canonical(&block)
  unload if loaded?
  configure(&block) if block_given?
  make_canonical
end
dimensions=(dimensions) click to toggle source

Set the dimensions attribute of self. Valid arguments passed in are (1) instances of the Dimensions class; or (2) string or symbol matching the physical quantity attribute of a known Dimensions object.

# File lib/quantify/unit/base_unit.rb, line 134
def dimensions=(dimensions)
  if dimensions.is_a? Dimensions
    @dimensions = dimensions
  elsif dimensions.is_a?(String) || dimensions.is_a?(Symbol)
    @dimensions = Dimensions.for(dimensions)
  else
    raise Exceptions::InvalidArgumentError, "Unknown physical_quantity specified"
  end
end
Also aliased as: physical_quantity=
divide(other) click to toggle source

Divide one unit by another. This results in the generation of a compound unit.

In the event that the new unit represents a known unit, the non-compound representation is returned (i.e. of the SI or NonSI class).

# File lib/quantify/unit/base_unit.rb, line 459
def divide(other)
  options = []
  self.instance_of?(Unit::Compound) ? options += @base_units : options << self

  if other.instance_of? Unit::Compound
    options += other.base_units.map { |base| base.index *= -1; base }
  else
    options << CompoundBaseUnit.new(other,-1)
  end
  Unit::Compound.new(*options)
end
Also aliased as: /
factor=(factor) click to toggle source
# File lib/quantify/unit/base_unit.rb, line 191
def factor=(factor)
  @factor = factor.to_f
end
has_same_identity_as?(other) click to toggle source

Check if unit has the identity as another, i.e. the same label. This is used to determine if a unit with the same accessors already exists in the module variable @@units

# File lib/quantify/unit/base_unit.rb, line 382
def has_same_identity_as?(other)
  @label == other.label && !@label.nil?
end
Also aliased as: ==
has_scaling?() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 274
def has_scaling?
  scaling != 0.0
end
is_alternative_for?(other) click to toggle source

Determine if another unit is an alternative unit for self, i.e. do the two units represent the same physical quantity. This is established by compraing their dimensions attributes. E.g.

Unit.metre.is_alternative_for? Unit.foot    #=> true

Unit.metre.is_alternative_for? Unit.gram    #=> false

Unit.metre.is_alternative_for? Unit.metre   #=> true
# File lib/quantify/unit/base_unit.rb, line 397
def is_alternative_for?(other)
  other.dimensions == @dimensions
end
is_base_quantity_si_unit?() click to toggle source

Determine if the unit is THE canonical SI unit for a base quantity (length, mass, time, etc.). This method ignores prefixed versions of SI base units, returning true only for metre, kilogram, second, Kelvin, etc.

# File lib/quantify/unit/base_unit.rb, line 305
def is_base_quantity_si_unit?
  is_si_unit? && is_base_unit? && is_benchmark_unit?
end
is_base_unit?() click to toggle source

Determine if the unit represents one of the base quantities, length, mass, time, temperature, etc.

# File lib/quantify/unit/base_unit.rb, line 297
def is_base_unit?
  Dimensions::BASE_QUANTITIES.map {|base| base.remove_underscores }.include? self.measures
end
is_benchmark_unit?() click to toggle source

Determine if the unit is one of the units against which all other units of the same physical quantity are defined. These units are almost entirely equivalent to the non-prefixed, SI units, but the one exception is the kilogram, which is an oddity in being THE canonical SI unit for mass, yet containing a prefix. This oddity makes this method useful/necessary.

# File lib/quantify/unit/base_unit.rb, line 327
def is_benchmark_unit?
  @factor == 1.0
end
is_compound_unit?() click to toggle source

Determine is a unit object represents an compound unit consisting of SI or non-SI named units

# File lib/quantify/unit/base_unit.rb, line 343
def is_compound_unit?
  self.is_a? Compound
end
is_derived_unit?() click to toggle source

Determine is the unit is a derived unit - that is, a unit made up of more than one of the base quantities

# File lib/quantify/unit/base_unit.rb, line 312
def is_derived_unit?
  !is_base_unit?
end
is_dimensionless?() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 347
def is_dimensionless?
  @dimensions.is_dimensionless?
end
is_equivalent_to?(other) click to toggle source

Determine if self is equivalent to another. Equivalency is based on representing the same physical quantity (i.e. dimensions) and the same factor and scaling values.

Unit.metre.is_equivalent_to? Unit.foot    #=> false

Unit.metre.is_equivalent_to? Unit.gram    #=> false

Unit.metre.is_equivalent_to? Unit.metre   #=> true

The base_units attr of Compound units are not compared. Neither are the names or symbols. This is because we want to recognise cases where units derived from operations and defined as compound units (therefore having compounded names and symbols) are the same as known, named units. For example, if we build a unit for energy using only SI units, we want to recognise this as a joule, rather than a kg m^2 s^-2, e.g.

(Unit.kg*Unit.m*Unit.m/Unit.s/Unit.s).is_equivalent_to? Unit.joule

                                   #=> true
# File lib/quantify/unit/base_unit.rb, line 372
def is_equivalent_to?(other)
  [:dimensions,:factor,:scaling].all? do |attr|
    self.send(attr) == other.send(attr)
  end
end
is_non_si_unit?() click to toggle source

Determine is a unit object represents an NonSI named unit

# File lib/quantify/unit/base_unit.rb, line 337
def is_non_si_unit?
  self.is_a? NonSI
end
is_prefixed_unit?() click to toggle source

Determine if the unit is a prefixed unit

# File lib/quantify/unit/base_unit.rb, line 317
def is_prefixed_unit?
  self.prefix ? true : false
end
is_si_unit?() click to toggle source

Determine is a unit object represents an SI named unit

# File lib/quantify/unit/base_unit.rb, line 332
def is_si_unit?
  self.is_a? SI
end
label=(label) click to toggle source

Set the label attribute of self. This represents the unique identifier for the unit, and follows JScience for many standard units and general formatting (e.g. underscores, forward slashes, middots).

Labels are stringified and superscripts are formatted according to the configuration found in Quantify.use_superscript_characters?

Labels are a unique consistent reference and are therefore case senstive.

# File lib/quantify/unit/base_unit.rb, line 177
def label=(label)
  label = label.to_s.gsub(" ","_")
  @label = Unit.use_superscript_characters? ? label.with_superscript_characters : label.without_superscript_characters
end
load(&block) click to toggle source

Load an initialized Unit into the system of known units.

If a block is given, the unit can be configured prior to loading, in a similar to way to the configure method.

# File lib/quantify/unit/base_unit.rb, line 225
def load(&block)
  block.call(self) if block_given?
  raise Exceptions::InvalidArgumentError, "A unit with the same label: #{self.name}) already exists" if loaded?
  Quantify::Unit.units << self if valid?
  return self
end
loaded?() click to toggle source

check if an object with the same label already exists

# File lib/quantify/unit/base_unit.rb, line 238
def loaded?
  Unit.units.any? { |unit| self.has_same_identity_as? unit }
end
make_canonical() click to toggle source

Make self the canonical representation of the unit defined by self#label

# File lib/quantify/unit/base_unit.rb, line 243
def make_canonical
  unload if loaded?
  load
end
measures() click to toggle source

Describes what the unit measures/represents. This is taken from the @dimensions ivar, being, ultimately an attribute of the assocaited Dimensions object, e.g.

Unit.metre.measures                      #=> :length

Unit.J.measures                          #=> :energy
# File lib/quantify/unit/base_unit.rb, line 286
def measures
  @dimensions.describe
end
multiply(other) click to toggle source

Multiply two units together. This results in the generation of a compound unit.

# File lib/quantify/unit/base_unit.rb, line 444
def multiply(other)
  options = []
  self.instance_of?(Unit::Compound) ? options += @base_units : options << self
  other.instance_of?(Unit::Compound) ? options += other.base_units : options << other
  Unit::Compound.new(*options)
end
Also aliased as: times, *
name=(name) click to toggle source

Set the name attribute of self. Names are stringified, singlularized and stripped of underscores. Superscripts are formatted according to the configuration found in Quantify.use_superscript_characters? Names are NOT case sensitive (retrieving units by name can use any or mixed cases).

# File lib/quantify/unit/base_unit.rb, line 150
def name=(name)
  name = name.to_s.remove_underscores.singularize
  @name = Unit.use_superscript_characters? ? name.with_superscript_characters : name.without_superscript_characters
end
physical_quantity=(dimensions)
Alias for: dimensions=
pluralized_name() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 290
def pluralized_name
  @name.pluralize
end
pow(power) click to toggle source

Raise a unit to a power. This results in the generation of a compound unit, e.g. m^3.

In the event that the new unit represents a known unit, the non-compound representation is returned (i.e. of the SI or NonSI class).

# File lib/quantify/unit/base_unit.rb, line 478
def pow(power)
  return nil if power == 0
  original_unit = self.clone
  if power > 0
    new_unit = self.clone
    (power - 1).times { new_unit *= original_unit }
  elsif power < 0
    new_unit = reciprocalize
    ((power.abs) - 1).times { new_unit /= original_unit }
  end
  return new_unit
end
Also aliased as: **
reciprocalize() click to toggle source

Return new unit representing the reciprocal of self, i.e. 1/self

# File lib/quantify/unit/base_unit.rb, line 493
def reciprocalize
  Unit.unity / self
end
refresh_attributes() click to toggle source

Refresh the name, symbol and label attributes of self with respect to the configuration found in Quantify.use_superscript_characters?

# File lib/quantify/unit/base_unit.rb, line 185
def refresh_attributes
  self.name = name
  self.symbol = symbol
  self.label = label
end
scaling() click to toggle source

Returns the scaling factor for the unit with repsect to its SI alternative.

For example the scaling factor for degrees celsius is 273.15, i.e. celsius is a value of 273.15 greater than kelvin (but with no multiplicative factor).

# File lib/quantify/unit/base_unit.rb, line 270
def scaling
  @scaling || 0.0
end
si_unit() click to toggle source

Returns the SI unit for the same physical quantity which is represented by self, e.g.

# File lib/quantify/unit/base_unit.rb, line 420
def si_unit
  @dimensions.si_unit
end
symbol=(symbol) click to toggle source

Set the symbol attribute of self. Symbols are stringified and stripped of any underscores. Superscripts are formatted according to the configuration found in Quantify.use_superscript_characters?

Conventional symbols for units and unit prefixes prescribe case clearly (e.g. ‘m’ => metre, ‘M’ => mega-; ‘g’ => gram, ‘G’ => giga-) and therefore symbols ARE case senstive.

# File lib/quantify/unit/base_unit.rb, line 163
def symbol=(symbol)
  symbol = symbol.to_s.remove_underscores
  @symbol = Unit.use_superscript_characters? ? symbol.with_superscript_characters : symbol.without_superscript_characters
end
times(other)
Alias for: multiply
to_hash() click to toggle source

Return a hash representation of self containing each unit attribute (i.e each instance variable)

# File lib/quantify/unit/base_unit.rb, line 514
def to_hash
  hash = {}
  self.instance_variables.each do |var|
    symbol = var.to_s.gsub("@","").to_sym
    hash[symbol] = send symbol
  end
  return hash
end
unload() click to toggle source

Remove from system of known units.

# File lib/quantify/unit/base_unit.rb, line 233
def unload
  Unit.unload(self.label)
end
valid?() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 424
def valid?
  return true if valid_descriptors? && valid_dimensions?
  raise Exceptions::InvalidArgumentError, "Unit definition must include a name, a symbol, a label and physical quantity"
end
valid_descriptors?() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 429
def valid_descriptors?
  return true if is_dimensionless?
  [:name, :symbol, :label].all? do |attr|
    attribute = send(attr)
    attribute.is_a?(String) && !attribute.empty?
  end
end
valid_dimensions?() click to toggle source
# File lib/quantify/unit/base_unit.rb, line 437
def valid_dimensions?
  @dimensions.is_a? Dimensions
end
with_prefix(name_or_symbol) click to toggle source

Apply a prefix to self. Returns new unit according to the prefixed version of self, complete with modified name, symbol, factor, etc..

# File lib/quantify/unit/base_unit.rb, line 500
def with_prefix(name_or_symbol)
  self.class.initialize_prefixed_version(name_or_symbol,self)
end
with_prefixes(*prefixes) click to toggle source

Return an array of new unit instances based upon self, together with the prefixes specified by prefixes

# File lib/quantify/unit/base_unit.rb, line 507
def with_prefixes(*prefixes)
  [prefixes].map { |prefix| self.with_prefix(prefix) }
end

Private Instance Methods

initialize_copy(source) click to toggle source

Clone self and explicitly clone the associated Dimensions object located at @dimensions.

This enables full or ‘deep’ copies of the already initialized units to be retrieved and manipulated without corrupting the known unit representations. (self.clone makes only a shallow copy, i.e. clones attributes but not referenced objects)

Calls superclass method
# File lib/quantify/unit/base_unit.rb, line 547
def initialize_copy(source)
  super
  instance_variable_set("@dimensions", @dimensions.clone)
end
method_missing(method, *args, &block) click to toggle source

Provides syntactic sugar for several methods. E.g.

Unit.metre.to_kilo

is equivalent to Unit.metre.with_prefix :kilo.

Unit.m.alternatives_by_name

is equaivalent to Unit.m.alternatives :name

Calls superclass method
# File lib/quantify/unit/base_unit.rb, line 574
def method_missing(method, *args, &block)
  if method.to_s =~ /(to_)(.*)/ && prefix = Prefix.for($2.to_sym)
    return self.with_prefix(prefix)
  elsif method.to_s =~ /(alternatives_by_)(.*)/ && self.respond_to?($2.to_sym)
    return self.alternatives($2.to_sym)
  end
  super
end