Class::Tiny and refactor _getglob out into _Utils
Matt S Trout [Sat, 6 Nov 2010 22:02:10 +0000 (22:02 +0000)]
lib/Class/Tiny.pm [new file with mode: 0644]
lib/Class/Tiny/Object.pm [new file with mode: 0644]
lib/Class/Tiny/_Utils.pm [new file with mode: 0644]
lib/Role/Tiny.pm
lib/Sub/Defer.pm
t/class-tiny.t [new file with mode: 0644]

diff --git a/lib/Class/Tiny.pm b/lib/Class/Tiny.pm
new file mode 100644 (file)
index 0000000..1bc127c
--- /dev/null
@@ -0,0 +1,29 @@
+package Class::Tiny;
+
+use strictures 1;
+use Class::Tiny::_Utils;
+
+sub import {
+  my $target = caller;
+  *{_getglob("${target}::extends")} = sub {
+    *{_getglob("${target}::ISA")} = \@_;
+  };
+  *{_getglob("${target}::with")} = sub {
+    require Role::Tiny;
+    die "Only one role supported at a time by with" if @_ > 1;
+    Role::Tiny->apply_role_to_package($_[0], $target);
+  };
+  foreach my $type (qw(before after around)) {
+    *{_getglob "${target}::${type}"} = sub {
+      _install_modifier($target, $type, @_);
+    };
+  }
+  {
+    no strict 'refs';
+    @{"${target}::ISA"} = do {
+      require Class::Tiny::Object; ('Class::Tiny::Object');
+    } unless @{"${target}::ISA"};
+  }
+}
+
+1;
diff --git a/lib/Class/Tiny/Object.pm b/lib/Class/Tiny/Object.pm
new file mode 100644 (file)
index 0000000..e3e31c7
--- /dev/null
@@ -0,0 +1,16 @@
+package Class::Tiny::Object;
+
+use strictures 1;
+
+sub new {
+  my $class = shift;
+  bless({ @_ }, $class);
+}
+
+sub does {
+  require Role::Tiny;
+  { no warnings 'redefine'; *does = \&Role::Tiny::does_role }
+  goto &Role::Tiny::does_role;
+}
+
+1;
diff --git a/lib/Class/Tiny/_Utils.pm b/lib/Class/Tiny/_Utils.pm
new file mode 100644 (file)
index 0000000..1131e1e
--- /dev/null
@@ -0,0 +1,20 @@
+package Class::Tiny::_Utils;
+
+use strictures 1;
+use base qw(Exporter);
+
+our @EXPORT = qw(_getglob _install_modifier);
+
+sub _getglob { no strict 'refs'; \*{$_[0]} }
+
+sub _install_modifier {
+  require Class::Method::Modifiers;
+  my ($into, $type, $name, $code) = @_;
+  my $ref = ref(my $to_modify = $into->can($name));
+  if ($ref && $ref =~ /Sub::Defer::Deferred/) {
+    require Sub::Defer; undefer($to_modify);
+  }
+  Class::Method::Modifiers::install_modifier(@_);
+}
+
+1;
index 7883099..38ddb82 100644 (file)
@@ -1,13 +1,11 @@
 package Role::Tiny;
 
 use strictures 1;
-use Class::Method::Modifiers ();
+use Class::Tiny::_Utils;
 
 our %INFO;
 our %APPLIED_TO;
 
-sub _getglob { no strict 'refs'; \*{$_[0]} }
-
 sub import {
   my $target = caller;
   # get symbol table reference
@@ -78,7 +76,7 @@ sub apply_role_to_package {
   }
 
   foreach my $modifier (@{$info->{modifiers}||[]}) {
-    Class::Method::Modifiers::install_modifier($to, @{$modifier});
+    _install_modifier($to, @{$modifier});
   }
 
   # only add does() method to classes and only if they don't have one
index 11430fa..15f2cee 100644 (file)
@@ -2,13 +2,12 @@ package Sub::Defer;
 
 use strictures 1;
 use base qw(Exporter);
+use Class::Tiny::_Utils;
 
 our @EXPORT = qw(defer undefer);
 
 our %DEFERRED;
 
-sub _getglob { no strict 'refs'; \*{$_[0]} }
-
 sub undefer {
   my ($deferred) = @_;
   my ($target, $maker, $undeferred_ref) = @{
diff --git a/t/class-tiny.t b/t/class-tiny.t
new file mode 100644 (file)
index 0000000..85070c3
--- /dev/null
@@ -0,0 +1,78 @@
+use strictures 1;
+use Test::More;
+
+{
+  package MyClass0;
+
+  BEGIN { our @ISA = 'ZeroZero' }
+
+  use Class::Tiny;
+}
+
+BEGIN {
+  is(
+    $INC{'Class/Tiny/Object.pm'}, undef,
+    'Object.pm not loaded if not required'
+  );
+}
+
+{
+  package MyClass1;
+
+  use Class::Tiny;
+}
+
+is_deeply(
+  [ @MyClass1::ISA ], [ 'Class::Tiny::Object' ], 'superclass defaulted'
+);
+
+{
+  package MyClass2;
+
+  use base qw(MyClass1);
+  use Class::Tiny;
+}
+
+is_deeply(
+  [ @MyClass2::ISA ], [ 'MyClass1' ], 'prior superclass left alone'
+);
+
+{
+  package MyClass3;
+
+  use Class::Tiny;
+
+  extends 'MyClass2';
+}
+
+is_deeply(
+  [ @MyClass3::ISA ], [ 'MyClass2' ], 'extends sets superclass'
+);
+
+{
+  package MyClass4;
+
+  use Class::Tiny;
+
+  extends 'WhatTheFlyingFornication';
+
+  extends qw(MyClass2 MyClass3);
+}
+
+is_deeply(
+  [ @MyClass4::ISA ], [ qw(MyClass2 MyClass3) ], 'extends overwrites'
+);
+
+{
+  package MyClass5;
+
+  use Class::Tiny;
+
+  sub foo { 'foo' }
+
+  around foo => sub { my $orig = shift; $orig->(@_).' with around' };
+}
+
+is(MyClass5->foo, 'foo with around', 'method modifier');
+
+done_testing;