Got it to work by changing line 94 of Moose/Meta/Method/Delegation
From
   my $accessor = $self->associated_attribute->get_read_method_ref;

To
   my $accessor = $self->associated_attribute->get_read_method;


This appears to make the delegator run the accessor fully
and not just retrieve the value set in the attribute.



So now I can do...

has 'foo' => (is=>'rw', handles=>[qw/meth1 meth2/]);

around 'foo' => sub {
  my ($next,$self) = (shift,shift);

  my $obj = $self->$next(@_);

  return ref($res) ? $res : Foo->new();
}


* If foo is provided, it will be held and used to delegate to.
* If not provided, a new Foo object will be created each time to delegate to.
* Since Foo is a Singleton it will return the same object.
* A reference of this Foo object wont be stored in the containing object,
  keeping the object serialisation clean.
* If required, the 'around' method can now dynamically decide
  what kind of object to delegate to.








Tom Howe
Morgan Stanley | Technology
25 Cabot Square | Canary Wharf | Floor 03
London, E14 4QA
Phone: +44 20 7425-9380
tom.h...@morganstanley.com


> -----Original Message-----
> From: Howe, Tom (IT)
> Sent: 15 January 2009 13:56
> To: ch...@prather.org
> Cc: Sartak; Yuval Kogman; Stevan Little; moose@perl.org
> Subject: RE: Possible to use coerce and auto_deref?
>
> Just to follow up re delegation,
> I found in the code for Moose::Meta::Method::Delegation...
>
>
> sub _initialize_body {
>     my $self = shift;
>
>     my $method_to_call = $self->delegate_to_method;
>     return $self->{body} = $method_to_call
>         if ref $method_to_call;
>
>     my $accessor = $self->_get_delegate_accessor;         <
> ======= gets coderef to access raw value
>
>     my $handle_name = $self->name;
>
>     # NOTE: we used to do a goto here, but the goto didn't handle
>     # failure correctly (it just returned nothing), so I took that
>     # out. However, the more I thought about it, the less I liked it
>     # doing the goto, and I prefered the act of delegation being
>     # actually represented in the stack trace.  - SL
>     $self->{body} = sub {
>         my $instance = shift;
>         my $proxy    = $instance->$accessor();
> <=== gets raw value
>         ( defined $proxy )
>             || $self->throw_error(
>             "Cannot delegate $handle_name to $method_to_call because "
>                 . "the value of "
>                 . $self->name
>                 . " is not defined",
>             method_name => $method_to_call,
>             object      => $instance
>             );
>         $proxy->$method_to_call(@_);
>     <====== runs against raw value
>     };
> }
>
> sub _get_delegate_accessor {
>     my $self = shift;
>
>     my $accessor =
> $self->associated_attribute->get_read_method_ref;  <==
> retrieves the coderef to access raw value
>
>     $accessor = $accessor->body if blessed $accessor;
>
>     return $accessor;
> }
>
>
>
> This means that if the value is not defined it will fail to
> delegate to it. Makes sense except in my situation.
>
> One option would be to get the value post 'around' processing.
>
> Maybe this would break other things though??
>
>
>
>
>
>
>
> > -----Original Message-----
> > From: Chris Prather [mailto:perig...@gmail.com]
> > Sent: 14 January 2009 19:41
> > To: Howe, Tom (IT)
> > Cc: Sartak; Yuval Kogman; Stevan Little; moose@perl.org
> > Subject: Re: Possible to use coerce and auto_deref?
> >
> > On Wed, Jan 14, 2009 at 1:06 PM, Howe, Tom (IT)
> > <tom.h...@morganstanley.com> wrote:
> > > I have something like this:
> > >
> > > subtype 'Thing'
> > >  => as 'Object'
> > >  => where { $_->isa('ThingClass') };
> > >
> > > coerce 'Thing'
> > >  => from 'Str'
> > >  => via { ThingClass->new ( xyz=> $_ ) };
> > >
> > >
> > > has 'things' => (
> > >  is=>'rw',
> > >  isa=>'ArrayRef[Thing]',
> > >  lazy=>1,
> > >  coerce=>1,
> > >  auto_deref=>1,
> > >  default => sub { [] },
> > > };
> > >
> > >
> > > But if I pass in a list of strings to convert to Things, I
> > get the error:
> > >
> > > "Cannot coerce without a type coercion"
> > >
> > > I've managed to get around it by defining a type 'ArrayRef[Thing]'
> > >
> > > Eg
> > > Subtype 'ArrayRef[Thing]'
> > >  => as 'ArrayRef'
> > >  => where {
> > >        foreach (@{$_} ....
> > >     };
> > >
> > > Coerce 'ArrayRef[Thing]'
> > >  => from 'ArrayRef'
> > >
> > > But this means
> > > A) I'm essentially reimplementing ArrayRef each time.
> > > B) It always coerces!
> >
> > isa => 'Arrayref[Thing]'  is setting up the subtype for you,
> > so you don't need to do that explicitly You will need to
> > create the coercion however, and yes every time you enable
> > coerce => 1 Moose will look for an applicable coercion (for
> > example: from ArrayRef to ArrayRef[Thing]) and apply it.
> >
> > > It should ideally check before it coerces...
> > >
> > > Anything I can do to improve on this?
> >
> > What are you hoping it will check?
> >
> > Moose doesn't do any kind of "Deep Coercion" because the edge
> > cases become far to messy to handle in an elegant way ... or
> > at least so far as anybody has thought of. What you are doing
> > seems to be the right solution ... with my comments about the
> > "subtype ArrayRef[Thing]"
> > statment being optionally implicit.
> >
> > > Also, I'd like to be able to declare something like
> > >
> > > has 'foo' => ( is=>'rw', delegate=> sub { Foo->instance
> }, handles
> > > =>[qw/x y z/] )
> > >
> > > Where, if no value is passed in to foo() on construction,
> > the accessor created for foo() will always trigger the
> > delegate sub but will not store anything in the object in the
> > way default does.
> > >
> > >
> > > I tried this ..
> > >
> > > has 'foo' => ( is=>'rw', isa=>'Object', handles=> [qw/meth1 meth2
> > > meth3/]);
> > >
> > > around 'foo' => sub {
> > >  my ($next,$self,@args) = @_;
> > >  if (@args) {
> > >    return $self->$next(@args);
> > >  } else {
> > >    return $self->$next() || Foo->instance();  } };
> > >
> > > But got error:
> > >
> > > Cannot delegate meth1 to meth1 because the value of foo is
> > not defined...
> > >
> > >
> > > Is there some way to do this?
> >
> > Uh ... not if I'm understanding what you're asking for properly.
> >
> > You want an attribute accessor to sometimes store and Object,
> > and sometimes just call a Class Method on some random class
> > if the attribute isn't set? You'll have to write that yourself:
> >
> > has foo => ( is => 'rw', predicate => '_has_foo', handles =>
> > [qw(one two three)] );
> >
> > around [qw(one two three)] => sub {
> >     my ($next, $self) = (shift, shift);
> >     return Foo->instance->$next(@_) unless $self->_has_foo;
> >     return $self->$next(@_);
> > };
> >
> > You could probably wrap this up in a MooseX module with some
> > sugar if you find your using it a lot, but I don't understand
> > why you'd want to do that in the first place.
> >
> > -Chris
> >
--------------------------------------------------------

NOTICE: If received in error, please destroy and notify sender. Sender does not 
intend to waive confidentiality or privilege. Use of this email is prohibited 
when received in error.

Reply via email to