1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
|
=head1 NAME
RT::StyleGuide - RT Style Guide
=head1 CAVEATS
This file is somewhat out of date; L<hacking> takes precedence over it.
=head1 INTRODUCTION
All code and documentation that is submitted to be included in the RT
distribution should follow the style in this document. This is not to
try to stifle your creativity, but to make life easier for everybody who
has to work with your code, and to aid those who are not quite sure how
to do something.
These conventions below apply to perl modules, web programs, and
command-line programs, specifically, but also might apply to some
degree to any Perl code written for use in RT.
Note that these are all guidelines, not unbreakable rules. If you have
a really good need to break one of the rules herein, however, then it is
best to ask on the B<rt-devel> mailing list first.
Note that with much of this document, it is not so much the Right Way as
it is Our Way. We need to have conventions in order to make life easier
for everyone. So don't gripe, and just follow it, because you didn't
get a good grade in "Plays Well With Others" in kindergarten and you
want to make up for it now.
If you have any questions, please ask us on the B<rt-devel> mailing list:
http://www.bestpractical.com/rt/lists.html
We don't always follow this guide. We are making changes throughout
our code to be in line with it. But just because we didn't do
it yet, that is no excuse. Do it anyway. :-)
This document is subject to change at the whims of the core RT team.
We hope to add any significant changes at the bottom of the document.
=head1 CODING PRINCIPLES
=head2 Perl Version
We code everything to Perl 5.10.1 or higher.
=head2 Documentation
All modules will be documented using the POD examples in the module
boilerplate. The function, purpose, use of the module will be
explained, and each public API will be documented with name,
description, inputs, outputs, side effects, etc.
If an array or hash reference is returned, document the size of the
array (including what each element is, as appropriate) and name each key
in the hash. For complex data structures, map out the structure as
appropriate (e.g., name each field returned for each column from a DB
call; yes, this means you shouldn't use "SELECT *", which you shouldn't
use anyway).
Also document what kind of data returned values are. Is it an integer,
a block of HTML, a boolean?
All command-line program options will be documented using the
boilerplate code for command-line programs, which doesn't yet exist.
Each available function, switch, etc. should be documented, along
with a statement of function, purpose, use of the program. Do not
use the same options as another program, for a different purpose.
All web templates should be documented with a statement of function,
purpose, and use in a mason comment block.
Any external documents, and documentation for command-line programs and
modules, should be written in POD, where appropriate. From there, they
can be translated to many formats with the various pod2* translators.
Read the perlpod manpage before writing any POD, because although POD is
not difficult, it is not what most people are used to. It is not a
regular markup language; it is just a way to make easy documentation
for translating to other formats. Read, and understand, the perlpod
manpage, and ask us or someone else who knows if you have any questions.
=head2 Version
Our distribution versions use tuples, where the first number is the
major revision, the second number is the version, and third
number is the subversion. Odd-numbered versions are development
versions. Examples:
1.0.0 First release of RT 1
1.0.1 Second release of RT 1.0
1.0.10 etc.
1.1.0 First development release of RT 1.2 (or 2.0)
2.0.0 First release of RT 2
Versions may end in "rc" and a number if they are release candidates:
2.0.0rc1 First release candiate for real 2.0.0
=head2 Comments
All code should be self-documenting as much as possible. Only include
necessary comments. Use names like "$ticket_count", so you don't need to
do something like:
# ticket count
my $tc = 0;
Include any comments that are, or might be, necessary in order for
someone else to understand the code. Sometimes a simple one-line
comment is good to explain what the purpose of the following code is
for. Sometimes each line needs to be commented because of a complex
algorithm. Read Kernighan & Pike's I<Practice of Programming> about
commenting. Good stuff, Maynard.
=head2 Warnings and Strict
All code must compile and run cleanly with "use strict" enabled and the
perl "-w" (warnings) option on. If you must do something that -w or
strict complains about, there are workarounds, but the chances that you
really need to do it that way are remote.
=head2 Lexical Variables
Use only lexical variables, except for special global variables
($VERSION, %ENV, @ISA, $!, etc.) or very special circumstances (see
%HTML::Mason::Commands::session ). Global variables
for regular use are never appropriate. When necessary, "declare"
globals with "use vars" or "our()".
A lexical variable is created with my(). A global variable is
pre-existing (if it is a special variable), or it pops into existence
when it is used. local() is used to tell perl to assign a temporary
value to a variable. This should only be used with special variables,
like $/, or in special circumstances. If you must assign to any global
variable, consider whether or not you should use local().
local() may also be used on elements of arrays and hashes, though there
is seldom a need to do it, and you shouldn't.
=head2 Pass by Reference
Arrays and hashes should be passed to and from functions by reference
only. Note that a list and an array are NOT the same thing. This
is perfectly fine:
return($user, $form, $constants);
An exception might be a temporary array of discrete arguments:
my @return = ($user, $form);
push @return, $constants if $flag;
return @return;
Although, usually, this is better (faster, easier to read, etc.):
if ($flag) {
return($user, $form, $constants);
} else {
return($user, $form);
}
We need to talk about Class::ReturnValue here.
=head2 Method parameters
If a method takes exactly one mandatory argument, the argument should be
passed in a straightforward manner:
my $self = shift;
my $id = shift;
In all other cases, the method needs to take named parameters, usually
using a C<%args> hash to store them:
my $self = shift;
my %args = (
Name => undef,
Description => undef,
@_
);
You may specify defaults to those named parameters instead of using
C<undef> above, as long as it is documented as such.
It is worth noting that the existing RT codebase had not followed this
style perfectly; we are trying to fix it without breaking existing APIs.
=head2 Tests
Modules should provide test code, with documentation on how to use
it. Test::More makes it easy to create tests. Any code you write
should have a testsuite. Any code you alter should have a test
suite. If a patch comes in without tests, there is something wrong.
When altering code, you must run the test harness before submitting a
patch or committing code to the repository.
"make test" will run the test suite.
=head2 STDIN/STDOUT
Always report errors using $RT::Logger. It's a Log::Dispatch object.
Unlike message meant for the user, log messages are not to be
internationalized.
There are several different levels ($RT::Logger methods) of logging:
=over 4
=item debug
Used for messages only needed during system debugging.
=item info
Should be used to describe "system-critical" events which aren't errors.
Examples: creating users, deleting users, creating tickets, creating queues,
sending email (message id, time, recipients), recieving mail, changing
passwords, changing access control, superuser logins)
=item error
Used for RT-generated failures during execution.
=item crit
Should be used for messages when an action can not be completed due to some
error condition beyond our control.
=back
In the web UI and modules, never print directly to STDERR. Do not print
directly to STDOUT, unless you need to print directly to the user's console.
In command-line programs, feel free to print to STDERR and STDOUT as
needed for direct console communication. But for actual error reporting,
use the logging API.
=head2 System Calls
Always check return values from system calls, including open(),
close(), mkdir(), or anything else that talks directly to the system.
Perl built-in system calls return the error in $!; some functions in
modules might return an error in $@ or some other way, so read the module's
documentation if you don't know. Always do something, even if it is
just calling $RT::Logger->warning(), when the return value is not what you'd expect.
=head1 STYLE
Much of the style section is taken from the perlsyle manpage. We make
some changes to it here, but it wouldn't be a bad idea to read that
document, too.
=head2 Terminology
=over 4
=item function vs. sub(routine) vs. method
Just because it is the Perl Way (not necessarily right for all
languages, but the documented terminology in the perl documentation),
"method" should be used only to refer to a subroutine that are object
methods or class methods; that is, these are functions that are used
with OOP that always take either an object or a class as the first
argument. Regular subroutines, ones that are not object or class
methods, are functions. Class methods that create and return an object
are optionally called constructors.
=item Users
"users" are normally users of RT, the ones hitting the site; if using
it in any other context, specify.
"system users" are user
names on the operating system. "database users" are the user names in
the database server. None of these needs to be capitalized.
=back
=head2 Names
Don't use single-character variables, except as iterator variables.
Don't use two-character variables just to spite us over the above rule.
Constants are in all caps; these are variables whose value will I<never>
change during the course of the program.
$Minimum = 10; # wrong
$MAXIMUM = 50; # right
Other variables are lowercase, with underscores separating the words.
They words used should, in general, form a noun (usually singular),
unless the variable is a flag used to denote some action that should be
taken, in which case they should be verbs (or gerunds, as appropriate)
describing that action.
$thisVar = 'foo'; # wrong
$this_var = 'foo'; # right
$work_hard = 1; # right, verb, boolean flag
$running_fast = 0; # right, gerund, boolean flag
Arrays and hashes should be plural nouns, whether as regular arrays and
hashes or array and hash references. Do not name references with "ref"
or the data type in the name.
@stories = (1, 2, 3); # right
$comment_ref = [4, 5, 6]; # wrong
$comments = [4, 5, 6]; # right
$comment = $comments->[0]; # right
Make the name descriptive. Don't use variables like "$sc" when you
could call it "$story_count". See L<"Comments">.
There are several variables in RT that are used throughout the code,
that you should use in your code. Do not use these variable names for
anything other than how they are normally used, and do not use any
other variable names in their place. Some of these are:
$self # first named argument in object method
Subroutines (except for special cases, like AUTOLOAD and simple accessors)
begin with a verb, with words following to complete the action. Accessors
don't start with "Get" if they're just the name of the attribute.
Accessors which return an object should end with the suffix Obj.
This section needs clarification for RT.
Words begin with a capital letter. They
should as clearly as possible describe the activity to be peformed, and
the data to be returned.
Load(); # good
LoadByName(); # good
LoadById(); # good
Subroutines beginning with C<_> are special: they are not to be used
outside the current object. There is not to be enforced by the code
itself, but by someone very big and very scary.
For large for() loops, do not use $_, but name the variable.
Do not use $_ (or assume it) except for when it is absolutely
clear what is going on, or when it is required (such as with
map() and grep()).
for (@list) {
print; # OK; everyone knows this one
print uc; # wrong; few people know this
print uc $_; # better
}
Note that the special variable C<_> I<should> be used when possible.
It is a placeholder that can be passed to stat() and the file test
operators, that saves perl a trip to re-stat the file. In the
example below, using C<$file> over for each file test, instead of
C<_> for subsequent uses, is a performance hit. You should be
careful that the last-tested file is what you think it is, though.
if (-d $file) { # $file is a directory
# ...
} elsif (-l _) { # $file is a symlink
# ...
}
Package names begin with a capital letter in each word, followed by
lower case letters (for the most part). Multiple words should be StudlyCapped.
RT::User # good
RT::Database::MySQL # proper name
RT::Display::Provider # good
RT::CustomField # not so good, but OK
Plugin modules should begin with "RT::Extension::", followed by the name
of the plugin.
=head1 Code formatting
When in doubt, use perltidy; RT includes a F<.perltidyrc>.
=head2 Indents and Blank Space
All indents should be four spaces; hard tabs are forbidden.
No space before a semicolon that closes a statement.
foo(@bar) ; # wrong
foo(@bar); # right
Line up corresponding items vertically.
my $foo = 1;
my $bar = 2;
my $xyzzy = 3;
open(FILE, $fh) or die $!;
open(FILE2, $fh2) or die $!;
$rot13 =~ tr[abcedfghijklmnopqrstuvwxyz]
[nopqrstuvwxyzabcdefghijklm];
# note we use a-mn-z instead of a-z,
# for readability
$rot13 =~ tr[a-mn-z]
[n-za-m];
Put blank lines between groups of code that do different things. Put
blank lines after your variable declarations. Put a blank line before a
final return() statement. Put a blank line following a block (and
before, with the exception of comment lines).
An example:
# this is my function!
sub foo {
my $val = shift;
my $obj = new Constructor;
my($var1, $var2);
$obj->SetFoo($val);
$var1 = $obj->Foo();
return($val);
}
print 1;
=head2 Parentheses
For control structures, there is a space between the keyword and opening
parenthesis. For functions, there is not.
for(@list) # wrong
for (@list) # right
my ($ref) # wrong
my($ref) # right
Be careful about list vs. scalar context with parentheses!
my @array = ('a', 'b', 'c');
my($first_element) = @array; # a
my($first_element) = ('a', 'b', 'c'); # a
my $element_count = @array; # 3
my $last_element = ('a', 'b', 'c'); # c
Always include parentheses after functions, even if there are no arguments.
There are some exceptions, such as list operators (like print) and unary
operators (like undef, delete, uc).
There is no space inside the parentheses, unless it is needed for
readability.
for ( map { [ $_, 1 ] } @list ) # OK
for ( @list ) # not really OK, not horrible
On multi-line expressions, match up the closing parenthesis with either
the opening statement, or the opening parenthesis, whichever works best.
Examples:
@list = qw(
bar
baz
); # right
if ($foo && $bar && $baz
&& $buz && $xyzzy) {
print $foo;
}
Whether or not there is space following a closing parenthesis is
dependent on what it is that follows.
print foo(@bar), baz(@buz) if $xyzzy;
Note also that parentheses around single-statement control expressions,
as in C<if $xyzzy>, are optional (and discouraged) C<if> it is I<absolutely>
clear -- to a programmer -- what is going on. There is absolutely no
need for parentheses around C<$xyzzy> above, so leaving them out enhances
readability. Use your best discretion. Better to include them, if
there is any question.
The same essentially goes for perl's built-in functions, when there is
nothing confusing about what is going on (for example, there is only one
function call in the statement, or the function call is separated by a
flow control operator). User-supplied functions must always include
parentheses.
print 1, 2, 3; # good
delete $hash{key} if isAnon($uid); # good
However, if there is any possible confusion at all, then include the
parentheses. Remember the words of Larry Wall in the perlstyle manpage:
When in doubt, parenthesize. At the very least it will
let some poor schmuck bounce on the % key in vi.
Even if you aren't in doubt, consider the mental welfare
of the person who has to maintain the code after you, and
who will probably put parens in the wrong place.
So leave them out when it is absoutely clear to a programmer, but if
there is any question, leave them in.
=head2 Braces
(This is about control braces, not hash/data structure braces.)
There is always a space befor the opening brace.
while (<$fh>){ # wrong
while (<$fh>) { # right
A one-line block may be put on one line, and the semicolon may be
omitted.
for (@list) { print }
Otherwise, finish each statement with a semicolon, put the keyword and
opening curly on the first line, and the ending curly lined up with the
keyword at the end.
for (@list) {
print;
smell();
}
Generally, we prefer "cuddled elses":
if ($foo) {
print;
} else {
die;
}
=head2 Operators
Put space around most operators. The primary exception is the for
aesthetics; e.g., sometimes the space around "**" is ommitted,
and there is never a space before a ",", but always after.
print $x , $y; # wrong
print $x, $y; # right
$x = 2 >> 1; # good
$y = 2**2; # ok
Note that "&&" and "||" have a higher precedence than "and" and "or".
Other than that, they are exactly the same. It is best to use the lower
precedence version for control, and the higher for testing/returning
values. Examples:
$bool = $flag1 or $flag2; # WRONG (doesn't work)
$value = $foo || $bar; # right
open(FILE, $file) or die $!;
$true = foo($bar) && baz($buz);
foo($bar) and baz($buz);
Note that "and" is seldom ever used, because the statement above is
better written using "if":
baz($buz) if foo($bar);
Most of the time, the confusion between and/&&, or/|| can be alleviated
by using parentheses. If you want to leave off the parentheses then you
I<must> use the proper operator. But if you use parentheses -- and
normally, you should, if there is any question at all -- then it doesn't
matter which you use. Use whichever is most readable and aesthetically
pleasing to you at the time, and be consistent within your block of code.
Break long lines AFTER operators, except for ".", "and", "or", "&&", "||".
Try to keep the two parts to a binary operator (an operator that
has two operands) together when possible.
print "foo" . "bar" . "baz" .
"buz"; # wrong
print "foo" . "bar" . "baz"
. "buz"; # right
print $foo unless $x == 3 && $y ==
4 && $z == 5; # wrong
print $foo unless $x == 3 && $y == 4
&& $z == 5; # right
=head2 Other
Put space around a complex subscript inside the brackets or braces.
$foo{$bar{baz}{buz}}; # OK
$foo{ $bar{baz}{buz} }; # better
In general, use single-quotes around literals, and double-quotes
when the text needs to be interpolated.
It is OK to omit quotes around names in braces and when using
the => operator, but be careful not to use a name that doubles as
a function; in that case, quote.
$what{'time'}{it}{is} = time();
When making compound statements, put the primary action first.
open(FILE, $fh) or die $!; # right
die $! unless open(FILE, $fh); # wrong
print "Starting\n" if $verbose; # right
$verbose && print "Starting\n"; # wrong
Use here-docs instead of repeated print statements.
print <<EOT;
This is a whole bunch of text.
I like it. I don't need to worry about messing
with lots of print statements and lining them up.
EOT
Just remember that unless you put single quotes around your here-doc
token (<<'EOT'), the text will be interpolated, so escape any "$" or "@"
as needed.
=head1 INTERNATIONALIZATION
=head2 String extraction styleguide
=over 4
=item Web templates
Templates should use the /l filtering component to call the localisation
framework
The string Foo!
Should become <&|/l&>Foo!</&>
All newlines should be removed from localized strings, to make it easy to
grep the codebase for strings to be localized
The string Foo
Bar
Baz
Should become <&|/l&>Foo Bar Baz</&>
Variable subsititutions should be moved to Locale::MakeText format
The string Hello, <%$name %>
should become <&|/l, $name &>Hello, [_1]</&>
Multiple variables work just like single variables
The string You found <%$num%> tickets in queue <%$queue%>
should become <&|/l, $num, $queue &>You found [_1] tickets in queue [_2]</&>
When subcomponents are called in the middle of a phrase, they need to be escaped
too:
The string <input type="submit" value="New ticket in"> <& /Elements/SelectNewTicketQueue&>
should become <&|/l, $m->scomp('/Elements/SelectNewTicketQueue')&><input type="submit" value="New ticket in"> [_1]</&>
The string <& /Widgets/TitleBoxStart, width=> "40%", titleright => "RT $RT::VERSION for RT->Config->Get('rtname')", title => 'Login' &>
should become <& /Widgets/TitleBoxStart,
width=> "40%",
titleright => loc("RT [_1] for [_2]",$RT::VERSION, RT->Config->Get('rtname')),
title => loc('Login'),
&>
=item Library code
Within RT's core code, every module has a localization handle available through the 'loc' method:
The code return ( $id, "Queue created" );
should become return ( $id, $self->loc("Queue created") );
When returning or localizing a single string, the "extra" set of parenthesis () should be omitted.
The code return ("Subject changed to ". $self->Data );
should become return $self->loc( "Subject changed to [_1]", $self->Data );
It is important not to localize the names of rights or statuses within RT's core, as there is logic that depends on them as string identifiers. The proper place to localize these values is when they're presented for display in the web or commandline interfaces.
=back
=head1 CODING PRCEDURE
This is for new programs, modules, specific APIs, or anything else.
=over 4
=item Present idea to rt-devel
We may know of a better way to approach the problem, or know of an
existing way to deal with it, or know someone else is working on it.
This is mostly informal, but a fairly complete explanation for the need
and use of the code should be provided.
=item Present complete specs to rt-devel
The complete proposed API should be submitted for
approval and discussion. For web and command-line programs, present the
functionality and interface (op codes, command-lin switches, etc.).
The best way to do this is to take the documentation portion of the
boilerplate and fill it in. You can make changes later if necessary,
but fill it in as much as you can.
=item Prepare for code review
When you are done, the code will undergo a code review by a member of
the core team, or someone picked by the core team. This is not to
belittle you (that's just a nice side effect), it is to make sure that
you understand your code, that we understand your code, that it won't
break other code, that it follows the documentation and existing
proposal. It is to check for possible optimizations or better ways of
doing it.
Note that all code is expected to follow the coding principles and style
guide contained in this document.
=item Finish it up
After the code is done (possibly going through multiple code reviews),
if you do not have repository access, submit it to rt-bugs@fsck.com as a
unified diff. From that point on, it'll be handled by someone with
repository access.
=back
=head1 BUG REPORTS, PATCHES
Use rt-bugs@bestpractical.com for I<any> bug that is not being fixed
immediately. If it is not in RT, there is a good chance it will not be
dealt with.
Send patches to rt-bugs@bestpractical.com, too. Use C<diff -u> for
patches.
=head1 SCHEMA DESIGN
RT uses a convention to denote the foreign key status in its tables.
The rule of thumb is:
=over 4
=item When it references to another table, always use the table name
For example, the C<Template> field in the C<Scrips> table refers to
the C<Id> of the same-named C<Template> table.
=item Otherwise, always use the C<Id> suffix
For example, the C<ObjectId> field in the C<ACL> table can refer
to any object, so it has the C<Id> suffix.
=back
There are some legacy fields that did not follow this rule, namely
C<ACL.PrincipalId>, C<GroupMembers.GroupId> and C<Attachments.TransactionId>,
but new tables are expected to be consistent.
=head1 EXTENDING RT CLASSES
=head2 The Overlay mechanism
RT's classes allow "overlay" methods to be placed into files named Filename_Vendor.pm and Filename_Local.pm
_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.
These overlay files can contain new subs or subs to replace existing subs in this module.
Each of these files should begin with the line
no warnings qw(redefine);
so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
=head1 TO DO
Talk about DBIx::SearchBuilder
Talk about mason
component style
cascading style sheets
Talk about adding a new translation
Talk more about logging
|