class Runby::Pace

Represents a pace consisting of a distance and a time in which that distance was covered

Attributes

distance[R]
time[R]

Public Class Methods

new(time_or_pace, distance = '1K') click to toggle source
Calls superclass method
# File lib/runby_pace/pace.rb, line 10
def self.new(time_or_pace, distance = '1K')
  return time_or_pace if time_or_pace.is_a? Pace
  super
end
new(time_or_pace, distance = '1K') click to toggle source
# File lib/runby_pace/pace.rb, line 15
def initialize(time_or_pace, distance = '1K')
  case time_or_pace
  when RunbyTime
    init_from_time time_or_pace, distance
  when String
    init_from_string time_or_pace, distance
  else
    raise 'Invalid Time or Pace'
  end
  freeze
end
parse(str) click to toggle source

@param [String] str is either a long-form pace such as “10:00 per mile” or a short-form pace like “10:00 p/mi”

# File lib/runby_pace/pace.rb, line 58
def self.parse(str)
  str = str.to_s.strip.chomp
  match = str.match %r{^(?<time>[:\d]*) ?(?: per |p\/)(?<distance>(?:[\d.]+ ?)?\w+)$}
  raise "Invalid pace format (#{str})" unless match
  time = Runby::RunbyTime.new(match[:time])
  distance = Runby::Distance.new(match[:distance])
  Pace.new time, distance
end
try_parse(str) click to toggle source
# File lib/runby_pace/pace.rb, line 67
def self.try_parse(str)
  pace = nil
  error_message = nil
  warning_message = nil
  begin
    pace = Pace.parse str
  rescue StandardError => ex
    error_message = ex.message
  end
  { pace: pace, error: error_message, warning: warning_message }
end

Public Instance Methods

+(other) click to toggle source

@param [Pace, RunbyTime] other

# File lib/runby_pace/pace.rb, line 116
def +(other)
  if other.is_a?(Pace)
    Pace.new(@time + other.convert_to(@distance).time, @distance)
  elsif other.is_a?(RunbyTime)
    Pace.new(@time + other, @distance)
  end
end
-(other) click to toggle source

@param [Pace, RunbyTime] other

# File lib/runby_pace/pace.rb, line 107
def -(other)
  if other.is_a?(Pace)
    Pace.new(@time - other.convert_to(@distance).time, @distance)
  elsif other.is_a?(RunbyTime)
    Pace.new(@time - other, @distance)
  end
end
<=>(other) click to toggle source
# File lib/runby_pace/pace.rb, line 79
def <=>(other)
  raise "Unable to compare Runby::Pace to #{other.class}(#{other})" unless [Pace, RunbyTime, String].include? other.class
  if other.is_a? Pace
    meters_per_minute.round(2) <=> other.meters_per_minute.round(2)
  elsif other.is_a? RunbyTime
    @time <=> other
  elsif other.is_a? String
    return 0 if to_s == other || to_s(format: :long) == other
    return 0 if @time == other
    self <=> try_parse(other)[:pace]
  end
end
almost_equals?(other_pace, tolerance_time = '00:01') click to toggle source
# File lib/runby_pace/pace.rb, line 92
def almost_equals?(other_pace, tolerance_time = '00:01')
  if other_pace.is_a?(RunbyTime)
    return almost_equals?(Pace.new(other_pace, @distance), tolerance_time)
  end
  if other_pace.is_a?(String)
    return almost_equals?(Pace.new(other_pace, @distance), tolerance_time) if other_pace.match?(/^\d?\d:\d\d$/)
    other_pace = Pace.parse(other_pace)
  end
  tolerance = RunbyTime.new(tolerance_time)
  fast_end = (self - tolerance)
  slow_end = (self + tolerance)
  slow_end <= other_pace && other_pace <= fast_end
end
as_speed() click to toggle source
# File lib/runby_pace/pace.rb, line 44
def as_speed
  total_minutes = @time.total_minutes
  multiplier = total_minutes.positive? ? (60 / total_minutes).round(2) : 0
  distance = Runby::Distance.new(@distance.uom, multiplier)
  Runby::Speed.new distance
end
convert_to(target_distance) click to toggle source
# File lib/runby_pace/pace.rb, line 27
def convert_to(target_distance)
  target_distance = Distance.new(target_distance) unless target_distance.is_a?(Distance)
  return self if @distance == target_distance
  conversion_factor = target_distance / @distance
  Pace.new @time * conversion_factor, target_distance
end
distance_covered_over_time(time) click to toggle source
# File lib/runby_pace/pace.rb, line 124
def distance_covered_over_time(time)
  time = Runby.sanitize(time).as(RunbyTime)
  if time.total_minutes.zero? || @distance.multiplier.zero?
    return Runby::Distance.new(@distance.uom, 0)
  end
  divisor = @time.total_minutes / time.total_minutes / @distance.multiplier
  distance_covered = Runby::Distance.new(@distance.uom, 1 / divisor)
  distance_covered
end
meters_per_minute() click to toggle source
# File lib/runby_pace/pace.rb, line 51
def meters_per_minute
  total_minutes = @time.total_minutes
  return 0 unless total_minutes.positive?
  @distance.meters / total_minutes
end
to_s(format: :short) click to toggle source
# File lib/runby_pace/pace.rb, line 34
def to_s(format: :short)
  leading_one_regex = /^1 ?/
  distance_s = @distance.to_s(format: format).gsub(leading_one_regex, '')
  case format
  when :short then "#{time} p/#{distance_s}"
  when :long then "#{time} per #{distance_s}"
  else raise "Invalid string format #{format}"
  end
end

Private Instance Methods

init_from_string(string, distance = '1K') click to toggle source
# File lib/runby_pace/pace.rb, line 136
def init_from_string(string, distance = '1K')
  pace = Pace.try_parse(string)
  if pace[:pace]
    @time = pace[:pace].time
    @distance = pace[:pace].distance
    return
  end
  @time = Runby::RunbyTime.new string
  @distance = Runby::Distance.new distance
end
init_from_time(time, distance) click to toggle source
# File lib/runby_pace/pace.rb, line 147
def init_from_time(time, distance)
  @time = time
  @distance = Runby::Distance.new distance
end