This and other RFCs are available on the web at http://dev.perl.org/rfc/ =head1 TITLE Apache-like Event and Dispatch Handlers =head1 VERSION Maintainer: Nathan Wiger <[EMAIL PROTECTED]> Date: 14 Aug 2000 Last Modified: 25 Sep 2000 Mailing List: [EMAIL PROTECTED] Number: 101 Version: 3 Status: Frozen =head1 ABSTRACT A complete Perl 5 implementation of this can be found as Class::Handler http://www.perl.com/CPAN/authors/id/N/NW/NWIGER/Class-Handler-1.03.tar.gz Currently, there is no way to have multiple methods or modules for dealing with events without significant contortions: $data = $r1->get_data || $r2->stuff || $r3->func; $r1->do_stuff(@vals) or $r1->do_this(@vals); These simple cases can actually be tolerated. However, there are many more complex cases which cannot be handled at all in Perl. These include opening files only in certain directories, having methods decline or partially handle requests, and so on. This RFC proposes the idea of a C<handler>, which is a special type of class that is actually composed of one or more classes. Their operation is very similar to Apache handlers: requests can be handled, declined, or partially handled, without the top-level program having to know about it. =head1 NOTES ON FREEZE The only concern ever raised was on why this should be core-worthy. One word: speed. Currently, it can be implemented via C<AUTOLOAD>, but this is slow. Also, other RFCs such as B<RFC 14> rely on the notion of handlers to gain important functionality (such as the ability to transparently open URLs and different file types). Damian has a separate RFC on pre and post sub handlers. It may be possible to integrate the two into a common handler framework/methodology. Unfortunately, I don't think either of us has the time to do this at this point because of the upcoming RFC deadline. However, this is something that should definitely be looked into in the future. =head1 DESCRIPTION =head2 Overview The concept of a C<handler> is actually not that complex. In the simplest case, it can be thought of as a type of abstraction: sub open_it { my $file = shift; return open $file || HTTP::open $file || FTP::open $file; } Then, in your script, you would simply say: $fileobject = open_it "< $filename"; This gives you several benefits: 1. The internal handling of open_it can be changed without having to update all your programs 2. Each operation can actually partially process the request, if appropriate 2. Your program is easier to read and understand >>From a Perl standpoint, these handlers work just like normal functions and classes: they have methods, properties, inheritance, and so forth. The only difference is that these handlers do not live in an external file, but rather are assembled internally by Perl. =head2 Adding and Using Handlers First, the examples assume that the reader is somewhat familiar with RFC 14. If not, it is recommended that you give it a quick read at http://dev.perl.org/rfc/14.pod There are several competing syntaxes that I have for this proposal. I've provided the one that I think is the best, but this is open to discussion. The proposed syntax is to use a pragmatic style: use handler 'http' => 'MyHTTP'; use handler 'http' => 'LWP::UserAgent'; This would assemble a C<handler> called 'http' which could then be used in functions in your program. This handler would be a pseudo-class that inherits methods from C<MyHTTP> and C<LWP::UserAgent>, in that order. So: $fo = open http "http://www.yahoo.com" or die; would call C<http->open>, consistent with the current Perl implementation. The only difference would be that C<http> now tries to call the C<open()> method from C<MyHTTP> and C<LWP::UserAgent>. As such, the above call would result in the following flow chart: $fo http->open undef ^ | ^ | | | | Does MyHTTP::open exist? | | YES/ \NO | | / \ | | Try it Does LWP::UserAgent::open exist? | | / \ ^ YES/ \NO | | OK/ \UNDEF / / ---------------- ------- ------ Try it | | / \ | | OK/ \UNDEF | ------------------------- --------------------- Some highlights: 1. Each class's open() method is tried in turn. 2. If undef is returned, the next one in sequence is tried. 3. If 'OK' (simply meaning 1 or some other true value, like $fo) is returned, that is propagated out and returned by the top-level handler. 4. All classes are tried until 'OK' is returned or the last one is reached. This allows you to easily chain classes and methods together with a couple key benefits over an inline C<||>: 1. Each handler can partially handle the request, but still return undef, deferring to the next one in line. 2. The handlers can be reordered internally at-will without the main C<open http> code having to be redone. 3. Different class open() methods can use internal rules, such as "only open .com URLs", without you having to put checks for this all over the place in the top-level program. Note that C<open()> is the name of the method called on each class because that is the name of the method called on the C<http> handler. If: http->bob(@stuff); was called, then C<MyHTTP::bob> and C<LWP::UserAgent::bob> would be attempted, in that order. =head2 Removing Handlers In addition to handlers being added, they need to be removed as well. This is where C<no handler> comes in: no handler 'http' => 'MyHTTP'; # remove MyHTTP from list no handler 'http'; # remove http handler The first example removes C<MyHTTP> from the list of classes used by the C<http> handler. The second syntax removes the C<http> handler entirely, meaning that this call: $fo = open http "http://www.yahoo.com"; will result in the familiar error: Can't locate object method "open" via package "http" This should obey blocks as well (like C<strict>), allowing you to say: { # force LWP::UserAgent to be used no handler 'http' => 'MyHTTP'; $fo = open http "http://www.yahoo.com"; } $fo2 = open http "https://www.etrade.com"; =head2 Automatic Handler Registration and Deregistration When a class is imported, it should be able to automatically register as a member of a certain C<handler>. For example, the above code would be better written as: use MyHTTP; # these register as 'http' use LWP::UserAgent; # handlers automatically $fo = open http "http://www.yahoo.com"; This means that there needs to be some mechanism for a module to execute the equivalent of a 'use handler' statement, but have it take affect in the package C<main>. The easiest way it seems is to simply qualify the full package name you want to affect: package MyHTTP; use handler 'main::http' => 'MyHTTP'; This borders on scary action-at-a-distance, though, and should be used with care. =head1 IMPLEMENTATION A complete Perl 5 implementation of this can be found as Class::Handler http://www.perl.com/CPAN/authors/id/N/NW/NWIGER/Class-Handler-1.03.tar.gz The Perl 5 implementation uses two functions, C<handler> and <nohandler>, instead of the pragmatic style proposed in the RFC. This style may be more appropriate, depending how these are used. One problem with pragmas is that they are compile-time-only, meaning that dynamically changing handler lists is tricky to say the least. A module may remain the best implementation for this, the only problems are with speed (since the Perl 5 version requires AUTOLOAD) and also using this mechanism for core methods (like the new C<open> from RFC 14). =head1 REFERENCES RFC 14: Modify open() to support FileObjects and Extensibility RFC 8: The AUTOLOAD subroutine should be able to decline a request http://www.mail-archive.com/perl6-language-io@perl.org/msg00086.html