Here I summarize a few of the problems encountered, show my "minimal" solution (least amount of code rewriting) and offer a chance for fellow monks to toss eggs at my sinful ways, or better yet, offer up improvements.
There are four ways to get a new Time::Piece object. There is a constructor, new(), the gmtime() and localtime() functions which return Time::Piece objects in scalar context, plus there are overloaded arithmetic operators (used in conjunction with other Time::Piece objects, Time::Seconds objects, or scalar values representing seconds).
In addition to the arithmetic operators, Time::Piece objects also overload stringification so that the objects returned from gmtime() and localtime() still appear to produce a date string in scalar context.
All four of these paths to Time::Piece construction eventually utilize a private method _mktime() that performs the blessing, etc. This is what I referred to in the past as a "hybrid" method, because it operates either as an object method or as a function that takes seconds since epoch as an argument (in order to accommodate gmtime() and localtime()). What it does not currently do is act as a static method, which takes the package name as a first argument in such constructs as Time::Piece->new.
The goal is to have a subclass produce new instants of the subclass via any of the four construction methods described above -- not producing a Time::Piece object.
Overriding _mktime() does not work since it also operates as a function. It's difficult to track isa relationships on procedural methods, because a) they aren't methods, and b) Time::Piece hard codes its blessings as Time::Piece objects.
Overriding new(), gmtime(), localtime(), and all arithmetic operators is overkill akin to reimplementation -- undesirable.
After discussions here on the Monastery with brother fever, I decided that the most efficient way to approach the problem (aside from contacting the author and submitting a patch for Time::Piece) was to replace Time::Piece::_mktime() with a friendlier version. The new version accomodates both seconds since epoch or class names as arguments.
This does not solve everything, though, particularly my desire to have new objects blessed into their respective derived classes, no matter how far along the path of inheritance they reside. For that, I ended up having to leave a trail of "inheritance crumbs", sneakily using the import() method for my nefarious purposes. (for the curious that examine the code below, import() is invoked before @ISA relationships are established, hence the delay in actually invoking isa() calls).
Below you will find my results. The first generation subclass, TimeTame.pm, does most of the nasty dirty work. Notice how much easier subsequent subclassing is with TimeFoo.pm (the exports still have to be carried forward, which is right, but I wish there was some sort of "re_export" sort of pragma for Exporter). Also included is a small sample script that prints out some basic inheritance information.
Fellow Brethren. I know I have sinned in this quixotic quest. Set your codes of purity aside for the moment, however, and share with us better solutions.
Matt
| TimeTame.pm |
|
| TimeFoo.pm |
|
| tt.pl |
|
|
|---|