[OT] Wrapping methods

2009-07-06 Thread Nicholas Clark
Sorry to mention the 4 letter word...

I'm not sure if there is a good answer to this, but:

Say I have a system of interchangeable components defined by

   @ISA = 'Generic::Base::Class';

   sub run {
... # does stuff
   }


such that it's invoked to do its work as $object->run(...);

Is there any not-insanely-hacky way to conjure up a wrapper class
Generic::Base::ClassWithTimeout

such that a component could be written as 

   @ISA = 'Generic::Base::ClassWithTimeout';

   sub run {
... # does stuff
   }


called, as ever, as $object->run(...);

but Generic::Base::ClassWithTimeout has done something funky to wrap &run
in a routine that does the eval block/alarm/$SIG{ALARM} handler?

Basically, the fun is that I'd like to do something equivalent to invert the
order of inheritance, such that the parent's &run seems to be called first,
and it can "SUPER" delegate to its child.


Of course this can also be solved in various other ways, such as having the
code be

   @ISA = 'Generic::Base::ClassWithTimeout';

   sub run_with_timeout {
... # does stuff
   }

and Generic::Base::ClassWithTimeout having a run() method that calls
$self->run_with_timeout(...) inside the alarmed block, but that doesn't feel
as elegant an interface.

Nicholas Clark


Re: [OT] Wrapping methods

2009-07-06 Thread David Cantrell
On Mon, Jul 06, 2009 at 05:06:03PM +0100, Nicholas Clark wrote:

> but Generic::Base::ClassWithTimeout has done something funky to wrap &run
> in a routine that does the eval block/alarm/$SIG{ALARM} handler?
> Basically, the fun is that I'd like to do something equivalent to invert the
> order of inheritance, such that the parent's &run seems to be called first,
> and it can "SUPER" delegate to its child.

You can't use inheritance for this.  You need to detect whenever a class
is loaded - perhaps by doing some Voodoo in @INC or %INC.  Then to
figure out which classes inherit from your class, use Class::CanBeA, and
then Hook::LexWrap or something similar to wrap its run() method.

It sounds like this would be terribly fragile though.  eg, if run() is
AUTOLOADed or otherwise defined at runtime.

-- 
David Cantrell | London Perl Mongers Deputy Chief Heretic

Hail Caesar!  Those about to vi ^[ you!


Re: [OT] Wrapping methods

2009-07-06 Thread Joel Bernstein

On 6 Jul 2009, at 17:06, Nicholas Clark wrote:

such that it's invoked to do its work as $object->run(...);

Is there any not-insanely-hacky way to conjure up a wrapper class
Generic::Base::ClassWithTimeout

such that a component could be written as

  @ISA = 'Generic::Base::ClassWithTimeout';

  sub run {
   ... # does stuff
  }


called, as ever, as $object->run(...);

but Generic::Base::ClassWithTimeout has done something funky to wrap  
&run

in a routine that does the eval block/alarm/$SIG{ALARM} handler?


http://search.cpan.org/~drolsky/Moose/lib/Moose/Cookbook/Basics/Recipe6.pod

Basically, the fun is that I'd like to do something equivalent to  
invert the
order of inheritance, such that the parent's &run seems to be called  
first,

and it can "SUPER" delegate to its child.


That's exactly what the augment/inner feature of Moose provides, if  
I've understood you correctly.


Of course you might not be ready to drink the Moose kool-aid and I  
dare say the list will come up with other ways, but this seems like a  
good option.


Part of me says that inheritance is not the correct way to model this,  
at all. Yet I suspect you've got some methods which can hang, and  
where you want to inject from outside the ability to have those  
methods time out, without fiddling with the code of those classes. I  
don't know a good way to do what you asked, without significantly  
expanding the scope of the question or without knowing more about the  
actual problem domain.


/joel


Re: [OT] Wrapping methods

2009-07-06 Thread Ask Bjørn Hansen


On Jul 6, 2009, at 9:06, Nicholas Clark wrote:


Of course this can also be solved in various other ways, such as  
having the

code be

  @ISA = 'Generic::Base::ClassWithTimeout';

  sub run_with_timeout {
   ... # does stuff
  }

and Generic::Base::ClassWithTimeout having a run() method that calls
$self->run_with_timeout(...) inside the alarmed block, but that  
doesn't feel

as elegant an interface.


I'm pretty sure Moose can help with this sort of thing.

I think you can get the API reasonably elegant with just clever naming  
though.  Don't make it "run_with_timeout", but "_run" (or whatever -  
something generic).   The Generic::Base::run can call that  
automatically so it'll Just Work for all the classes (with or without  
wrappers).



 - ask


