Skip to content

Commit

Permalink
[29] Add bigdecimal support (#30)
Browse files Browse the repository at this point in the history
* update ruby version to 3.2.1

* add method to check seconds

* add name friendly methods to convert distance, clocktime and seconds

* add handle to more than 24h in conversions of seconds

* add round option to user in convert

* update Readme and gemspec

* update readme and gemspec

* Add bigdecimal option and methods to receive result in seconds or bigdecimal

* Rubocop lint adjusts

* update gitignore

* update gem version in readme
  • Loading branch information
0jonjo authored Jul 21, 2024
1 parent ec9a829 commit c328d7e
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 36 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/calcpace-*
/calcpace-*
calcpace.gemspec
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Calcpace [![Gem Version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=rb&r=r&ts=1683906897&type=6e&v=1.0.0&x2=0)](https://badge.fury.io/rb/calcpace)
# Calcpace [![Gem Version](https://badge.fury.io/rb/calcpace.svg)](https://badge.fury.io/rb/calcpace)

Calcpace is a Ruby gem designed to assist with calculations related to running and cycling activities. It can calculate pace, total time, and distance, and also convert distances between miles and kilometers. Results are provided in a readable format, with times in HH:MM:SS and distances in X.X format.
Calcpace is a Ruby gem that helps with calculations related to running/cycling activities or general purposes involving distance and time. It can calculate pace, total time, and distance, accepting time in seconds or HH:MM:SS format. It also converts distances between miles and kilometers. The results are provided in a readable format, with times in HH:MM:SS or seconds and distances in X.X format. To prevent precision problems, the gem supports BigDecimal to handle the calculations, if you need, and always returns data using the same distance unit (kilometers or miles) that was used as input.

## Installation

### Add to your Gemfile

```ruby
gem 'calcpace', '~> 1.0.0'
gem 'calcpace', '~> 1.1.1'
```

Then run bundle install.
Expand All @@ -24,47 +24,49 @@ gem install calcpace

### Calculate Pace

To calculate pace, provide the total time (in HH:MM:SS format) and distance (in X.X format, representing kilometers or miles).
To calculate pace, provide the total time (in HH:MM:SS format) and distance (in X.X format, representing kilometers or miles). You can use the method `pace` or `pace_seconds`. If you want an extra precision in the calculations, you can pass true as parameter to the method `pace_seconds` and receive result in BigDecimal.

```ruby
calculate = Calcpace.new
calculate.pace('01:00:00', 12) # => "00:05:00"
calculate.pace('string', 12) # It must be a time (RuntimeError)
calculate.pace_seconds('01:00:00', 12) # => 300
calculate.pace_seconds('01:37:21', 12.3, true) # => 0.474878048780487804878048780487804878049e3
calculate.pace_seconds('01:37:21', 12.3) # => 474.8780487804878
```

### Calculate Total Time

To calculate total time, provide the pace (in HH:MM:SS format) and distance (in X.X format, representing kilometers or miles).
To calculate total time, provide the pace (in HH:MM:SS format) and distance (in X.X format, representing kilometers or miles). You can use the method `total_time` or `total_time_seconds`. If you want an extra precision in the calculations, you can pass true as parameter to the method `total_time_seconds` and receive result in BigDecimal.

```ruby
calculate = Calcpace.new
calculate.total_time('00:05:00', 12) # => "01:00:00"
calculate.total_time('00:05:00', 'string') # It must be a XX:XX:XX time (RuntimeError)
calculate.total_time_seconds('01:37:21', 12.3) # => 71844.3
calculate.total_time_seconds('01:37:21', 12.3, true) # => 0.718443902439024390243902439024390243902e5
```

### Calculate Distance

To calculate distance, provide the running time (in HH:MM:SS format) and pace (in HH:MM:SS format).
To calculate distance, provide the running time (in HH:MM:SS format) and pace (in HH:MM:SS format). If you want an extra precision in the calculations, you can pass true as parameter to the method and receive result in BigDecimal.

```ruby
Calcpace.new.distance('01:30:00', '00:05:00') # => 18.0
calculate = Calcpace.new
calculate.distance('01:37:21', '00:06:17') # => 15.0
calculate.distance('01:37:21', '00:06:17', true) # => 0.15493368700265251989389920424403183024e2
calculate.distance('01:37:21', 'string') # It must be a time (RuntimeError)
```

### Convert Distances

To convert distances, provide the distance and the unit of measurement (either 'km' for kilometers or 'mi' for miles).
To convert distances, provide the distance and the unit of measurement (either 'km' for kilometers or 'mi' for miles). If want to change the default round of the result (2), you can pass a second parameter to the method.

```ruby
converter = Calcpace.new
converter.convert(10, 'km') # => 6.21
converter.convert_distance(10, 'mi') # => 16.09
```

If want to change the default round of the result (2), you can pass a second parameter to the method.

```ruby
converter = Calcpace.new
converter.convert(10, 'km', 1) # => 6.2
converter.convert_distance(10, 'mi') # => 16.09
converter.convert(10, 'mi', 3) # => 16.093
```

Expand Down
8 changes: 4 additions & 4 deletions Rakefile.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# frozen_string_literal: true

require "minitest/test_task"
require 'minitest/test_task'

Minitest::TestTask.create(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.libs << 'test'
t.libs << 'lib'
t.warning = false
end

task :default => :test
task default: :test
8 changes: 4 additions & 4 deletions calcpace.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Gem::Specification.new do |s|
s.name = 'calcpace'
s.version = '1.0.0'
s.summary = 'Calcpace: calculate time and distances for activities such as running and cycling.'
s.description = 'Calculate pace, total time, distance and easily convert distances (kilometers and miles) for activities like running and cycling. Get readable results in HH:MM:SS or X.X format for time and distance calculations.'
s.version = '1.1.1'
s.summary = 'Calcpace: calculate time, distance, pace, velocity and convert distances in an easy and precise way.'
s.description = 'Calcpace is a Ruby gem that helps with calculations related to running/cycling activities or general purposes involving distance and time. It can calculate pace, total time, and distance. It also converts distances between miles and kilometers and check formats of time and distance. The results are provided in a readable format, with times in HH:MM:SS or seconds and distances in X.X format. If you need, the gem supports BigDecimal to handle the calculations, '
s.authors = ['Joao Gilberto Saraiva']
s.email = '[email protected]'
s.files = ['lib/calcpace.rb', 'lib/calcpace/calculator.rb', 'lib/calcpace/checker.rb', 'lib/calcpace/converter.rb']
Expand All @@ -16,7 +16,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rubocop', '~> 0.79'
s.add_development_dependency 'rubocop-minitest', '~> 0.11'
s.required_ruby_version = '>= 2.7.0'
s.post_install_message = "It's time to grab your sneakers or hop on your bike and start exercising! Thank you for installing Calcpace!"
s.post_install_message = "It's time to calculate! Thank you for installing Calcpace."
s.metadata = { 'source_code_uri' => 'https://github.com/0jonjo/calcpace' }
s.homepage = 'https://github.com/0jonjo/calcpace'
s.license = 'MIT'
Expand Down
33 changes: 27 additions & 6 deletions lib/calcpace/calculator.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
# frozen_string_literal: true

require 'bigdecimal'

module Calculator
def pace(time, distance)
def pace(time, distance, bigdecimal = false)
pace_in_seconds = pace_seconds(time, distance, bigdecimal)
convert_to_clocktime(pace_in_seconds)
end

def pace_seconds(time, distance, bigdecimal = false)
check_time(time)
check_distance(distance)
convert_to_clocktime(convert_to_seconds(time) / distance.to_f)
seconds = convert_to_seconds(time)
bigdecimal ? seconds / BigDecimal(distance.to_s) : seconds / distance
end

def total_time(pace, distance, bigdecimal = false)
total_time_in_seconds = total_time_seconds(pace, distance, bigdecimal)
convert_to_clocktime(total_time_in_seconds)
end

def total_time(pace, distance)
def total_time_seconds(pace, distance, bigdecimal = false)
check_time(pace)
check_distance(distance)
convert_to_clocktime(convert_to_seconds(pace) * distance.to_f)
pace_seconds = convert_to_seconds(pace)
bigdecimal ? pace_seconds * BigDecimal(distance.to_s) : pace_seconds * distance
end

def distance(time, pace)
def distance(time, pace, bigdecimal = false)
check_time(time)
check_time(pace)
convert_to_seconds(time).to_f / convert_to_seconds(pace).round(2)
if bigdecimal
time_seconds = BigDecimal(convert_to_seconds(time).to_s)
pace_seconds = BigDecimal(convert_to_seconds(pace).to_s)
else
time_seconds = convert_to_seconds(time)
pace_seconds = convert_to_seconds(pace)
end
time_seconds / pace_seconds
end
end
15 changes: 10 additions & 5 deletions lib/calcpace/converter.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# frozen_string_literal: true

require 'bigdecimal'

module Converter
KM_TO_MI = BigDecimal('0.621371')
MI_TO_KM = BigDecimal('1.60934')

def to_seconds(time)
check_time(time)
convert_to_seconds(time)
Expand All @@ -15,7 +20,7 @@ def convert(distance, unit, round_limit = 2)
check_distance(distance)
check_unit(unit)
check_integer(round_limit)
convert_the_distance(distance, unit, round_limit)
convert_the_distance(BigDecimal(distance.to_s), unit, round_limit)
end

def convert_to_seconds(time)
Expand All @@ -24,16 +29,16 @@ def convert_to_seconds(time)
end

def convert_to_clocktime(seconds)
seconds >= 86_400 ? time = '%d %H:%M:%S' : time = '%H:%M:%S'
Time.at(seconds).utc.strftime(time)
format = seconds >= 86_400 ? '%d %H:%M:%S' : '%H:%M:%S'
Time.at(seconds.to_i).utc.strftime(format)
end

def convert_the_distance(distance, unit, round_limit = 2)
case unit
when 'km'
(distance * 0.621371).round(round_limit)
(distance * KM_TO_MI).round(round_limit)
when 'mi'
(distance * 1.60934).round(round_limit)
(distance * MI_TO_KM).round(round_limit)
end
end
end
36 changes: 36 additions & 0 deletions test/calcpace/test_calculator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'minitest/autorun'
require 'bigdecimal'
require_relative '../../lib/calcpace'

class TestCalculator < Minitest::Test
Expand All @@ -14,6 +15,23 @@ def test_pace
assert_raises(RuntimeError) { @checker.pace('00:00:00', 0) }
assert_raises(RuntimeError) { @checker.pace('00:00:00', -1) }
assert_equal '00:06:00', @checker.pace('01:00:00', 10)
assert_equal '00:07:54', @checker.pace('01:37:21', 12.3)
end

def test_pace_without_bigdecimal_precision
assert_equal '00:07:54', @checker.pace('01:37:21', 12.3, false)
end

def test_pace_seconds
assert_raises(RuntimeError) { @checker.pace_seconds('', 10) }
assert_raises(RuntimeError) { @checker.pace_seconds('invalid', 10) }
assert_raises(RuntimeError) { @checker.pace_seconds('00:00:00', 0) }
assert_raises(RuntimeError) { @checker.pace_seconds('00:00:00', -1) }
assert_equal BigDecimal('474.8780487804878'), @checker.pace_seconds('01:37:21', 12.3)
end

def test_pace_seconds_with_bigdecimal_precision
assert_equal BigDecimal('0.474878048780487804878048780487804878049e3'), @checker.pace_seconds('01:37:21', 12.3, true)
end

def test_total_time
Expand All @@ -24,9 +42,27 @@ def test_total_time
assert_equal '01:00:00', @checker.total_time('00:05:00', 12)
end

def test_total_time_seconds
assert_raises(RuntimeError) { @checker.total_time_seconds('', 10) }
assert_raises(RuntimeError) { @checker.total_time_seconds('invalid', 10) }
assert_raises(RuntimeError) { @checker.total_time_seconds('00:00:00', 0) }
assert_raises(RuntimeError) { @checker.total_time_seconds('00:00:00', -1) }
assert_equal 3600, @checker.total_time_seconds('00:05:00', 12)
assert_equal 71_844.3, @checker.total_time_seconds('01:37:21', 12.3)
end

def test_total_time_seconds_with_bigdecimal_precision
assert_equal BigDecimal('0.718443e5'), @checker.total_time_seconds('01:37:21', 12.3, true)
end

def test_distance
assert_raises(RuntimeError) { @checker.distance('', '00:05:00') }
assert_raises(RuntimeError) { @checker.distance('01:00:00', '') }
assert_equal 18.0, @checker.distance('01:30:00', '00:05:00')
assert_equal 15.0, @checker.distance('01:37:21', '00:06:17')
end

def test_distance_with_bigdecimal_precision
assert_equal BigDecimal('0.15493368700265251989389920424403183024e2'), @checker.distance('01:37:21', '00:06:17', true)
end
end
2 changes: 1 addition & 1 deletion test/calcpace/test_converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_convert_to_clocktime

def test_to_clocktime
assert_equal '01:00:00', @checker.to_clocktime(3600)
assert_equal '02 01:00:00', @checker.to_clocktime(90000)
assert_equal '02 01:00:00', @checker.to_clocktime(90_000)
assert_raises(RuntimeError) { @checker.to_clocktime(-1) }
assert_raises(RuntimeError) { @checker.to_clocktime(0) }
assert_raises(RuntimeError) { @checker.to_clocktime('invalid') }
Expand Down

0 comments on commit c328d7e

Please sign in to comment.