Subject: | Auto inflate to DateTIme (with patch) |
DateTime::Tiny would be 3098% more useful if it automatically turned
itself into a DateTime object when its asked to do something it doesn't
know how to do, rather than dying. This allows users to instantiate
DateTime::Tiny objects and pass them around without any special concerns.
Attached is a patch which does that. It adds an AUTOLOAD to
DateTime::Tiny which will auto inflate to DateTime if DateTime can do
it. It makes DateTime::Tiny->isa("DateTime") respond true, because it
is functionally indistinguishable from a subclass. I would have done
the same for can() but that would require loading DateTime and that
seemed contrary to the purpose of the module. You may wish to do so to
complete the facade.
The immediate use case for this is MongoDB. The maintainer had
mentioned that performance was being affected by the weight of all the
DateTime objects it creates. A light alternative would speed up the
driver immensely. A lot of applications would benefit. perl5i would
certainly benefit as it makes localtime(), gmtime() and time() return
DateTime objects. That's pretty fatty for just getting the year.
I rejected DateTime::LazyInit because A) its out of date and B) it
doesn't implement stringification so you're likely to wind up using
DateTime anyway.
Subject: | 0001-Make-DateTime-Tiny-autoinflate-if-given-a-method-it-.patch |
From 7443613de64a7d84e3190a5651ec08e7226da11e Mon Sep 17 00:00:00 2001
From: Michael G. Schwern <schwern@pobox.com>
Date: Tue, 27 Jul 2010 21:56:43 -0700
Subject: [PATCH 1/2] Make DateTime::Tiny autoinflate if given a method it does not understand.
This makes it 124% more useful.
---
lib/DateTime/Tiny.pm | 38 ++++++++++++++++++++++++++++++++++++++
t/03_autoinflate.t | 28 ++++++++++++++++++++++++++++
t/04_no_datetime.t | 26 ++++++++++++++++++++++++++
3 files changed, 92 insertions(+), 0 deletions(-)
create mode 100644 t/03_autoinflate.t
create mode 100644 t/04_no_datetime.t
diff --git a/lib/DateTime/Tiny.pm b/lib/DateTime/Tiny.pm
index f9f9d21..4fbc3f9 100644
--- a/lib/DateTime/Tiny.pm
+++ b/lib/DateTime/Tiny.pm
@@ -367,6 +367,44 @@ sub DateTime {
);
}
+# Say we're a DateTime object, since we act like one.
+# Doing can() would require actually loading DateTime just to check
+# which would violate the point of this module.
+sub isa {
+ my($proto, $isa) = @_;
+ return 1 if $isa eq 'DateTime';
+ return 1 if $proto->SUPER::isa($isa);
+ return 0;
+}
+
+our $AUTOLOAD;
+sub AUTOLOAD {
+ my $self = $_[0];
+
+ my($method) = $AUTOLOAD =~ /::([^:]+)$/;
+
+ # Avoid autoloading any magic methods like DESTROY
+ return if $method =~ /^[A-Z]+$/;
+ return if $method eq 'import';
+
+ # Inflate
+ my $dt = $self->DateTime;
+
+ # Check DateTime can do it.
+ my $code = $dt->can($method) or do {
+ require Carp;
+ Carp::croak( sprintf q[Can't locate object method "%s" via package "%s"], $method, ref $self );
+ };
+
+ # Stuff the contents of DateTime into ourself and rebless.
+ # Then $self will be a real DateTime object without the caller knowing otherwise.
+ %$self = %$dt;
+ bless $self, "DateTime";
+
+ # And away we go.
+ goto &$code;
+}
+
1;
=pod
diff --git a/t/03_autoinflate.t b/t/03_autoinflate.t
new file mode 100644
index 0000000..cc0ef92
--- /dev/null
+++ b/t/03_autoinflate.t
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More;
+
+plan skip_all => "DateTime needed to test autoinflation" unless eval { require DateTime };
+plan "no_plan";
+
+use DateTime::Tiny;
+
+
+# Test implicit auto inflation
+{
+ my $date = DateTime::Tiny->new(
+ year => 2010,
+ month => 12,
+ day => 31,
+ );
+
+ isa_ok $date, "DateTime::Tiny";
+ isa_ok $date, "DateTime";
+
+ is $date->month_name, "December";
+
+ is ref $date, "DateTime", "automatic inflation";
+}
diff --git a/t/04_no_datetime.t b/t/04_no_datetime.t
new file mode 100644
index 0000000..b23591c
--- /dev/null
+++ b/t/04_no_datetime.t
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More;
+
+plan skip_all => "Tests to run without DateTime" if eval { require DateTime };
+plan "no_plan";
+
+use DateTime::Tiny;
+
+
+# Try to inflate a DateTime::Tiny object without DateTime available
+{
+ my $date = DateTime::Tiny->new(
+ year => 2010,
+ month => 12,
+ day => 31,
+ );
+
+ isa_ok $date, "DateTime::Tiny";
+ isa_ok $date, "DateTime";
+
+ ok !eval { $date->month_name };
+}
--
1.7.2
Subject: | 0002-Document-auto-inflation.patch |
From 8a04bf4c95e1352f79f93b96564ce97da0ec895b Mon Sep 17 00:00:00 2001
From: Michael G. Schwern <schwern@pobox.com>
Date: Tue, 27 Jul 2010 22:00:22 -0700
Subject: [PATCH 2/2] Document auto inflation
---
lib/DateTime/Tiny.pm | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/lib/DateTime/Tiny.pm b/lib/DateTime/Tiny.pm
index 4fbc3f9..1c83c12 100644
--- a/lib/DateTime/Tiny.pm
+++ b/lib/DateTime/Tiny.pm
@@ -409,6 +409,20 @@ sub AUTOLOAD {
=pod
+=head1 Automatic DateTime Upgrade
+
+If a DateTime::Tiny object is asked to perform a method it cannot
+handle but DateTime can, it will automatically load DateTime and turn
+itself into a DateTime object. This will happen transparently to the
+user. For example:
+
+ my $dt = DateTime::Tiny->now;
+
+ # month_name is not a method of DateTime::Tiny but it is of
+ # DateTime. After this method call, $dt is a DateTime object.
+ print $dt->month_name;
+
+
=head1 SUPPORT
Bugs should be reported via the CPAN bug tracker at
--
1.7.2