Beefy Boxes and Bandwidth Generously Provided by pair Networks Cowboy Neal with Hat
Pathologically Eclectic Rubbish Lister
 
PerlMonks

RFC Class::AppendMethods

by diotalevi (Canon)
 | Log in | Create a new user | The Monastery Gates | Super Search | 
 | Seekers of Perl Wisdom | Meditations | PerlMonks Discussion | 
 | Obfuscation | Reviews | Cool Uses For Perl | Perl News | Q&A | Tutorials | 
 | Poetry | Recent Threads | Newest Nodes | Donate | What's New | 

on Apr 22, 2003 at 05:59 UTC ( #252199=perlquestion: print w/ replies, xml ) Need Help??
diotalevi has asked for the wisdom of the Perl Monks concerning the following question:

RFC Class::AppendMethods

In response to Installing chained methods I created Class::AppendMethods. I'd like to get any general feedback on the module prior to uploading it to CPAN. I'm requesting any suggestions regarding API, coding style or the method used to solve this problem (or even the module name if you feel like it). The module consists of a single subroutine which has the job of either copying a code reference into a slot or if something is already present - installing a wrapper to ensure both the new and old methods are called.

Class::AppendMethods

package Class::AppendMethods; use strict; use warnings; use vars qw(@ISA @EXPORT_OK $VERSION %METHODS); require Exporter; @ISA = qw(Exporter); @EXPORT_OK = qw(append_method); $VERSION = '0.01'; sub append_method { my $method_name = shift; my $method_to_install = shift; no strict 'refs'; no warnings 'redefine'; # If I was given a method name then fetch the code # reference from the named slot unless (ref $method_to_install ) { # symref $method_to_install = \&{$method_to_install}; } # Track the list of references to install unless (exists $METHODS{$method_name}) { $METHODS{$method_name} = []; } my $pre_existing_methods = $METHODS{$method_name}; push @$pre_existing_methods, $method_to_install; # Install the new methods if (*{$method_name}{CODE}) { # If the pre-existing method isn't in the local cache then cop +y it over # first. unless (grep $_ == *{$method_name}{CODE}, @$pre_existing_metho +ds) { unshift @$pre_existing_methods, *{$method_name}{CODE}; } # Already existing method *{$method_name} = sub { $_[0]->$_( @_[1 .. $#_] ) for @$pre_existing_methods; }; } else { # Single method - no special calling *{$method_name} = $method_to_install; } # Return the method as a convenience (for who knows what, I don't +know) return *{$method_name}{CODE}; } 1; __END__ =head1 NAME Class::AppendMethods - Install multiple methods into a single slot =head1 SYNOPSIS use Class::AppendMethods 'append_method'; # This installs both versioning_hook and auditing_hook into the # method Object::pre_insert. for my $hook (qw(versioning auditing)) { append_method( 'Object::pre_insert', "${hook}_hook" ); } sub versioning_hook { ... } sub auditing_hook { ... } =head1 DESCRIPTION This allows you to install more than one method into a single method n +ame. I created this so I could install both versioning and auditing hooks i +nto another module's object space. So instead of creating a single larger +method which incorporates the functionality of both hooks I created C<Class::AppendMethods::append_method> to install a wrapper method as +needed. If only one method is ever installed into a space, it is installed dir +ectly with no wrapper. If you install more than one then C<append_method> cr +eates a wrapper which calls each of the specified methods in turn. =head1 PUBLIC METHODS =over 4 =item append_method append_method( $method_name, $method ); This function takes two parameters - the fully qualified name of the m +ethod to install into and the method to install. C<$method_name> must be the fully qualified method name. This means th +at for the method C<pre_insert> of a C<Foo::Bar> object you must pass in C<'Foo::Bar::pre_insert'>. C<$method> may be either a code reference or the fully qualified name +of the method to use. =back =head2 EXAMPLES =over 4 =item Example 1 use Class::AppendMethods 'append_method'; # This installs both versioning_hook and auditing_hook into the # method Object::pre_insert. for my $hook (qw(versioning auditing)) { append_method( 'Object::pre_insert', "${hook}_hook" ); } sub versioning_hook { ... } sub auditing_hook { ... } =item Example 2 use Class::AppendMethods 'append_method'; my @versioned_tables = ( .... ); my @audited_tables = ( .... ); for my $table_list ( { tables => \ @versioned_tables, prefix => 'versioned' }, { tables => \ @audited_tables, prefix => 'audited' } ) { my $tables = $table_list->{'tables'}; my $prefix = $table_list->{'prefix'}; for my $table ( @$tables ) { for my $hook ( qw[pre_insert pre_update pre_delete]) { my $method_name = "GreenPartyDB::Database::${table}::${ho +ok}"; my $method_inst = __PACKAGE__ . "::${prefix}_${hook}"; append_method( $method_name, $method_inst ); } } } sub versioned_pre_insert { ... } sub versioned_pre_update { ... } sub versioned_pre_delete { ... } sub audited_pre_insert { ... } sub audited_pre_update { ... } sub audited_pre_delete { ... } =back =head2 EXPORT This class optionally exports the C<append_method> function. =cut

Comment on RFC Class::AppendMethods
Download Code
Re: RFC Class::AppendMethods
by diotalevi (Canon) on Apr 22, 2003 at 06:14 UTC

    I should add that I mean for this code meant to be good enough for you to use it for your own production code. Mostly I want to use it for myself but I figure that if it can make it past the gauntlet here then its likely ok. So please, evaluate this like you would potential production code for your system.

Re: RFC Class::AppendMethods
by DrManhattan (Chaplain) on Apr 22, 2003 at 08:27 UTC
    Neat! A couple of suggestions: First, an insert_method() would be a useful addition to append_method(). I'd like to be able to add a subroutine that executes before an existing one instead of after. Also, your example should probably mention that the package of the method to append needs to be specified explicitly when passed as a string or it ends up in Class::AppendMethod and fails to exist. E.g.
    #!/usr/bin/perl use strict; package Dummy; sub test { print "one\n"; } package main; use Class::AppendMethods 'append_method'; append_method("Dummy::test", "post_test"); sub post_test { print "two\n"; } Dummy::test;
    Gives me this output:
    one Undefined subroutine &Class::AppendMethods::post_test called at /home/ +matt/Class/AppendMethods.pm line 43.
    Whereas
    append_method("Dummy::test", "main::post_test");
    or
    append_method("Dummy::test", \&post_test);
    ... behaves as expected.

    -Matt

Re: RFC Class::AppendMethods
by PodMaster (Abbot) on Apr 22, 2003 at 08:43 UTC
    It just hit me, couldn't you do what what Class::AppendMethods does with Class::Delegation by TheDamian?


    MJD says you can't just make shit up and expect the computer to know what you mean, retardo!
    I run a Win32 PPM repository for perl 5.6x+5.8x. I take requests.
    ** The Third rule of perl club is a statement of fact: pod is sexy.

      Yes.... it looks like it might be possible. I didn't know about C::D's multiple method calling functionality. Instead of calling append_method I can use C::D (assuming I know all the methods I want to use at onece, build a bit of perl code and then eval that. I'll keep this in mind but I don't think this is any easier. Thanks PodMaster.

      my @methods_q = map "sub { \$_[0]->GreenPartyDB::Database::$method( \@ +_[ 1 .. \$#_ ] ) }", @methods; my $method_c = join ", ", @method_c; eval qq[ package GreenPartyDB::Database::${table}; use Class::Delegation send => '$hook', to => [ $method_c ];

Login:
Password
remember me
What's my password?
Create A New User

Node Status?
node history
Node Type: perlquestion [id://252199]
Approved by robartes
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (21)
jdporter
holli
Gavin
JavaFan
CardinalNumber
jwkrahn
kennethk
thezip
Eyck
LanX
hangon
pemungkah
state-o-dis-array
ssandv
draegtun
BenHopkins
MikeDexter
jmccarrell
tomerb
David S
Spakz
As of 2010-02-09 22:51 GMT
Sections?
The Monastery Gates
Seekers of Perl Wisdom
Meditations
PerlMonks Discussion
Categorized Q&A
Tutorials
Obfuscated Code
Perl Poetry
Cool Uses for Perl
Perl News
Information?
PerlMonks FAQ
Guide to the Monastery
What's New at PerlMonks
Voting/Experience System
Tutorials
Reviews
Library
Perl FAQs
Other Info Sources
Find Nodes?
Nodes You Wrote
Super Search
List Nodes By Users
Newest Nodes
Recently Active Threads
Selected Best Nodes
Best Nodes
Worst Nodes
Saints in our Book
Leftovers?
The St. Larry Wall Shrine
Offering Plate
Awards
Craft
Snippets Section
Code Catacombs
Quests
Editor Requests
Buy PerlMonks Gear
PerlMonks Merchandise
Planet Perl
Perlsphere
Use Perl
Perl.com
Perl 5 Wiki
Perl Jobs
Perl Mongers
Perl Directory
Perl documentation
CPAN
Random Node
Voting Booth?

What level of existential comfort do you require?

Palace
Executive suite at the best hotel
Regular hotel in a decent part of town
Motel
Boarding house
Sleeping Bag on Couch in Basement
Any port in a storm
Camping under the freeway overpass
Jail
Other

Results (283 votes), past polls