module Runby # Represents the distance units (e.g. kilometers, miles) used in paces # including the human-readable description of each unit # and the factor used to convert it to kilometers. class DistanceUnit attr_reader :symbol, :description, :conversion_factor def initialize(unit_of_measure) if unit_of_measure.is_a? Symbol raise "':#{unit_of_measure.to_s}' is an unknown unit of measure" unless DistanceUnit.known_uom? unit_of_measure @symbol = unit_of_measure @conversion_factor = @@_uom_definitions[@symbol][:conversion_factor] elsif unit_of_measure.is_a? String parsed_uom = DistanceUnit.parse(unit_of_measure) @symbol = parsed_uom.symbol @conversion_factor = @@_uom_definitions[@symbol][:conversion_factor] end @description = @@_uom_definitions[@symbol][:description] end def to_s(format: :long, pluralize: false) case format when :short then @symbol.to_s when :long then pluralize ? description_plural : @description end end def description_plural @@_uom_definitions[@symbol][:description_plural] end def self.parse(description) description = description.strip.chomp.downcase found_uom = nil @@_uom_definitions.each do |uom, details| if details[:synonyms].include? description found_uom = uom break end end raise "Error parsing distance unit '#{description}'" unless found_uom return DistanceUnit.new found_uom end def self.try_parse(str) uom, error_message = nil begin uom = parse str rescue StandardError => ex error_message = "#{ex.message}" end { uom: uom, error: error_message } end def self.known_uom?(symbol) # TODO: test @@_uom_definitions.has_key?(symbol) end def ==(other) if other.is_a? DistanceUnit @symbol == other.symbol elsif other.is_a? String @symbol == DistanceUnit.parse(other) else raise "Error comparing DistanceUnit to #{other.class}" end end @@_uom_definitions = { km: { description: 'kilometer', description_plural: 'kilometers', conversion_factor: 1.0, synonyms: %w(k km kms kilometer kilometers) }, m: { description: 'meter', description_plural: 'meters', conversion_factor: 0.001, synonyms: %w(m meter meters) }, mi: { description: 'mile', description_plural: 'miles', conversion_factor: 1.609344, synonyms: %w(mi mile miles) }, ft: { description: 'foot', description_plural: 'feet', conversion_factor: 0.0003048, synonyms: %w(ft foot feet) }, yd: { description: 'yard', description_plural: 'yards', conversion_factor: 1093.61, synonyms: %w(y yd yds yard yards) }, # Fun distance unit of measures marathon: { description: 'marathon', description_plural: 'marathons', conversion_factor: 42.1648128, synonyms: %w(marathon) } } end end