lib/quantify/unit/base_unit.rb in quantify-1.0.4 vs lib/quantify/unit/base_unit.rb in quantify-1.0.5
- old
+ new
@@ -9,13 +9,12 @@
# by SI and NonSI unit classes.
# 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
#
- def self.load(options)
- unit = self.new(options)
- unit.load
+ def self.load(options=nil,&block)
+ self.new(options,&block).load
end
def self.construct_and_load(unit,&block)
self.construct(unit, &block).load
end
@@ -48,12 +47,12 @@
new_unit = self.new unit.to_hash
yield new_unit if block_given?
return new_unit
end
- # Syntactic sugar for defining the known units, enabling the required
- # associated units to be loaded at runtime, e.g.
+ # 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
@@ -63,13 +62,13 @@
#
def self.configure &block
class_eval &block if block
end
- attr_accessor :name, :symbol, :label
- attr_accessor :dimensions, :factor
- attr_accessor :acts_as_alternative_unit, :acts_as_equivalent_unit
+ attr_accessor :name, :symbol, :label, :factor
+ attr_reader :dimensions
+ attr_reader :acts_as_alternative_unit, :acts_as_equivalent_unit
# Create a new Unit::Base instance.
#
# Valid options are: :name => The unit name, e.g. :kilometre
#
@@ -99,56 +98,73 @@
#
# 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
#
- def initialize(options=nil)
+ def initialize(options=nil)
+ @acts_as_alternative_unit = true
+ @acts_as_equivalent_unit = false
+ @factor = 1.0
+ @symbol = nil
+ @label = nil
if options.is_a? Hash
+ self.dimensions = options[:dimensions] || options[:physical_quantity]
@name = options[:name].standardize.singularize.downcase
- options[:dimensions] = options[:dimensions] || options[:physical_quantity]
- if options[:dimensions].is_a? Dimensions
- @dimensions = options[:dimensions]
- elsif options[:dimensions].is_a? String or options[:dimensions].is_a? Symbol
- @dimensions = Dimensions.for options[:dimensions]
- else
- raise Exceptions::InvalidArgumentError, "Unknown physical_quantity specified"
- end
- @factor = options[:factor].nil? ? 1.0 : options[:factor].to_f
- @symbol = options[:symbol].nil? ? nil : options[:symbol].standardize
- @label = options[:label].nil? ? nil : options[:label].to_s
- @acts_as_alternative_unit = true
- @acts_as_equivalent_unit = false
+ @factor = options[:factor].to_f if options[:factor]
+ @symbol = options[:symbol].standardize if options[:symbol]
+ @label = options[:label].to_s if options[:label]
end
yield self if block_given?
valid?
end
+ def dimensions=(dimensions)
+ if dimensions.is_a? Dimensions
+ @dimensions = dimensions
+ elsif dimensions.is_a? String or dimensions.is_a? Symbol
+ @dimensions = Dimensions.for dimensions
+ else
+ raise Exceptions::InvalidArgumentError, "Unknown physical_quantity specified"
+ end
+ end
+ alias :physical_quantity= :dimensions=
+
# 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)).operate do |unit|
+ # (Unit.pound_force/(Unit.in**2)).configure do |unit|
# unit.symbol = 'psi'
# unit.label = 'psi'
# unit.name = 'pound per square inch'
# end
#
- def operate
+ def configure
yield self if block_given?
return self if valid?
end
+ # Similar to #configure but makes the new unit configuration the canonical
+ # unit for self.label
+ #
+ def configure_as_canonical &block
+ unload if loaded?
+ configure &block if block_given?
+ make_canonical
+ end
+
# Load an initialized Unit into the system of known units.
#
- # If a block is given, the unit can be operated on prior to loading, in a
- # similar to way to the #operate method.
+ # If a block is given, the unit can be configured prior to loading, in a
+ # similar to way to the #configure method.
#
def load
yield 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
# Remove from system of known units.
def unload
Unit.unload(self.label)
@@ -157,15 +173,23 @@
# check if an object with the same label already exists
def loaded?
Unit.units.any? { |unit| self.has_same_identity_as? unit }
end
+ # Make self the canonical representation of the unit defined by self#label
def make_canonical
- unload
+ unload if loaded?
load
end
+ # Set the canonical unit label - the unique unit identifier - to a new value
+ def canonical_label=(new_label)
+ unload if loaded?
+ self.label = new_label
+ load
+ end
+
def acts_as_alternative_unit=(value)
@acts_as_alternative_unit = (value == (true||false) ? value : false)
make_canonical
end
@@ -201,15 +225,25 @@
def pluralized_name
self.name.pluralize
end
- # Determine if the unit represents one of the base quantities
+ # Determine if the unit represents one of the base quantities, length,
+ # mass, time, temperature, etc.
+ #
def is_base_unit?
Dimensions::BASE_QUANTITIES.map(&:standardize).include? self.measures
end
+ # 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.
+ #
+ def is_base_quantity_si_unit?
+ is_si_unit? and is_base_unit? and is_benchmark_unit?
+ end
+
# Determine is the unit is a derived unit - that is, a unit made up of more
# than one of the base quantities
#
def is_derived_unit?
not is_base_unit?
@@ -223,11 +257,12 @@
end
# 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, making this method necessary.
+ # kilogram, which is an oddity in being THE canonical SI unit for mass, yet
+ # containing a prefix. This oddity makes this method useful/necessary.
#
def is_benchmark_unit?
self.factor == 1.0
end
@@ -275,11 +310,11 @@
def is_same_as?(other)
[:dimensions,:factor,:scaling].all? do |attr|
self.send(attr) == other.send(attr)
end
end
-
+
alias :== :is_same_as?
# 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
@@ -373,10 +408,12 @@
options = []
self.instance_of?(Unit::Compound) ? options += self.base_units : options << self
other.instance_of?(Unit::Compound) ? options += other.base_units : options << other
Unit::Compound.new(*options)
end
+ alias :times :multiply
+ alias :* :multiply
# 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
@@ -391,10 +428,11 @@
else
options << CompoundBaseUnit.new(other,-1)
end
Unit::Compound.new(*options)
end
+ alias :/ :divide
# 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
@@ -410,19 +448,15 @@
new_unit = reciprocalize
((power.abs) - 1).times { new_unit /= original_unit }
end
return new_unit
end
+ alias :** :pow
# Return new unit representing the reciprocal of self, i.e. 1/self
def reciprocalize
Unit.unity / self
end
-
- alias :times :multiply
- alias :* :multiply
- alias :/ :divide
- alias :** :pow
# Apply a prefix to self. Returns new unit according to the prefixed version
# of self, complete with modified name, symbol, factor, etc..
#
def with_prefix(name_or_symbol)