Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Support for attributes:get in Attribute::Handlers?

by Thilosophy (Curate)
on Feb 15, 2009 at 10:25 UTC ( #743930=perlquestion: print w/ replies, xml ) Need Help??
Thilosophy has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

Attribute::Handlers makes it easier to work with subroutine attributes, which is otherwise (or even then) messy business.

However, it does not seem to integrate with the built-in attributes::get, which is supposed to list attributes for a given subroutine.

In the following code, attributes::get returns only the built-in attribute method, but not the Attribute::Handlers attribute Loud.

use strict; package LoudDecl; use Attribute::Handlers; use attributes; sub Loud :ATTR { print "NOISE!\n"; } sub foo: Loud method { print 1; } print "Attributes: "; print attributes::get(\&foo); print "\n";
Is there an easy way to get get to work?

Comment on Support for attributes:get in Attribute::Handlers?
Select or Download Code
Re: Support for attributes:get in Attribute::Handlers?
by shmem (Canon) on Feb 15, 2009 at 18:28 UTC


    use strict; package LoudDecl; use Attribute::Handlers; use attributes; sub FETCH_CODE_ATTRIBUTES { qw(Loud) } sub Loud :ATTR { print "NOISE!\n"; } sub foo: Loud method { print 1; } print "Attributes: "; print join ', ', attributes::get(\&foo); print "\n"; __END__ NOISE! Attributes: method, Loud

    See attributes, section "Package-specific Attribute Handling".

    update: if you define multiple attributes, you might want to return the right attributes associated to the sub questioned:

    use strict; package LoudDecl; use Attribute::Handlers; use attributes; use Scalar::Util; my %attrs; sub FETCH_CODE_ATTRIBUTES { @{$attrs{Scalar::Util::refaddr($_[1])}}; } sub Loud :ATTR { print "NOISE!\n"; no strict 'refs'; push @{$attrs{Scalar::Util::refaddr($_[2])}}, 'Loud'; } sub Boom :ATTR { print "BOOM!\n"; no strict 'refs'; push @{$attrs{Scalar::Util::refaddr($_[2])}}, 'Boom'; } sub foo: Loud method { print 1; } sub bar : Loud Boom method { print "pling.\n"; } print "Attributes: "; print join ', ', attributes::get(\&bar); print "\n"; __END__ NOISE! NOISE! BOOM! Attributes: method, Loud, Boom
      shmem++ for this, although this is basically hand-rolling the support, without any help from Attribute::Handlers. It is also rather intrusive, in that you have to amend the original attribute handlers, which could be a problem if those are located in someone else's modules.

      I suppose this is how it is with attributes...

        Thank you. - yes, this is hand-rolling, a workaround, and IMHO Attribute::Handlers should shoehorn FETCH_<type>_ATTRIBUTES methods into the caller's namespace, if not already there yet. Best thing to do would be patching Attribute::Handlers, but since I haven't done much with attributes yet, and haven' got (yet) a full understanding neither of what's going on inside Attribute::Handlers nor why it is written as it is; and since the POD states

        There are undoubtedly serious bugs lurking somewhere in code this funky :-) Bug reports and other feedback are most welcome.

        it is quite easy for me to abstain from adding bugs - and patching code of TheDamian's make is a terrible thing to do... ;-)

        You could send TheDamian "other feedback", though.

