Friday, January 7, 2011

Some meta-progamming fun with Ruby (a define_method example)

I am currently working on my first gem which I hope to publish soon. It will be based on this beginning_of_fortnight method but will be more complete and flexible and (hopefully) it will be properly tested and documented. And it will be a gem of course, so it will be easy to install.

So the logic used to determine which half of a fortnight a given day falls in I wrote to work with Time objects. I decided that re-implementing it with Date objects would not be trivial. Take a look at this:

>> (Date.today + 1.week) - Date.today
=> Rational(7, 1)
>> (Time.now + 1.week) - Time.now
=> 604799.999613
>> (Time.now + 1.week) - Time.now
=> 604799.999532
>> 7.days.to_i
=> 604800

The result of subtracting two Dates surprised me a little. I guess Rational(7, 1) is 7 days. But anyway I didn't want to get any deeper into that so I did it like this:
So instead of figuring out how to apply my algorithm to Dates I will just convert them to Times, call the Time method on them, then convert them back to Dates afterwards.

Actually Date#to_time and Time#to_date are not part of the standard library but are defined by ActiveSupport. Since my gem requires ActiveSupport that's fine. Of course I could have written out the three methods but why not add some meta-programming coolness to the lazy to DRY it right up.

The splat on *args is important. My Time methods take an argument with a default value. The splatted args takes care of that so no matter how many arguments there are (or aren't), they will get passed correctly into the send. If anyone wants to check out (and/or checkout) the WIP gem, you can find it here (github). Feedback is welcome.

Todos include cleaning the rdoc output and adding gem dependencies to the gemspec (though I don't know if I'd want it to auto-install active support if you didn't have it already...)