Similar to 'use' and 'no' pragma, I'm suggesting to introduce the new functions override_mp2_api and restore_mp2_api for those APIs in Apache::compat that collide with 2.0 API. The only difference is that one needs an explicit call to restore_mp2_api, which I suppose could be done by returning an object, which if made a lexically scoped will call restore_mp2_api on its own (i.e. DESTROY), but since it's going to be used very infrequently and eventually won't be needed at all, I don't see a reason to bother. See the compat/request test change for an example in the patch below.
I decided not to use the import() method, but have an explicit function call, since import() may make some people think that it imports the overriden methods, which it doesn't.
I think there are only two functions at the moment that collide with mp2 API:
Apache::RequestRec::notes and Apache::RequestRec::finfo. There is also APR::URI::unparse, which introduces a special case but I think it's harmless.
in June I posted an implementation for 2 more colliding functions: Apache::Connection::local_addr Apache::Connection::remote_addr http://marc.theaimsgroup.com/?l=apache-modperl-dev&m=105452446932154&w=2 which I will now be able to commit using this new functionality.
The cool thing is that we can introduce a sub-class of Registry which will do the wrapping into a handler like so:
'sub handler {' . 'Apache::compat::override_mp2_api('Apache::RequestRec::notes');' . $code . 'Apache::compat::restore_mp2_api('Apache::RequestRec::notes');' . '}';
or something like that (overriding other subs as well).
Here is the patch:
Index: lib/Apache/compat.pm =================================================================== RCS file: /home/cvs/modperl-2.0/lib/Apache/compat.pm,v retrieving revision 1.90 diff -u -r1.90 compat.pm --- lib/Apache/compat.pm 19 Nov 2003 19:30:11 -0000 1.90 +++ lib/Apache/compat.pm 15 Dec 2003 02:02:10 -0000 @@ -50,6 +50,86 @@ $INC{'Apache/Table.pm'} = __FILE__; }
+# api => "overriding code" +# the overriding code, needs to "return" the original CODE reference +# when eval'ed , so that it can be restored later +my %overridable_mp2_api = ( + 'Apache::RequestRec::notes' => <<'EOI', +{ + require Apache::RequestRec; + my $notes_sub = *Apache::RequestRec::notes{CODE}; + *Apache::RequestRec::notes = sub { + my $r = shift; + return wantarray() + ? ($r->table_get_set(scalar($r->$notes_sub), @_)) + : scalar($r->table_get_set(scalar($r->$notes_sub), @_)); + }; + $notes_sub; +} +EOI + + 'Apache::RequestRec::finfo' => <<'EOI', +{ + require APR::Finfo; + my $finfo_sub = *APR::Finfo::finfo{CODE}; + sub Apache::RequestRec::finfo { + my $r = shift; + stat $r->filename; + \*_; + } + $finfo_sub; +} +EOI +); + +my %overridden_mp2_api = (); + +# this function enables back-compatible APIs which can't coexist with +# mod_perl 2.0 APIs with the same name and therefore it should be +# avoided if possible. +# +# it expects a list of fully qualified functions, like +# "Apache::RequestRec::finfo" +sub override_mp2_api { + my (@subs) = @_; + + for my $sub (@subs) { + unless (exists $overridable_mp2_api{$sub}) { + die __PACKAGE__ . ": $sub is not overridable"; + } + if(exists $overridden_mp2_api{$sub}) { + warn __PACKAGE__ . ": $sub has been already overridden"; + next; + } + $overridden_mp2_api{$sub} = eval $overridable_mp2_api{$sub}; + unless (exists $overridden_mp2_api{$sub} && + ref($overridden_mp2_api{$sub}) eq 'CODE') { + die "overriding $sub didn't return a CODE ref"; + } + } +} + +# restore_mp2_api does the opposite of override_mp2_api(), it removes +# the overriden API and restores the original mod_perl 2.0 API +sub restore_mp2_api { + my (@subs) = @_; + + for my $sub (@subs) { + unless (exists $overridable_mp2_api{$sub}) { + die __PACKAGE__ . ": $sub is not overridable"; + } + unless (exists $overridden_mp2_api{$sub}) { + warn __PACKAGE__ . ": can't restore $sub, " . + "as it has not been overridden"; + next; + } + my $original_sub = delete $overridden_mp2_api{$sub}; + no warnings 'redefine'; + no strict 'refs'; + *$sub = $original_sub; + } +} + sub request { my $what = shift;
@@ -249,15 +329,6 @@ : scalar($r->table_get_set(scalar($r->err_headers_out), @_)); }
-{ - my $notes_sub = *Apache::RequestRec::notes{CODE}; - *Apache::RequestRec::notes = sub { - my $r = shift; - return wantarray() - ? ($r->table_get_set(scalar($r->$notes_sub), @_)) - : scalar($r->table_get_set(scalar($r->$notes_sub), @_)); - } -}
sub register_cleanup { shift->pool->cleanup_register(@_); @@ -345,12 +416,6 @@
sub chdir_file { #XXX resolve '.' in @INC to basename $r->filename -} - -sub finfo { - my $r = shift; - stat $r->filename; - \*_; }
*log_reason = \&log_error; Index: t/response/TestCompat/request.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/response/TestCompat/request.pm,v retrieving revision 1.3 diff -u -r1.3 request.pm --- t/response/TestCompat/request.pm 11 Apr 2003 07:34:03 -0000 1.3 +++ t/response/TestCompat/request.pm 15 Dec 2003 02:02:10 -0000 @@ -75,6 +75,8 @@
# $r->notes { + Apache::compat::override_mp2_api('Apache::RequestRec::notes'); + my $key = 'notes-test'; # get/set scalar context { @@ -98,6 +100,10 @@ $r->notes->add($key => $_) for @exp; ok t_cmp([EMAIL PROTECTED], [ $r->notes($key) ], "\$r->notes in list context"); } + + # restore the real 2.0 notes() method, now that we are done + # with the compat one + Apache::compat::restore_mp2_api('Apache::RequestRec::notes'); }
# get_remote_host()
__________________________________________________________________ Stas Bekman JAm_pH ------> Just Another mod_perl Hacker http://stason.org/ mod_perl Guide ---> http://perl.apache.org mailto:[EMAIL PROTECTED] http://use.perl.org http://apacheweek.com http://modperlbook.org http://apache.org http://ticketmaster.com
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]