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 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 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 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 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 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)