Re: [OT] Wrapping methods

2009-07-06 Thread Daniel Ruoso
Em Seg, 2009-07-06 às 17:06 +0100, Nicholas Clark escreveu:
> Sorry to mention the 4 letter word...
> I'm not sure if there is a good answer to this, but:
> Say I have a system of interchangeable components defined by
>@ISA = 'Generic::Base::Class';
>sub run { ... }
> such that it's invoked to do its work as $object->run(...);
> Is there any not-insanely-hacky way to conjure up a wrapper class
> Generic::Base::ClassWithTimeout
> such that a component could be written as 
>@ISA = 'Generic::Base::ClassWithTimeout';
>sub run { ... }
> called, as ever, as $object->run(...);

As it was already pointed out, Moose provides you the tools to do that,
but the most sane way to do that is by using a metaclass, that will
allow you to use method modifiers, as you can see in
add_around_method_modifier.

http://search.cpan.org/~drolsky/Class-MOP-0.89/lib/Class/MOP/Class.pm#Method_Modifiers

daniel



Re: [OT] Wrapping methods

2009-07-06 Thread Eden Cardim
On Mon, Jul 6, 2009 at 2:24 PM, Joel Bernstein wrote:
>> Basically, the fun is that I'd like to do something equivalent to invert
>> the
>> order of inheritance, such that the parent's &run seems to be called
>> first,
>> and it can "SUPER" delegate to its child.
>
> That's exactly what the augment/inner feature of Moose provides, if I've
> understood you correctly.

For an alarm I'd say a "before" modifier would be better than a
wrapper of any sort.

-- 
   Eden Cardim   Need help with your Catalyst or DBIx::Class project?
  Code Monkeyhttp://www.shadowcat.co.uk/catalyst/
 Shadowcat Systems Ltd.  Want a managed development or deployment platform?
http://edenc.vox.com/http://www.shadowcat.co.uk/servers/


Re: [OT] Wrapping methods

2009-07-07 Thread Luis Motta Campos
David Cantrell wrote:
> On Mon, Jul 06, 2009 at 05:06:03PM +0100, Nicholas Clark wrote:
> 
>> but Generic::Base::ClassWithTimeout has done something funky to
>> wrap &run in a routine that does the eval block/alarm/$SIG{ALARM}
>> handler? Basically, the fun is that I'd like to do something
>> equivalent to invert the order of inheritance, such that the
>> parent's &run seems to be called first, and it can "SUPER" delegate
>> to its child.
> 
> You can't use inheritance for this.  You need to detect whenever a
> class is loaded - perhaps by doing some Voodoo in @INC or %INC.  Then
> to figure out which classes inherit from your class, use
> Class::CanBeA, and then Hook::LexWrap or something similar to wrap
> its run() method.

I second that.

> It sounds like this would be terribly fragile though.  eg, if run()
> is AUTOLOADed or otherwise defined at runtime.

Good practices dictate that whenever you provide AUTOLOADed methods, you
should also override UNIVERSAL::can() to provide proper information
about the AUTOLOADable methods.

If you have reasonable control over the implementation, I would dare to
contradict David and state that fragility is not a big deal.

