Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Re^4: Why does changing a Moose attributes' properties remove the around modifier

by stvn (Monsignor)
on Jul 18, 2011 at 20:39 UTC ( [id://915278]=note: print w/replies, xml ) Need Help??


in reply to Re^3: Why does changing a Moose attributes' properties remove the around modifier
in thread Why does changing a Moose attributes' properties remove the around modifier

It would, however, be pretty nice if Moose could support this sort of behavior natively and have it just work, instead of requiring that sort of workaround.

Well, it has been discussed before, the idea of inheriting method modifiers in subclasses, but the determination always is that this would be dangerous action at a distance. If you want to override a method in a subclass you should not need to worry about whether one of the superclasses in the chain has a method modifier attached to it. While this might be behavior you want now, it is most certainly not a good generic behavior and since method modifiers are generic things, ... you get the idea here.

I would be interested to know what problem you are trying to solve by using an around on an attribute accessor. Often times this kind of stuff can be solved using some other feature of Moose attributes.

-stvn

Replies are listed 'Best First'.
Re^5: Why does changing a Moose attributes' properties remove the around modifier
by mcrose (Beadle) on Jul 18, 2011 at 21:17 UTC

    I use it for keeping objects internally with implicit error checking as anything uncoercible dies, but not exposing them externally. For a contrived example, something like:

    has [ qw/server peer monitor netlog/ ] => ( is => 'ro', isa => 'NetAdd +r::IP', coerce => 1, required => 1 ); coerce 'NetAddr::IP' => from 'Str' => via { NetAddr::IP->new($_, '255. +255.255.255') }; around [ qw/server peer monitor netlog/ ] => sub { my $orig = shift; my $self = shift; my $netaddr_ip = $self->orig(@_); return $netaddr_ip->addr; };

    The above accepds valid single IPs (v4 /32 and v6 /128) and passes them back out as addresses, but avoids the default stringification (CIDR notation) the object by default resolves to. Using the lists w/ around is shorter and easier to read as I don't need to make a seperate shim stub for each object's accessor and means it's less likely future refactoring will forget to stringify the accessor appropriately.

    However, it's not possible to make a more specific derived class instance with default IPs for some values as it kills the 'around' modifier.

    I mean, it's easy enough to say that 'you get an object back, deal with it B)' but if I can inflate passed things to objects as they come in and deflate them back to strings as they go out, why not do it?

    Edit:Actually, why not just add a 'decoerce' or 'deflate' or something to the accessor, a coderef that gets run against a value as the value is being returned? It'd allow a (deflate => sub { $_->clone }) to be applied to a DateTime accessor, for example, to make sure a end user doesn't ->add() to your internal value, etc, while not being spooky action at a distance; it's just another attribute of the accessor that can be changed or ignored or overridden by inheriting classes.

      If you're trying to perform validation, why not just use the facility designed for that?

      use Moose::Util::TypeConstraints; subtype 'Net::IP::Address', as 'Str', where { NetAddr::IP->new($_, '255.255.255.255') }; no Moose::Util::TypeConstraints;
      has [ qw/server peer monitor netlog/ ] => ( is => 'ro', isa => 'Net::IP::Address', required => 1, );

      If we suppose you have a good reason to store the IP address an object, then I still find your approach very poor. Don't try to change the behaviour of an existing getter; create a getter that has the behaviour your want.

      coerce 'NetAddr::IP' => from 'Str' => via { NetAddr::IP->new($_, '255.255.255.255') }; for my $field (qw( server peer monitor netlog )) { my $object = "${field}_object"; has $object => ( init_arg => $field, is => 'ro', isa => 'NetAddr::IP', coerce => 1, required => 1, handles => { $field => sub { $_[0]->$object->addr }, }, ); }

      This makes

      $o->server
      a shortcut for
      $o->server_object->addr

      The constructor takes

      C->new(server => ...)
      rather than
      C->new(server_object => ...)
      thanks to init_arg.

      Thanks to coerce => 1 and the coercion rule, both a NetAddr::IP object and a IP address in string form can be used as the value passed to the constructor.

        Yeah, that's a much better way of behaving. I'd missed the documentation that mentioned 'init_arg'. I do need to read through the Class::MOP stuff some time to make sure I'm not missing out on things like this. Thanks for the pointer.

      So my first concern with this approach is that you are overriding the accessors and returning a value that is in conflict with the type declared in the attribute. This is just bound to confuse someone at some point.

      My second concern is that you are blocking access to the underlying NetAddr::IP objects via the normal and expected accessor methods. This means that if someone wants/needs to get at the NetAddr::IP objects they must use raw HASH access. Essentially your code is forcing someone to do something they very much should not do.

      I suggest you follow ikegami suggestion in Re^6: Why does changing a Moose attributes' properties remove the around modifier and use the 'handles' option to get the addr return value but still keep access to the underlying NetAddr::IP object if you ever need it.

      Actually, why not just add a 'decoerce' or 'deflate' or something to the accessor, a coderef that gets run against a value as the value is being returned? It'd allow a (deflate => sub { $_->clone }) to be applied to a DateTime accessor, for example, to make sure a end user doesn't ->add() to your internal value, etc, while not being spooky action at a distance; it's just another attribute of the accessor that can be changed or ignored or overridden by inheriting classes.

      This might make a good MooseX:: module, I suggest giving it a try (though I think 'deflate' is better then 'decoerce'). It is the policy of the Moose core to not add any feature that has not been successfully implemented and tested as a MooseX:: module, this helps us to avoid running into back-compat issues.

      -stvn

        I may look into trying to write it in some spare time, if only to learn some of the Class::MOP and Moose internals better. Thanks for your feedback.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://915278]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-29 13:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found