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
|
=pod
=head1 NAME
Perl::Critic::CORE_DEVELOPER - Hints for working on the Perl::Critic core.
=head1 DESCRIPTION
This document is a grab-bag of notes for those who are working on the
underpinnings of Perl::Critic. They are intended to be informative,
but unfortunately can not really be considered authoritative. It is in
the nature of the task being described that the user of this document
will end up working out the details for him- or herself based on the
actual work being performed. B<Caveat lector.>
=head1 BECOMING A CORE DEVELOPER
Here are my thoughts on how to get started. Note that the steps are
not numbered because I'm not sure there is a clear order to them. The
items with two stars in front of them are from the mailing list; the
ones with one star are my opinion. Although sometimes I have felt it
helpful to comment on the two-star items, just to make things
thoroughly unclear.
* If you're unsure of yourself, install Perl::Critic, then download
the source and rummage around in it.
** Subscribe to the developers' mailing list. There are instructions
in L<Perl::Critic/EXTENDING THE CRITIC>. The F<commits> mailing list
is another good one.
** If you are working on a GitHub issue, you should update the ticket to
say that you are, to keep other people from duplicating your effort.
* I personally would update GitHub at the point I was reasonably confident
I could hack it, just to prevent myself from having to update GitHub
again in a week or so saying "oops, bit off more than I could chew."
But that's me talking.
* Development requires using Module::Build rather than
ExtUtils::MakeMaker. In other words,
$ perl Build.PL
$ ./Build
$ ./Build test
* You need to run the suite of author tests by running
$ ./Build authortest
(but not 'make authortest', which is one of the reasons you should
start with F<Build.PL> rather than F<Makefile.PL>) These should run
cleanly before you declare your work done. My advice, though, is not
to worry about them until your code is functionally correct.
=head2 Modules required for authortest
The authortest requires a B<bunch> of modules above and beyond those
required to run C<Perl::Critic>. The list probably depends on which
C<Perl::Critic> you are testing, so the following should not be
considered definitive. You need the following in addition to B<all>
optional modules for Perl::Critic itself.
Devel::EnforceEncapsulation
Perl::Critic::More
Test::Kwalitee
Test::Memory::Cycle
Test::Perl::Critic
Test::Pod
Test::Pod::Coverage
Test::Without::Module
You can find out what the optional modules are by looking at
C<recommended_module_versions()> in
F<inc/Perl/Critic/BuildUtilities.pm>.
In the absence of C<Test::Memory::Cycle>, the relevant tests are
simply skipped. In the absence of the other modules, the tests die
horribly. Of course, either way they do not get run, so the
difference is mainly one of aesthetics.
Under Perl 5.12 and above, L<Devel::Cycle|Devel::Cycle> 1.11 needs to
be patched to handle a C<Regexp> as a first-class Perl object. See
L<https://rt.cpan.org/Public/Bug/Display.html?id=56681> for the
details.
=head1 ADDING A GLOBAL CONFIGURATION ITEM
Perlcritic handles global configuration items and command line options
in very similar ways. These notes will cover adding both a global
configuration item and a corresponding, same-named command option.
These notes can not, of course, cover implementing the functionality
of the new item, just the mechanics of getting the item into
Perl::Critic.
=head2 Naming Conventions
All names are lower-case, except for the names of constants (if any),
which are upper-case. When a name contains multiple words, dashes
will be used to separate the words in the configuration item name and
the command line option, and underscores will be used in the accessor
and attribute value names, and constant names if any.
For example, if "new item" is being added, the configuration item is
"new-item", the command option is "--new-item", the accessors are
C<new_item()>, and the value of the attribute will be stored in
C<< $self->{_new_item} >>. If there are constants involved, their
names will start with C<NEW_ITEM_>. These names will be used in the
following discussion.
=head2 Implementation
There are several files that must be modified to get your new
configuration item and/or command line option.
=head3 F<lib/Perl/Critic/Utils/Constants.pm>
If there are manifest constants connected with your implementation
they go here. You may well at least have a
$NEW_ITEM_DEFAULT
to define. All the constants for your new item must be exported, and
should be exported not only individually but all together with export
tag
new_item
=head3 F<lib/Perl/Critic/Command.pm>
If your new item is a command option, its L<Getopt::Long|Getopt::Long>
specification must be defined in C<_get_option_specification()>. If
your new configuration item does not have a corresponding command
option, you do not need to make any changes to this file.
=head3 F<lib/Perl/Critic/OptionsProcessor.pm>
If your new item is a global configuration item, you need to add the
code to handle it here. Specifically:
You must add code to the C<_init()> method to store the value of your
item as an attribute value, defaulting it if necessary. Using our
naming convention, a single-valued item would be stored like this:
$self->{_new_item} = delete $args{'new-item'} //
$NEW_ITEM_DEFAULT;
If the item has synonyms (e.g. both 'color' and 'colour' meaning the
same thing), you must check for all of them. If the
item took a list of values, they would be parsed apart and stored as
an array reference.
You must also add and document an accessor for your new item. This
would look something like this:
sub new_item {
my ($self) = @_;
return $self->{_new_item};
}
In the case of multi-valued items, the accessor must return the array
reference, so the above specimen code works in that case also.
Note that no validation is done here -- this class is simply a bridge
between the physical F<.perlcriticrc> file and
L<Perl::Critic::Config|Perl::Critic::Config>, which is where the
action is.
If your new item is a command option without a corresponding global
configuration item, you do not need to modify this file.
=head3 F<lib/Perl/Critic/Config.pm>
You must write a C<_validate_and_store_new_item()> method to validate
and store the value of the new item. The signature of this method
depends on the details of your new item, but it must include at least
the value of the item, B<even if> there is no corresponding global
configuration item. If it is possible to get validation failures, it
will also need an errors object to add the validation exception to.
Because the details vary, the best way to proceed is probably to find
a method similar to the one you want to write, and implement from
there. The C<_validate_and_store_top()> method is a reasonable
starting point for an item having a single value. The validated value
needs to be stored in C<< $self->{_new_item} >>.
You must call C<_validate_and_store_new_item()> in the C<_init()>
method.
You must write and document an accessor method for the value of the
new item. The typical accessor method for a single-valued item is
sub new_item {
my ($self) = @_;
return $self->{_new_item};
}
but the accessor for a multi-valued item must return a list:
sub new_item {
my ($self) = @_;
return @{ $self->{_new_item} };
}
Last, you must document the item itself.
=head3 F<lib/Perl/Critic/ProfilePrototype.pm>
If your new item has a corresponding global configuration item, you
must update the C<to_string()> method to include the item in the
string. Your implementation of the item must be such that the
generated string is the same as the input string for the item, except
for whitespace.
If your new item has no corresponding global configuration item, you
do not need to change this file.
=head3 F<bin/perlcriticrc>
If your new item has a corresponding command option, you must document
it here. If it does not, you do not need to change this file.
=head3 F<examples/perlcriticrc>
If your new item has a corresponding global configuration item, you
must add it here. If it does not, you do not need to change this file.
=head2 Testing
The following test files must be considered for modification:
t/00_modules.t
t/01_config.t
t/01_config_bad_perlcritic.t
t/04_options_processor.t
t/07_command.t
t/10_user_profile.t
t/16_roundtrip_defaults.t
Depending on your new item, you may not need to change all of these,
but you should at least review them. Depending on what your new item
actually does, other test files may need to be modified as well.
=head1 DEPRECATING AND REMOVING A PUBLIC SUBROUTINE OR METHOD
This is something to be done cautiously. The code in question may only
exist to serve Perl::Critic, but if it is documented as public it may
well be in use "in the wild", either in add-ons to Perl::Critic or by
users of Perl::Critic.
Before deprecating public code, the potential deprecator must discuss
the issues on the Perl::Critic developers' mailing list. There are
instructions on how to subscribe to this list in
L<Perl::Critic/EXTENDING THE CRITIC>.
Once agreement is reached, the technical details of the deprecation
are fairly simple.
You must insert something like the following in the code to be
deprecated:
warnings::warnif(
'deprecated',
'Perl::Critic::Utils::foo() deprecated, use blah::foo() instead.',
);
You should have the deprecated subroutine delegate its functionality
to the new subroutine, if that is practical (it may not be).
You must update the documentation to say that the old code is
deprecated, and what the replacement is.
After the old code has been deprecated for a couple production
releases, it can be removed.
=head1 AUTHOR
Thomas R. Wyant, III F<wyant at cpan dot org>
=head1 COPYRIGHT
Copyright (c) 2009-2011 Thomas R. Wyant, III
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. The full text of this license
can be found in the LICENSE file included with this module.
=cut
##############################################################################
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# fill-column: 78
# indent-tabs-mode: nil
# c-indentation-style: bsd
# End:
# ex: set ts=8 sts=4 sw=4 tw=70 ft=pod expandtab shiftround :
|