151 lines
3.1 KiB
Ruby
151 lines
3.1 KiB
Ruby
module UnitConversion
|
|
class ValueUnit
|
|
|
|
def self.for(value_string, unit_string = nil, formatter = nil)
|
|
raise UnparseableUnitError, "value is empty" if value_string.blank?
|
|
|
|
if String === value_string && unit_string.nil?
|
|
value_string, unit_string = parse_single_string(value_string)
|
|
end
|
|
|
|
if value_string.is_a?(ParsedNumber)
|
|
value = value_string
|
|
else
|
|
value = ParsedNumber.new(value_string)
|
|
end
|
|
|
|
unit = case unit_string
|
|
when nil
|
|
nil
|
|
when ->(u) { u.blank? }
|
|
nil
|
|
when ParsedUnit
|
|
unit_string
|
|
else
|
|
ParsedUnit.new(unit_string)
|
|
end
|
|
|
|
if unit.nil?
|
|
ValueNoUnit.new(value, formatter)
|
|
else
|
|
ValueUnit.new(value, unit, formatter)
|
|
end
|
|
end
|
|
|
|
def self.parse_single_string(value_unit_string)
|
|
match = UNIT_PARSING_REGEX.match(value_unit_string.to_s.strip)
|
|
|
|
if match && match[:value].present?
|
|
return match[:value], match[:unit]
|
|
else
|
|
raise UnparseableUnitError, "'#{value_unit_string}' does not appear to be a valid measurement of the form <value> <units> (ie '5 cup' or '223 gram/cup')"
|
|
end
|
|
end
|
|
|
|
attr_reader :formatter
|
|
|
|
def initialize(value, unit, formatter = nil)
|
|
@value = value
|
|
@unit = unit
|
|
@formatter = formatter || NumberFormatter.for(value, unit)
|
|
end
|
|
|
|
def to_s
|
|
"#{pretty_value} #{unit}"
|
|
end
|
|
|
|
def unit
|
|
@unit
|
|
end
|
|
|
|
def value
|
|
@value
|
|
end
|
|
|
|
def raw_value
|
|
@value.value
|
|
end
|
|
|
|
def unitwise
|
|
unit.unitwise(value.value)
|
|
end
|
|
|
|
# Returns a new ValueUnit scaled by the given factor
|
|
def scale(factor)
|
|
if factor.present?
|
|
parsed_factor = ParsedNumber.new(factor)
|
|
ScaleConversion.new(parsed_factor).convert(self)
|
|
else
|
|
self
|
|
end
|
|
end
|
|
|
|
# Returns a new ValueUnit with the given new_parsed_unit. If converting between mass and volume,
|
|
# also requires a density UnitValue
|
|
def convert(new_unit, density = nil)
|
|
new_parsed_unit = ParsedUnit.new(new_unit)
|
|
parsed_density = density ? ValueUnit.for(density) : nil
|
|
|
|
if new_parsed_unit.unit != self.unit.unit
|
|
ConvertConversion.new(new_parsed_unit, parsed_density).convert(self)
|
|
else
|
|
self
|
|
end
|
|
end
|
|
|
|
def auto_unit
|
|
AutoUnitConversion.new.convert(self)
|
|
end
|
|
|
|
def density?
|
|
unit.density?
|
|
end
|
|
|
|
def volume?
|
|
unit.volume?
|
|
end
|
|
|
|
def mass?
|
|
unit.mass?
|
|
end
|
|
|
|
def pretty_value
|
|
formatter.format(value.value)
|
|
end
|
|
end
|
|
|
|
class ValueNoUnit < ValueUnit
|
|
def initialize(value, formatter = nil)
|
|
@value = value
|
|
@formatter = formatter || NumberFormatter.for(value, unit)
|
|
end
|
|
|
|
def to_s
|
|
"#{pretty_value}"
|
|
end
|
|
|
|
def unit
|
|
nil
|
|
end
|
|
|
|
def unitwise
|
|
raise UnknownUnitError, "No unit value provided"
|
|
end
|
|
|
|
def convert(new_parsed_unit, density_unit_value = nil)
|
|
raise UnknownUnitError, "No unit value provided"
|
|
end
|
|
|
|
def density?
|
|
false
|
|
end
|
|
|
|
def volume?
|
|
false
|
|
end
|
|
|
|
def mass?
|
|
false
|
|
end
|
|
end
|
|
end |