DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Open Source Adventures: Episode 14: Timecalc

So it turns out someone already created Evil Wordle, using the exact same algorithm I wanted to use. Well, it's a fairly obvious idea.

Before I pick up a new project, I want to do a few episodes about random things I created before, starting with a very small thing - Timecalc.

The problem

Some things like haircuts or cleaning up kitty boxes need to be done over and over, every N weeks or so. I don't want to put such tasks as a recurring events in the calendar, because if I do it late, I want it to start the new count down from when it was actually done, not from when it was supposed to be done.

So when I do something like that, I need a quick way to know what date is today + N days, weeks, or months.

This can be done in Ruby without that much difficulty, but it's still not perfect:

$ ruby -rdate -e 'puts Date.today + 7 * 4' 2022-04-08 
Enter fullscreen mode Exit fullscreen mode

But I thought that maybe it would be more useful to create a custom command for it.

Timecalc command

The command works like this:

$ timecalc '10.days' 2022-03-21 $ timecalc '4.weeks' 2022-04-08 $ ./bin/timecalc '4.weeks + 2.days' 2022-04-10 $ ./bin/timecalc 'week - day' 2022-03-17 
Enter fullscreen mode Exit fullscreen mode

The string passed to timecalc is just arbitrary Ruby code with some DSL preloaded, and automatically prints out the value. If the value is a Duration, it's added to Date.today before printing.

bin/timecalc

The binary doesn't do anything special, it doesn't even support --help or such. It just calls the library.

#!/usr/bin/env ruby require_relative "../lib/timecalc" ARGV.each do |expr| puts Timecalc.new.call(expr) end 
Enter fullscreen mode Exit fullscreen mode

By the way, I don't love any of the getopt-like libraries, as they tend to be overcomplicated and unflexible. optimist is OK.

lib/timecalc.rb

The library just loads Date and ActiveSupport, and then has some logic how to format the result. As you can see I thought about making it working with hours, minutes, and seconds as well, but I never actually used that functionality:

require "date" gem "activesupport", ">=5" require "active_support/core_ext/numeric/time" require "active_support/core_ext/time/calculations" require "active_support/core_ext/date/calculations" class Timecalc def initialize(today=Date.today) @today = Date.today end attr_reader :today %i[ second seconds minute minutes hour hours day days week weeks ].each do |unit| define_method(unit) { 1.send(unit) } end def call(expr) format_output eval(expr) end def format_output(result) case result when ActiveSupport::Duration (@today + result).to_s else result.to_s end end end 
Enter fullscreen mode Exit fullscreen mode

Specs

Here are specs, for library only:

describe Timecalc do examples = { "today" => "2019-07-20", "today + 3.days" => "2019-07-23", "today + week" => "2019-07-27", "today + 1.week" => "2019-07-27", "7.days" => "2019-07-27", "7.day" => "2019-07-27", } examples.each do |expr, expected_output| describe expr do let(:today) { Date.parse("2019-07-20") } let(:timecalc) { Timecalc.new(today) } let(:output) { timecalc.call(expr) } it do expect(output).to eq expected_output end end end end 
Enter fullscreen mode Exit fullscreen mode

Was it successful?

Timecalc was a case of me thinking this use case might be big enough, and worth creating a fancy tool for it (something like Unix units command), then prototyping a super simple version, and discovering that the prototype does everything I want, and I never needed anything fancier.

And in retrospect, even that is only a modest improvement over what Ruby one-liners like ruby -rdate -e 'puts Date.today + 7 * 4', so I didn't even mention timecalc until now.

I still use timecalc occasionally, so in the end it sort of worked out. And it was definitely worth doing a quick prototype before starting with parsers etc.

If you're interested, you can get the code here.

Coming next

In the next few episodes I want to showcase some of my other tiny projects, and how they went.

Top comments (0)