1 ##############################################################################
2 # $URL: http://perlcritic.tigris.org/svn/perlcritic/trunk/Perl-Critic/lib/Perl/Critic/Policy/Documentation/PodSpelling.pm $
3 # $Date: 2008-07-03 10:19:10 -0500 (Thu, 03 Jul 2008) $
6 ##############################################################################
8 package Perl::Critic::Policy::Documentation::PodSpelling;
14 use English qw(-no_match_vars);
19 use List::MoreUtils qw(uniq);
21 use Perl::Critic::Utils qw{
27 use Perl::Critic::Exception::Fatal::Generic qw{ throw_generic };
29 use base 'Perl::Critic::Policy';
31 our $VERSION = '1.088';
33 #-----------------------------------------------------------------------------
35 Readonly::Scalar my $POD_RX => qr{\A = (?: for|begin|end ) }mx;
36 Readonly::Scalar my $DESC => q{Check the spelling in your POD};
37 Readonly::Scalar my $EXPL => [148];
39 #-----------------------------------------------------------------------------
41 sub supported_parameters {
44 name => 'spell_command',
45 description => 'The command to invoke to check spelling.',
46 default_string => 'aspell list',
51 description => 'The words to not consider as misspelled.',
52 default_string => $EMPTY,
53 behavior => 'string list',
58 sub default_severity { return $SEVERITY_LOWEST }
59 sub default_themes { return qw( core cosmetic pbp ) }
60 sub applies_to { return 'PPI::Document' }
62 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
71 sub initialize_if_enabled {
72 my ( $self, $config ) = @_;
76 require Text::ParseWords;
82 return $FALSE if not $self->_derive_spell_command_line();
84 return $FALSE if not $self->_run_spell_command( <<'END_TEST_CODE' );
87 =head1 Test The Spell Command
95 #-----------------------------------------------------------------------------
98 my ( $self, $elem, $doc ) = @_;
100 my $code = $doc->serialize();
102 my $words = $self->_run_spell_command($code);
104 return if not $words; # error running spell command
106 return if not @{$words}; # no problems found
108 return $self->violation( "$DESC: @{$words}", $EXPL, $doc );
111 #-----------------------------------------------------------------------------
113 sub _derive_spell_command_line {
116 my @words = Text::ParseWords::shellwords($self->_get_spell_command());
120 if (! File::Spec->file_name_is_absolute($words[0])) {
121 $words[0] = File::Which::which($words[0]);
123 if (! $words[0] || ! -x $words[0]) {
126 $self->_set_spell_command_line(\@words);
128 return $self->_get_spell_command_line();
131 #-----------------------------------------------------------------------------
133 sub _get_spell_command {
136 return $self->{_spell_command};
139 sub _set_spell_command {
140 my ( $self, $spell_command ) = @_;
142 $self->{_spell_command} = $spell_command;
147 #-----------------------------------------------------------------------------
149 sub _get_spell_command_line {
152 return $self->{_spell_command_line};
155 sub _set_spell_command_line {
156 my ( $self, $spell_command_line ) = @_;
158 $self->{_spell_command_line} = $spell_command_line;
163 #-----------------------------------------------------------------------------
165 sub _get_stop_words {
168 return $self->{_stop_words};
171 sub _set_stop_words {
172 my ( $self, $stop_words ) = @_;
174 $self->{_stop_words} = $stop_words;
179 #-----------------------------------------------------------------------------
181 sub _run_spell_command {
182 my ($self, $code) = @_;
184 my $infh = IO::String->new( $code );
186 my $outfh = File::Temp->new()
187 or throw_generic "Unable to create tempfile: $OS_ERROR";
189 my $outfile = $outfh->filename();
192 local $EVAL_ERROR = undef;
195 # temporarily add our special wordlist to this annoying global
196 local %Pod::Wordlist::Wordlist = ##no critic(ProhibitPackageVars)
197 %{ $self->_get_stop_words() };
199 Pod::Spell->new()->parse_from_filehandle($infh, $outfh);
200 close $outfh or throw_generic "Failed to close pod temp file: $OS_ERROR";
201 return if not -s $outfile; # Bail out if no words to spellcheck
203 # run spell command and fetch output
204 local $SIG{PIPE} = sub { $got_sigpipe = 1; };
205 my $command_line = join $SPACE, @{$self->_get_spell_command_line()};
206 open my $aspell_out_fh, q{-|}, "$command_line < $outfile" ## Is this portable??
207 or throw_generic "Failed to open handle to spelling program: $OS_ERROR";
209 @words = uniq( <$aspell_out_fh> );
211 or throw_generic "Failed to close handle to spelling program: $OS_ERROR";
217 # Why is this extra step needed???
218 @words = grep { not exists $Pod::Wordlist::Wordlist{$_} } @words; ## no critic(ProhibitPackageVars)
222 # Eat anything we did ourselves above, propagate anything else.
225 and not ref Perl::Critic::Exception::Fatal::Generic->caught()
227 ref $EVAL_ERROR ? $EVAL_ERROR->rethrow() : die $EVAL_ERROR; ## no critic (ErrorHandling::RequireCarping)
236 #-----------------------------------------------------------------------------
242 #-----------------------------------------------------------------------------
246 =for stopwords Hmm stopwords
250 Perl::Critic::Policy::Documentation::PodSpelling - Check your spelling.
254 This Policy is part of the core L<Perl::Critic> distribution.
259 Did you write the documentation? Check.
261 Did you document all of the public methods? Check.
263 Is your documentation readable? Hmm...
265 Ideally, we'd like Perl::Critic to tell you when your documentation is
266 inadequate. That's hard to code, though. So, inspired by
267 L<Test::Spelling>, this module checks the spelling of your POD. It
268 does this by pulling the prose out of the code and passing it to an
269 external spell checker. It skips over words you flagged to ignore.
270 If the spell checker returns any misspelled words, this policy emits a
273 If anything else goes wrong -- you don't have Pod::Spell installed or
274 we can't locate the spell checking program or (gasp!) your module has
275 no POD -- then this policy passes.
277 To add exceptions on a module-by-module basis, add "stopwords" as
278 described in L<Pod::Spell>. For example:
280 =for stopword gibbles
282 =head1 Gibble::Manip -- manipulate your gibbles
288 This policy can be configured to tell which spell checker to use or to
289 set a global list of spelling exceptions. To do this, put entries in
290 a F<.perlcriticrc> file like this:
292 [Documentation::PodSpelling]
293 spell_command = aspell list
294 stop_words = gibbles foobar
296 The default spell command is C<aspell list> and it is interpreted as a
297 shell command. We parse the individual arguments via
298 L<Text::ParseWords> so feel free to use quotes around your arguments.
299 If the executable path is an absolute file name, it is used as-is. If
300 it is a relative file name, we employ L<File::Which> to convert it to
301 an absolute path via the C<PATH> environment variable. As described
302 in Pod::Spell and Test::Spelling, the spell checker must accept text
303 on STDIN and print misspelled words one per line on STDOUT.
307 L<Pod::Spell> is not included with Perl::Critic, nor is a spell
312 Initial development of this policy was supported by a grant from the Perl Foundation.
316 Chris Dolan <cdolan@cpan.org>
320 Copyright (c) 2007-2008 Chris Dolan. Many rights reserved.
322 This program is free software; you can redistribute it and/or modify
323 it under the same terms as Perl itself. The full text of this license
324 can be found in the LICENSE file included with this module
330 # cperl-indent-level: 4
332 # indent-tabs-mode: nil
333 # c-indentation-style: bsd
335 # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :