CustomizingWithOverlays: Difference between revisions
Line 50: | Line 50: | ||
This can be done by creating a new file named lib/RT/Queues_Local.pm, with: | This can be done by creating a new file named lib/RT/Queues_Local.pm, with: | ||
<source lang="perl"> | <source lang="perl"> | ||
<pre> | |||
use strict; | use strict; | ||
no warnings qw(redefine); | no warnings qw(redefine); | ||
Line 68: | Line 69: | ||
1; | 1; | ||
</pre> | |||
</source> | </source> | ||
Revision as of 06:09, 2 February 2021
Introduction
The RT library was designed to allow users and third party vendors to reimplement or modify specific functionality of the RT system easily. This is achieved by using overlays.
Basics
The overlay hierarchy goes as such:
XXX.pm |- XXX_Overlay.pm |- XXX_Vendor.pm |- XXX_Local.pm
The real RT code is in XXX.pm and XXX_Overlay.pm. You can redefine any method or variable in a XXX_Local.pm file and your code will be overlaid on top of the original RT code. See the bottom of lib/RT/Ticket.pm for more information.
But note that you are redefining a method and you have to maintain it across releases.
Wrapping functions
with Hook::LexWrap
Hook::LexWrap is perl module available from the CPAN that allows you to add a hook on any function in a perl module. This is very powerful tool in cooperation with overlays, as you can extend things like in sub-classing without rewriting whole method. You can change arguments of the method, run additional code before or after the method, you can replace the method by condition and many other things you can't do with overlays or the local dir. You can find examples in the RT book, RTIR and several Extensions.
It appears that Hook::LexWrap is no longer the recommended approach, RTIR has stopped doing this, as has several extensions (like MergeUsers), see this thread for a discussion about this: https://forum.bestpractical.com/t/plugins-and--vendor-files/4717/4
with symbol table hackery
With a bit of Perl magic you /can/ wrap subroutines manually:
*_Foo = &Foo; *Foo = sub { $RT::Logger->debug("Reversing output of foo"); reverse &_Foo };
This first expression makes an alias to the original sub. Then second redefines the sub Foo to be our new anonymous subroutine, which does any pre-/post- work we like, and invokes the original to do most of the work. I realize this is a lot of smoke and mirrors but its not all that hard to put into use.
Note: calling &_Load calls _Load with all the same parameters Load was called with, you may override that list the normal way i.e. _Load(param1,param2,etc)
Alternatives to overlays
CustomizingWithLocalDir is not really an alternative, but additional opportunity to make customization clearer and easier for upgrade or move to another server. For example, you can put XXX_Local.pm and XXX_Vendor.pm overlay files in the local tree to keep them segregated from the distribution files.
CustomizingWithPatches may be preferred in some cases.
Example
Imagine a simple example, you want to redefine the order of appearance of Queues everywhere, let's say by name in reverse alphabetical order. This is equivalent to redefine the Queues' _Init
method. We could do something like the symbol example above, but it'd be more efficient if the database simply sorted things correctly in the first place.
This can be done by creating a new file named lib/RT/Queues_Local.pm, with:
<pre>
use strict;
no warnings qw(redefine);
package RT::Queues;
sub _Init {
my $self = shift;
$self->{'table'} = "Queues";
$self->{'primary_key'} = "id";
# By default, order by name
$self->OrderBy( ALIAS => 'main',
FIELD => 'Name',
ORDER => 'DESC');
return ($self->SUPER::_Init(@_));
}
1;
</pre>
--Paul Matos
Caveats
When overlaying methods in _Local.pm files it is important to realize that you are not subclassing the base class - you are just replacing the base class's method with your own. For this reason do not do this:
Date_Local.pm:
sub Set {
$self->SUPER::Set;
}
Or you will find yourself in an infinite loop. And yes, I found out this the hard way. -- Stephen Turner
When putting overlay files in the "local" tree, you may find that simply copying the original .pm from the RT tree breaks due to namespace problems, i.e. "function not found" errors for calls in the RT::* namespace. This should be resolvable by adding explicit 'use' statements for the module(s) in question to your local overlay. -- Bill Cole