My €0.02
-- 
Luis Motta Campos is a software engineer,
Perl Programmer, foodie and photographer.


Re: [OT] Wrapping methods

2009-07-07 Thread Andy Wardley

Nicholas Clark wrote:

Of course this can also be solved in various other ways,

[...]

but that doesn't feel as elegant an interface.


Perhaps not, but IMHO having two distinct methods is the Right Way To Do It.
Anything else runs the risk of being Too Clever By Far[1].

I would have an external run() method to act as an interface and an internal
_run() method to provide the implementation.

sub run {
# setup
$self->_run(@_);
# cleanup
}

That's the simple, vanilla OO approach.  You can tart it up with a bit of
Moose magic if you like, but that should be the dressing on top, not the
cake itself.

That said, I think a better approach in this situation would be to implement
a generic call_method_with_timeout() method/function that you can inherit
from a base class, import from an external module, or mixin using some other
fancy technique.

sub call_method_with_timeout {
my ($self, $method, @args) = @_;

# setup # your timeout code
$self->$method(@args);   # goes around this
# cleanup   # bit here
}

You can then implement run() as a simple call to the above method.

use Your::Utils 'call_method_with_timeout';

sub run {
# easily skimmable, self documenting code, yummy.
shift->call_method_with_timeout( _run => @_ );
}

If a slick API is your thing then another approach is to use a monad object
to represent the timeout wrapper.  Something like this perhaps:

package Method::Timeout;
our $AUTOLOAD;

sub _bind_ {
my ($class, $object) = @_;
bless \$object, ref $class || $class;
}

sub AUTOLOAD {
my $self = shift;
my ($name) = ($AUTOLOAD =~ /([^:]+)$/ );
return if $name eq 'DESTROY';

# setup  # your timeout code
my $result = $$self->$name(@_);  # goes around this
# cleanup# bit here

return $result;
}

Then in your object class (or imported from another module)

use Method::Timeout;

sub timeout {
Method::Timeout->_bind_(@_);
}

The timeout() method returns a Method::Timeout object wrapped around your
$self object.  This wrapper object then delegates all method calls to the
original object, but with the timeout alarm set.

The end result is that you can then call any method with a timeout wrapper
like so:

   $object->timeout->run(@args);

You can also pass additional parameters to the timeout method if you like,
e.g.

   $object->timeout(10)->run(@args);

You can also reuse the timeout object if you need to:

   my $timeout = $object->timeout(10);
   $timeout->foo();
   $timeout->bar();
   $timeout->baz();

And it also works with class methods (as does call_method_with_timeout()):

   my $object = My::Class->timeout->new( connect => $a_slow_server );

I find this approach rather satisfying.  It clearly separates "the method
that you're calling" from "the way you want the method to be called".
Separation of concerns and all that.

Badger::Base implements the try() method this way.  It simply puts an eval { }
wrapper around the method you're calling, effectively downgrading a thrown
exception to an undefined return value [2].

   if ($object->try->some_method(@args)) {
   print "success\n!";
   }
   elsif ($@) {
   print "failed: ", $object->error, "\n";
   }
   else {
   print "declined: method returned false value, no error thrown\n";
   }

It's not as elegant as proper try/catch blocks, but I think it's nicer
than the Old Skool approach of wrapping eval { } blocks around the method
call.

   if (eval { $object->method(@args) }) {   # Not as nice, IMHO
   ...
   }

But I digress...

Whichever way you do it, I think it's important to separate the underlying
action being performed (the _run() method or whatever you call it) from the
additional timeout behaviour that is orthogonal to it.

Cheers
A


[1] i.e. clever enough to confuse others

[2] Returning undef to indicate failure is a Bad Thing.  Have your methods
throw exceptions by default and let the caller make the decision to
ignore/handle them (using try(), eval { } or whatever) when appropriate.
(for the grandmas who don't already know how to suck eggs :-)