10.2. Modifying RT s Codebase

 < Day Day Up > 

10.2. Modifying RT's Codebase

Most of RT's core classes are designed to allow you to customize their behavior through a mechanism called overlays. You also can use subclasses in some cases, or modules such as Damian Conway's Hook::LexWrap.

10.2.1. Overlays

Overlays are the easiest way to make major changes to RT's core classes. If you look at the RT::User class itself, you will see that it contains surprisingly little code.

Near the end, you'll see these lines:

     eval "require RT::User_Overlay";     if ($@ && $@ !~ qr{^Can't locate RT/User_Overlay.pm}) {         die $@;     }           eval "require RT::User_Vendor";     if ($@ && $@ !~ qr{^Can't locate RT/User_Vendor.pm}) {         die $@;     }           eval "require RT::User_Local";     if ($@ && $@ !~ qr{^Can't locate RT/User_Local.pm}) {         die $@;     } 

This is the core of RT's overlay mechanism. The RT::User_Overlay file ships with RT itself and contains most of the real core logic for the user class. It is important to note that this module's package declaration is RT::User.

The RT::User_Vendor module would be created by an operating system distribution like Debian or FreeBSD, and the RT::User_Local module is where per-installation custom code should be placed. Neither of these modules exist in a default RT installation.

This mechanism is somewhat unusual, and you may wonder why RT doesn't just use subclasses. The first reason is that many of RT's core database-based classes are auto-generated from the database schema. Additionally, using subclasses would require all classes that use RT::User (for example) to use the appropriate subclass instead, which requires a potentially complex configuration mechanism and could be a bit of a headache.

The downside is that you cannot use SUPER to call the code you've overridden. If you need to do that, you will need to use Hook::LexWrap, or assign another name to the original subroutine:

     BEGIN { *SUPER_SomeMethod = \&SomeMethod };           sub SomeMethod {        my $self = shift;        # ...some preprocessing...        $self->SUPER_SomeMethod(@_);        # ...some postprocessing...      } 

10.2.1.1. Quieting warnings

Let's assume that you've created a module RT::User_Local, and you've overridden the CurrentUserHasRight( ) method. When you go to restart RT, you'll get a warning like "Subroutine CurrentUserHasRight redefined at ... RT/User_Local.pm line 42."

This is not a helpful warning, since you already know you redefined that subroutine. You can silence it by adding this line to the top of your module:

     no warnings 'redefine'; 

10.2.1.2. A simple overlay example

In a default RT setup, all of the user data is stored in RT's database. But what if you want to get some of the user data from another source, like an LDAP directory or a different database? The answer is to create a User_Local.pm overlay module.

Here is a simple example that overrides the CanonicalizeUserInfo( ) method to lookup the user's real name using an in-house module:

     package RT::User;           use strict;     no warnings 'redefine';           use Yoyodyne::User;           sub CanonicalizeUserInfo {         my $self = shift;         my $args = shift;               my $user = Yoyodyne::User->new( $args->{EmailAddress} );               $args->{RealName} = $user->name             if $user;               return 1;     }           1; 

This method, CanonicalizeUserInfo( ), does nothing in the core RT classes and is explicitly provided to make it easy to customize user creation.

10.2.2. Hook::LexWrapHook::LexWrap

You can use the Hook::LexWrap module to achieve something similar to overriding a method in a subclass. Hook::LexWrap lets you insert pre- and post-call wrappers for a method or function call. Code run before the method can change the arguments being passed in, and code run after can change the return value.

Hook::LexWrap calls the wrapper code with an extra argument at the end of @_. If you set this value in your pre-call code, then the main method and post-call code will not be called. You can use this to short-circuit a method call and force a different return value.

Here's an example that takes advantage of that to prevent the resolution of a ticket which has never had a reply:

     package RT::Ticket;           use strict;     use Hook::LexWrap;           wrap 'SetStatus' => pre => \&check_for_replies;           sub check_for_replies {         my $self = shift;               my $transactions = RT::Transactions->new( $self->CurrentUser );         $transactions->LimitToTicket( $self->Id );         $transactions->Limit( FIELD => 'Type',                               VALUE => 'Correspond',                             );               unless ( $transactions->Count ) {             $_[-1] = [0, $self->loc('Ticket cannot be resolved without a reply')];         }     }           1; 

This code would go in local/lib/RT/Ticket_Local.pm.

The wrapper code short-circuits the call to SetStatus( ) if the ticket has no replies. This is done simply by setting the value of the last argument to the wrapper. If this argument is an array reference, then Hook::LexWrap will turn it into an array if the original method was called in array context.

If the ticket has replies, then the wrapper does not set this value, so the original method is called in the normal fashion.

     < Day Day Up > 


    RT Essentials
    RT Essentials
    ISBN: 0596006683
    EAN: 2147483647
    Year: 2005
    Pages: 166

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net