Skip Menu |

This queue is for tickets about the Moose CPAN distribution.

Report information
The Basics
Id: 40848
Status: rejected
Priority: 0/
Queue: Moose

People
Owner: stevan.little [...] gmail.com
Requestors: megamic [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 0.61
Fixed in: (no value)



Subject: Inherited modified attributes destroy up-chain method-modifiers
I recently had had a need to use the 'has +$name' syntax to modify the ISA option of an inherited attribute. It turned out that in the base class, I had used 'before' to wrap an accessor. I noticed that when I then called the accessor from an instance of my extended class, the before routine was not being called. The issue is obviously that the 'has +$name' will re-install all the accessors over again, effectively destroying any other methods that may have been installed in their place from the base class, method-modifiers such as before,after,etc. being prime examples. Note that this issue also affects MooseX::Attribute helpers, in that the 'provides' and 'curries' handlers will re-install methods in the class, again overwriting method modifiers from the base class. In fact, this is really the situation I had experienced: I had 'provides' install a 'get_thing' method on an attribute, which was wrapped by a 'before' method, and was subsequently destroyed by the extended class' 'has' method re-installing all the 'provides' methods. I think this qualifies as a bug, as it is really unexpected behavior, 'before' methods are ordinarily carried through inheritance, and there is nothing implicit in changing an attribute (say 'required') with 'has +$name' which would suggest that all your method modifying get destroyed. The following code illustrates the problem: package Foo; use Moose; has name => (is => 'rw', isa => 'Str'); before name => sub { warn "before\n" }; 1; package Bar; use Moose; extends 'Foo'; has '+name' => (required => 0); 1; my $foo = new Foo; my $bar = new Bar; $foo->name('bob'); // This prints 'before'... $bar->name('jim'); // this doesnt.
I do not consider this a bug, an annoyance maybe, but not a bug. As you figured out, the +name syntax copies the superclass attribute and then installs it in the local class, overriding everything that was inherited. Because of this, +name should not be considered to be so magical as to know what you may and may not inherit. It is sort of "brute force" in that sense. My feeling is that the complexity of making it more magical and all the permutations of interactions that would result would be less intuitive and more difficult to work with and work around. As you discovered when you encountered this bug, what was actually happening was pretty obvious and simple, thus easier to work with and work around. However, if you disagree with me, I am willing to discuss this in more detail, but please take it to the moose mailing list (moose@perl.org) so that the rest of the community can take part of it. Thanks, - Stevan On Wed Nov 12 02:15:39 2008, MEGAMIC wrote: Show quoted text
> I recently had had a need to use the 'has +$name' syntax to modify the > ISA option of an inherited attribute. It turned out that in the base > class, I had used 'before' to wrap an accessor. I noticed that when I > then called the accessor from an instance of my extended class, the > before routine was not being called. > > The issue is obviously that the 'has +$name' will re-install all the > accessors over again, effectively destroying any other methods that may > have been installed in their place from the base class, method-modifiers > such as before,after,etc. being prime examples. > > Note that this issue also affects MooseX::Attribute helpers, in that the > 'provides' and 'curries' handlers will re-install methods in the class, > again overwriting method modifiers from the base class. In fact, this is > really the situation I had experienced: I had 'provides' install a > 'get_thing' method on an attribute, which was wrapped by a 'before' > method, and was subsequently destroyed by the extended class' 'has' > method re-installing all the 'provides' methods. > > I think this qualifies as a bug, as it is really unexpected behavior, > 'before' methods are ordinarily carried through inheritance, and there > is nothing implicit in changing an attribute (say 'required') with 'has > +$name' which would suggest that all your method modifying get destroyed. > > The following code illustrates the problem: > package Foo; > use Moose; > > has name => (is => 'rw', isa => 'Str'); > > before name => sub { warn "before\n" }; > > 1; > > package Bar; > use Moose; > > extends 'Foo'; > > has '+name' => (required => 0); > > 1; > > my $foo = new Foo; > my $bar = new Bar; > > $foo->name('bob'); // This prints 'before'... > $bar->name('jim'); // this doesnt.
CC: moose [...] perl.org
Subject: Re: [rt.cpan.org #40848] Inherited modified attributes destroy up-chain method-modifiers
Date: Sat, 15 Nov 2008 12:31:32 -0800
To: bug-Moose [...] rt.cpan.org
From: "Michael Potter" <megamic [...] gmail.com>
On Fri, Nov 14, 2008 at 6:28 PM, Stevan Little via RT <bug-Moose@rt.cpan.org Show quoted text
> wrote:
Show quoted text
> <URL: http://rt.cpan.org/Ticket/Display.html?id=40848 > > > I do not consider this a bug, an annoyance maybe, but not a bug.
After looking a bit more deeply about how Moose handles attribute inheritance, I tend to agree with you now. I was originally thinking the 'has +' notation modified the base class's attribute (but in a way only visible to the derived class), but now I realize it just creates a new attribute in the derived class with options copied from the base class, and everything that comes with that. Show quoted text
> > > As you figured out, the +name syntax copies the superclass attribute and > then installs it in the local > class, overriding everything that was inherited. Because of this, +name > should not be considered to be > so magical as to know what you may and may not inherit. It is sort of > "brute force" in that sense.
Awww...I like magic! Show quoted text
> > > My feeling is that the complexity of making it more magical and all the > permutations of interactions that > would result would be less intuitive and more difficult to work with and > work around. As you discovered > when you encountered this bug, what was actually happening was pretty > obvious and simple, thus easier > to work with and work around.
Fine. I would love to know a straightforward work-around though, that does not drastically alter class structures. Would it be to much to ask for an option to 'has +' that simply disables any accessor generation (and provides/curries methods), that is totally *use-at-own-risk* and would assume all the appropriate methods were installed in the base class? Show quoted text
> > > However, if you disagree with me, I am willing to discuss this in more > detail, but please take it to the > moose mailing list (moose@perl.org) so that the rest of the community can > take part of it.
Ok I will CC my reply to this to the list. Show quoted text
> > > Thanks, > > - Stevan > > > > > On Wed Nov 12 02:15:39 2008, MEGAMIC wrote:
> > I recently had had a need to use the 'has +$name' syntax to modify the > > ISA option of an inherited attribute. It turned out that in the base > > class, I had used 'before' to wrap an accessor. I noticed that when I > > then called the accessor from an instance of my extended class, the > > before routine was not being called. > > > > The issue is obviously that the 'has +$name' will re-install all the > > accessors over again, effectively destroying any other methods that may > > have been installed in their place from the base class, method-modifiers > > such as before,after,etc. being prime examples. > > > > Note that this issue also affects MooseX::Attribute helpers, in that the > > 'provides' and 'curries' handlers will re-install methods in the class, > > again overwriting method modifiers from the base class. In fact, this is > > really the situation I had experienced: I had 'provides' install a > > 'get_thing' method on an attribute, which was wrapped by a 'before' > > method, and was subsequently destroyed by the extended class' 'has' > > method re-installing all the 'provides' methods. > > > > I think this qualifies as a bug, as it is really unexpected behavior, > > 'before' methods are ordinarily carried through inheritance, and there > > is nothing implicit in changing an attribute (say 'required') with 'has > > +$name' which would suggest that all your method modifying get destroyed. > > > > The following code illustrates the problem: > > package Foo; > > use Moose; > > > > has name => (is => 'rw', isa => 'Str'); > > > > before name => sub { warn "before\n" }; > > > > 1; > > > > package Bar; > > use Moose; > > > > extends 'Foo'; > > > > has '+name' => (required => 0); > > > > 1; > > > > my $foo = new Foo; > > my $bar = new Bar; > > > > $foo->name('bob'); // This prints 'before'... > > $bar->name('jim'); // this doesnt.
> > > >