Re: Support for attributes:get in Attribute::Handlers?
by ELISHEVA (Prior) on Feb 15, 2009 at 18:32 UTC
    I don't think so, not in a general way.According to the Perl documentation on attributes, attribute::get calls a per-package routine FETCH_CODE_ATTRIBUTES to get code attributes for that package. As near as I can tell looking at the source code for Attribute::Handlers, this function is never defined when it processes the BEGIN, CHECK, etc blocks for each package. Hence, attribute::get has no way of knowing about the attributes you defined via Attribute::Handlers.

    You could, of course, define the function yourself, except that you would have to patch the module code, hardly "easy". Attribute::Handlers stores all of its attribute definitions in a private-to-package my variable @aDeclarations. There is no way to get at this data except by working within the package itself, i.e. patching it.

    You could explicitly push the attributes into your own array each time you define a routine, as shmem suggests, but then you have to define to your team (or the coder that comes after you) that this is the "pattern" for how we define these functions and either write it down somewhere (will anyone read it?) or make it part of team memory (how?). That isn't always reliable - someone is bound to forget, leading to hard to track down bugs. So it is difficult in a different way.

    There is another important reason for encapsulating your handling of attributes in a module rather than explicitly defining a FETCH routine in each package. The interface for attribute handling, including the FETCH method is, to quote the perldocs:

    WARNING: the mechanisms described here are still experimental. Do not rely on the current implementation.

    Personally, I prefer solutions where the structure itself prevents human error and minimizes the impact of interface change, so I would go for a solution that encapsulates how Perl comes to understand your handlers exist and lets you define nothing other than attribute functionality in your attribute handlers. It turns out it is pretty easy and short (about 20-25 lines), if you make use of the current interface. That interface did not exist when Attribute::Handlers was first written in 2001, which is part of the reason it is so much longer.

    First I would define a small module, let's call it Monks::AttributeDispatcher, to replace mod:://Attribute::Handlers. This module's use statement would accept a dispatch table as the parameters for its use clause and would manufacture the calling package's FETCH_CODE_ATTRIBUTES and MODIFY_CODE ATTRIBUTES methods. The code that used the module would look like this:

    use strict; use warnings; #here's where you set up your attribute handlers use Monks::AttributeDispatcher Loud => sub { print "NOISY\n"; } , Boom => sub { print "BOOM\n"; } ; #here's where you show off your handlers sub foo: Loud method { print 1; } sub bar : Loud Boom method { print "pling.\n"; } print "Attributes: "; print join ', ', attributes::get(\&bar); print "\n";

    The Monks::AttributeDispatcher module would look like this:

    Note 1:Your handler gets three parameters: the name of the package where the subroutine is defined, the code reference of the subroutine, and the name of the attribute. This permits you to write context aware handlers, should you wish.

    Note 2: If your subroutines are too complex to define in-line (or you want two attributes to share the same handler) you can always define the handler subroutines in a BEGIN {} block before your use Monks::Dispatcher statement.

    The following code illustrates both notes 1&2:

    Caveat 1: This version assumes you only care about subroutine attributes handlers since that was all you mentioned in your post. It would be relatively easy to modify it to have it emulate Attribute::Handlers's ability to selectively apply handlers to more than one type of object (SCALAR, CODE, etc). I would (a) replace the value of each hash element with a 2-element array reference indicate type and code reference. (b) revise the module to make use of that information. There are separate FETCH and MODIFY methods for each type of reference.

    Caveat 2: I've been playing around with this some more and the one thing I can't seem to make Monks::AttributeDispatcher do is replace definition of function foo(...) with something else (I can,however, generate new functions that call foo(...).). Attribute::Handlers can do this easily because it gets the *name* of the subroutine rather than a code reference. MODIFY_CODE_ATTRIBUTES only gets the code reference. If there is a Perl trick to work around this, it is missing from my bag-o-tricks. If this is important to you and there is no work around, it looks to me like your only "non-intrusive" choice would be to patch Attribute::Handlers so that it generates the FETCH method.

    Best, beth

    Update: posted at the same time as shmem, added discussion of shmem's post.

    Update 2: Added question about why using Attribute::Handlers.

    Update 2:posted alternate to Attribute::Handlers - it is not nice to suggest one should make a patch without showing how.

    Update 3:added second caveat.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://743930]
Approved by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (15)
As of 2014-09-18 13:14 GMT
Find Nodes?
    Voting Booth?

    How do you remember the number of days in each month?

    Results (115 votes), past polls