SlideShare a Scribd company logo
1 of 174
Download to read offline
Jeffrey Thalhammer




                                  S o f t w a r e   S y s t e m s




                               jeff@imaginative-software.com


Thursday, September 30, 2010
Some Ways Are Better Than Others
                                        San Francisco Perl Mongers
                                               July 27, 2010




Thursday, September 30, 2010
HaveYou Ever?...


Thursday, September 30, 2010
print ‘Hello, my name is $namen’;




Thursday, September 30, 2010
eval ‘reqire Some::Module’;




Thursday, September 30, 2010
if(not $foo && $bar or $baz){
...
}




Thursday, September 30, 2010
if($this == ‘that’){
...
}




Thursday, September 30, 2010
What Is Perl::Critic?




Thursday, September 30, 2010
What Is Perl::Critic?

                    • Static Source Code Analyzer




Thursday, September 30, 2010
What Is Perl::Critic?

                    • Static Source Code Analyzer
                    • Like “lint” but for Perl code



Thursday, September 30, 2010
What Is Perl::Critic?

                    • Static Source Code Analyzer
                    • Like “lint” but for Perl code
                    • Finds bugs & enforces style


Thursday, September 30, 2010
What Is Perl::Critic?

                    • Static Source Code Analyzer
                    • Like “lint” but for Perl code
                    • Finds bugs & enforces style
                    • Mostly based on Perl Best Practices

Thursday, September 30, 2010
Genesis




Thursday, September 30, 2010
Genesis

                    • Large legacy code base (~500k lines)




Thursday, September 30, 2010
Genesis

                    • Large legacy code base (~500k lines)
                    • Evolved organically over 10 years




Thursday, September 30, 2010
Genesis

                    • Large legacy code base (~500k lines)
                    • Evolved organically over 10 years
                    • Many developers with various skill levels



Thursday, September 30, 2010
Genesis

                    • Large legacy code base (~500k lines)
                    • Evolved organically over 10 years
                    • Many developers with various skill levels
                    • “Worst code I’ve ever seen.”


Thursday, September 30, 2010
Genesis

                    • Large legacy code base (~500k lines)
                    • Evolved organically over 10 years
                    • Many developers with various skill levels
                    • “Worst code I’ve ever seen.”
                    • “Most profitable code I’ve ever seen.”

Thursday, September 30, 2010
Genesis




Thursday, September 30, 2010
Genesis

                    • Highly inconsistent (anti-patterns)




Thursday, September 30, 2010
Genesis

                    • Highly inconsistent (anti-patterns)
                    • Extremely complicated code



Thursday, September 30, 2010
Genesis

                    • Highly inconsistent (anti-patterns)
                    • Extremely complicated code
                    • Much copy & pasted code


Thursday, September 30, 2010
Genesis

                    • Highly inconsistent (anti-patterns)
                    • Extremely complicated code
                    • Much copy & pasted code
                    • Cargo cult

Thursday, September 30, 2010
Behold!




Thursday, September 30, 2010
But How Can I Get
                        Others To Learn Perl
                          Best Practices?


Thursday, September 30, 2010
Introducing PPI




Thursday, September 30, 2010
Introducing PPI

                    • By Adam Kennedy (a.k.a. alias)




Thursday, September 30, 2010
Introducing PPI

                    • By Adam Kennedy (a.k.a. alias)
                    • “Parse Perl Independently”




Thursday, September 30, 2010
Introducing PPI

                    • By Adam Kennedy (a.k.a. alias)
                    • “Parse Perl Independently”
                    • Parse and lex Perl code into DOM



Thursday, September 30, 2010
Introducing PPI

                    • By Adam Kennedy (a.k.a. alias)
                    • “Parse Perl Independently”
                    • Parse and lex Perl code into DOM
                    • Only perl can parse Perl


Thursday, September 30, 2010
Introducing PPI

                    • By Adam Kennedy (a.k.a. alias)
                    • “Parse Perl Independently”
                    • Parse and lex Perl code into DOM
                    • Only perl can parse Perl
                    • PPI comes pretty darn close

Thursday, September 30, 2010
And Perl::Critic Was
                                Born



Thursday, September 30, 2010
Getting Feet Wet
                                                  File: Bar.pm

 1         package Foo;
 2
 3         sub showGreeting ($) {
 4           my $name = shift;
 5           return undef if not $name;
 6           open FH, “>out.txt”;
 7           print FH ‘Hello $namen’;
 8         }

Thursday, September 30, 2010
Getting Feet Wet
            USAGE: perlcritic FILENAME

[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
Package declaration must match filename at line 1, column 1. Correct
the filename or package statement. (Severity: 5)
Subroutine prototypes used at line 3, column 1. See page 194 of PBP.
(Severity: 5)
Code before strictures are enabled at line 3, column 1. See page 429
of PBP. (Severity: 5)
"return" statement with explicit "undef" at line 5, column 3. See
page 199 of PBP. (Severity: 5)
Bareword file handle opened at line 6, column 3. See pages 202,204
of PBP. (Severity: 5)
Two-argument "open" used at line 6, column 3. See page 207 of PBP.
(Severity: 5)



Thursday, September 30, 2010
A Closer Look


Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)




Thursday, September 30, 2010
A Closer Look
                       Description



Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)




Thursday, September 30, 2010
A Closer Look
                       Description       Location



Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)




Thursday, September 30, 2010
A Closer Look
                       Description         Location



Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)


                                  PBP
                               Reference


Thursday, September 30, 2010
A Closer Look
                       Description              Location



Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)


                                  PBP
                                           Severity
                               Reference


Thursday, September 30, 2010
Fixing Violations
                                                   File: Bar.pm

 1         package Bar;
 2
 3         use strict;
 4         use warnings;
 5
 6         sub showGreeting {
 7           my $name = shift;
 8           return if not $name;
 9           open my $fh, ‘>’, “out.txt”;
 10          print $fh ‘Hello $name’;
 11        }
Thursday, September 30, 2010
Try Again
            USAGE: perlcritic FILENAME

 [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
 Bar.pm source OK




Thursday, September 30, 2010
Tightening The Screws
            USAGE: perlcritic --serverity N FILENAME

 [jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm
 Module does not end with "1;" at line 6, column 1. Must end with a
 recognizable true value. (Severity: 4)
 Subroutine "showGreeting" does not end with "return" at line 6,
 column 1. See page 197 of PBP. (Severity: 4)
 Close filehandles as soon as possible after opening them at line 9,
 column 1. See page 209 of PBP. (Severity: 4)




Thursday, September 30, 2010
File: Bar.pm

 1          package Bar;
 2
 3          use strict;
 4          use warnings;
 5
 6          sub showGreeting {
 7           my $name = shift;
 8           return if not $name;
 9           open my $fh, ‘>’, $name;
 10          print $fh ‘Hello $name’;
 11          close $fh;
 12          return 1;
 13          }
 14
 15             1;
Thursday, September 30, 2010
One More Time
            USAGE: perlcritic --serverity N FILENAME

 [jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm
 Bar.pm source OK




Thursday, September 30, 2010
USAGE: perlcritic --serverity N FILENAME




Thursday, September 30, 2010
USAGE: perlcritic --serverity N FILENAME
[jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of
PBP. (Severity: 2)
No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)
Return value of "close" ignored at line 11, column 1. Check the return value of "close" for
success. (Severity: 2)




Thursday, September 30, 2010
USAGE: perlcritic --serverity N FILENAME
[jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of
PBP. (Severity: 2)
No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)
Return value of "close" ignored at line 11, column 1. Check the return value of "close" for
success. (Severity: 2)

[jeff@callahan:/Users/jeff]$ perlcritic --severity 1 Bar.pm
RCS keywords $Id$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of
PBP. (Severity: 2)
RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. See page 441 of
PBP. (Severity: 2)
No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)
Subroutine "showGreeting" is not all lower case or all upper case at line 6, column 1. See
pages 45,46 of PBP. (Severity: 1)
Return value of "open" ignored at line 9, column 1. Check the return value of "open" for
success. (Severity: 3)
Return value of flagged function ignored - open at line 9, column 1. See pages 208,278 of
PBP. (Severity: 1)
File handle for "print" or "printf" is not braced at line 10, column 1. See page 217 of
PBP. (Severity: 1)
Return value of flagged function ignored - print at line 10, column 1. See pages 208,278 of
PBP. (Severity: 1)
String *may* require interpolation at line 10, column 11. See page 51 of PBP. (Severity:
1)
Return value of "close" ignored at line 11, column 1. Check the return value of "close" for
success. (Severity: 2)
Thursday, September 30, 2010
Five Levels Of Severity




Thursday, September 30, 2010
Five Levels Of Severity
        • 5 (Highest): Likely bug or widely accepted practice




Thursday, September 30, 2010
Five Levels Of Severity
        • 5 (Highest): Likely bug or widely accepted practice
        • 1 (Lowest): Purely cosmetic or highly controversial




Thursday, September 30, 2010
Five Levels Of Severity
        • 5 (Highest): Likely bug or widely accepted practice
        • 1 (Lowest): Purely cosmetic or highly controversial

        • Severities are determined by Policy authors


Thursday, September 30, 2010
Five Levels Of Severity
        • 5 (Highest): Likely bug or widely accepted practice
        • 1 (Lowest): Purely cosmetic or highly controversial

        • Severities are determined by Policy authors
        • Also have names (brutal, cruel, harsh, stern, gentle)

Thursday, September 30, 2010
Five Levels Of Severity
        • 5 (Highest): Likely bug or widely accepted practice
        • 1 (Lowest): Purely cosmetic or highly controversial

        • Severities are determined by Policy authors
        • Also have names (brutal, cruel, harsh, stern, gentle)
        • Can be configured
Thursday, September 30, 2010
Configuration
                               perlcritic [-12345 | --brutal | --cruel | --harsh | --stern | --gentle]
                                          [--severity number | name] [{-p | --profile} file | --noprofile]
                                          [--top [ number ]] [--theme expression] [--include pattern]
                                          [--exclude pattern] [{-s | --single-policy} pattern]
                                          [--only | --noonly] [--profile-strictness {warn|fatal|quiet}]
                                          [--force | --noforce] [--statistics] [--statistics-only]
                                          [--count | -C] [--verbose {number | format}]
                                          [--color | --nocolor] [--pager pager] [--quiet]
                                          [--color-severity-highest color_specification]
                                          [--color-severity-high color_specification]
                                          [--color-severity-medium color_specification]
                                          [--color-severity-low color_specification]
                                          [--color-severity-lowest color_specification]
                                          [--files-with-violations | -l]
                                          [--files-without-violations | -L]
                                          {FILE | DIRECTORY | STDIN}

                                 perlcritic --profile-proto

                                 perlcritic { --list | --list-enabled | --list-themes | --doc pattern [...] }

                                 perlcritic { --help | --options | --man | --version }

Thursday, September 30, 2010
Configuration




Thursday, September 30, 2010
Configuration

                    • .perlcriticrc file




Thursday, September 30, 2010
Configuration

                    • .perlcriticrc file
                    • In home directory or project directory



Thursday, September 30, 2010
Configuration

                    • .perlcriticrc file
                    • In home directory or project directory
                    • INI-style syntax


Thursday, September 30, 2010
Configuration
                                              File: ~/.perlcriticrc

    # Global options here
    severity = 2
    pager = /usr/bin/less
    color = 1

                          These will be the
                           defaults, unless
                          overridden at the
                           command-line
Thursday, September 30, 2010
USAGE: perlcritic FILENAME
[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of
PBP. (Severity: 2)
No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)
Return value of "close" ignored at line 11, column 1. Check the return value of "close" for
success. (Severity: 2)




             Default severity is now 2, as
              defined in .perlcriticrc file




Thursday, September 30, 2010
Getting More
                                Information
            USAGE: perlcritic --verbose 8 FILENAME

[jeff@callahan:/Users/jeff]$ perlcritic --verbose 8 Bar.pm
[Miscellanea::RequireRcsKeywords] RCS keywords $Revision$, $Source$, $Date$ not found at
line 1, column 1. (Severity: 2)
[Modules::RequireVersionVar] No "$VERSION" variable found at line 1, column 1. (Severity:
2)
[InputOutput::RequireCheckedClose] Return value of "close" ignored at line 11, column 1.
(Severity: 2)




                               Now showing name of the Policy

Thursday, September 30, 2010
Configuration
                                         File: ~/.perlcriticrc
   # Global options here
   severity = 2
   verbose = 8

   # Policy options here
   [Modules::RequireVersionVar]
   severity = 1

   # These policies are disabled
   [-Miscellanea::RequireRcsKeywords]
   [-InputOutput::RequireCheckedClose]


Thursday, September 30, 2010
Now running with
                                severity = 2, and
                                  verbose = 8


[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
Bar.pm source OK




Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity




Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail




Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”




Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”

        • Some of the available details:



Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”

        • Some of the available details:
         • Message, diagnostic (PBP pages), POD extract


Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”

        • Some of the available details:
         • Message, diagnostic (PBP pages), POD extract
         • Line, column, file name, file path, severity

Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”

        • Some of the available details:
         • Message, diagnostic (PBP pages), POD extract
         • Line, column, file name, file path, severity
         • Source code, PPI element, policy name
Thursday, September 30, 2010
Eleven Levels Of
                                   Verbosity
        • Increasing levels of detail
        • Can be customized with printf-style formats:
               “%m near %s at line %l in file %f”

        • Some of the available details:
         • Message, diagnostic (PBP pages), POD extract
         • Line, column, file name, file path, severity
         • Source code, PPI element, policy name
        • Integrates with editor’s compile-mode (vim, emacs)
Thursday, September 30, 2010
Lots of Policies




Thursday, September 30, 2010
Lots of Policies

                    • 128 Policies in Perl::Critic core




Thursday, September 30, 2010
Lots of Policies

                    • 128 Policies in Perl::Critic core
                    • 85 Policies taken directly from PBP



Thursday, September 30, 2010
Lots of Policies

                    • 128 Policies in Perl::Critic core
                    • 85 Policies taken directly from PBP
                    • Dozens of add-on Policies in CPAN


Thursday, September 30, 2010
Lots of Policies

                    • 128 Policies in Perl::Critic core
                    • 85 Policies taken directly from PBP
                    • Dozens of add-on Policies in CPAN
                    • Covering all aspects of programming

Thursday, September 30, 2010
Just A Taste




Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap




Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode




Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode
             • NamingConventions::ProhibitAmbiguousNames




Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode
             • NamingConventions::ProhibitAmbiguousNames
             • Subroutines::ProhibitExcessComplexity



Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode
             • NamingConventions::ProhibitAmbiguousNames
             • Subroutines::ProhibitExcessComplexity
             • CodeLayout::RequireTidyCode


Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode
             • NamingConventions::ProhibitAmbiguousNames
             • Subroutines::ProhibitExcessComplexity
             • CodeLayout::RequireTidyCode
             • RegularExpressions::ProhibitFixedStringMatches

Thursday, September 30, 2010
Just A Taste
             • BuiltinFunctions::ProhibitVoidMap
             • ControlStructures::ProhibitUnreachableCode
             • NamingConventions::ProhibitAmbiguousNames
             • Subroutines::ProhibitExcessComplexity
             • CodeLayout::RequireTidyCode
             • RegularExpressions::ProhibitFixedStringMatches
             • Documentation::RequirePodSections
Thursday, September 30, 2010
Tips For Choosing Policies




Thursday, September 30, 2010
Tips For Choosing Policies
             • Use --profile-proto option to generate your
                     first .perlcriticrc file.




Thursday, September 30, 2010
Tips For Choosing Policies
             • Use --profile-proto option to generate your
                     first .perlcriticrc file.
             • Start with --severity 5 and work your way
                     down.




Thursday, September 30, 2010
Tips For Choosing Policies
             • Use --profile-proto option to generate your
                     first .perlcriticrc file.
             • Start with --severity 5 and work your way
                     down.
             • Use the --doc option to view Policy
                     documentation.




Thursday, September 30, 2010
Tips For Choosing Policies
             • Use --profile-proto option to generate your
                     first .perlcriticrc file.
             • Start with --severity 5 and work your way
                     down.
             • Use the --doc option to view Policy
                     documentation.
             • Disable or reconfigure Policies that you don’t
                     agree with.


Thursday, September 30, 2010
Tips For Choosing Policies
             • Use --profile-proto option to generate your
                     first .perlcriticrc file.
             • Start with --severity 5 and work your way
                     down.
             • Use the --doc option to view Policy
                     documentation.
             • Disable or reconfigure Policies that you don’t
                     agree with.
             • Don’t try to do it all at once.
Thursday, September 30, 2010
Bending The Rules
           #!/usr/bin/perl

           print “Hin” if $^O eq ‘darwin’;
           print “Hellon” if $^O =~ /win/i;




Thursday, September 30, 2010
Bending The Rules
           #!/usr/bin/perl

           print “Hin” if $^O eq ‘darwin’;
           print “Hellon” if $^O =~ /win/i;


                • This would violate ProhibitPostfixControls


Thursday, September 30, 2010
Bending The Rules
           #!/usr/bin/perl

           print “Hin” if $^O eq ‘darwin’;
           print “Hellon” if $^O =~ /win/i;


                • This would violate ProhibitPostfixControls
                • But we really like the way it reads

Thursday, September 30, 2010
Bending The Rules
           #!/usr/bin/perl

           ## no critic

           print “Hin” if $^O eq ‘darwin’;
           print “Hellon” if $^O =~ /win/i;

           ## use critic


            •       ## no critic disables Perl::Critic until end
                    of block or until ## use critic
Thursday, September 30, 2010
Bending The Rules

           #!/usr/bin/perl

           frobulate($this) if $that; ## no critic



           • When attached to a line of code ##          no
                   critic disables Perl::Critic for that line only



Thursday, September 30, 2010
Bending The Rules

      #!/usr/bin/perl

      frobulate($this) if $that; ## no critic (ProhibitPostfixControls)




                                   You can also specify exactly
                                    which Policies to disable



Thursday, September 30, 2010
Perl::Critic Applied
         As command-line tool...
         $> perlcritic --verbose 8 MyModule.pm


         As pragma...
         #!/usr/bin/perl

         use strict;
         use warnings;
         use criticism ‘brutal’;
         ...

Thursday, September 30, 2010
Perl::Critic Applied

      As test library...
      #!/usr/bin/perl

      use Test::Perl::Critic;
      all_critic_ok();




Thursday, September 30, 2010
Tips For Working With
                         Legacy Code




Thursday, September 30, 2010
Tips For Working With
                         Legacy Code
             • Use --statistics option to identify hotspots.




Thursday, September 30, 2010
Tips For Working With
                         Legacy Code
             • Use --statistics option to identify hotspots.
             • Segregate new code from old code.




Thursday, September 30, 2010
Tips For Working With
                         Legacy Code
             • Use --statistics option to identify hotspots.
             • Segregate new code from old code.
             • Use “no critic” annotations around blocks of
                     nasty legacy code.




Thursday, September 30, 2010
Tips For Working With
                         Legacy Code
             • Use --statistics option to identify hotspots.
             • Segregate new code from old code.
             • Use “no critic” annotations around blocks of
                     nasty legacy code.
             • Don’t mix clean-up commits with regular work.

Thursday, September 30, 2010
Tips For Working With
                         Legacy Code
             • Use --statistics option to identify hotspots.
             • Segregate new code from old code.
             • Use “no critic” annotations around blocks of
                     nasty legacy code.
             • Don’t mix clean-up commits with regular work.
             • Use Test::Perl::Critic::Progressive to chip away at
                     the problem.

Thursday, September 30, 2010
Test::Perl::Critic::Progressive




Thursday, September 30, 2010
Test::Perl::Critic::Progressive

             • TAP-compatible test module.




Thursday, September 30, 2010
Test::Perl::Critic::Progressive

             • TAP-compatible test module.
             • Always passes the first time.




Thursday, September 30, 2010
Test::Perl::Critic::Progressive

             • TAP-compatible test module.
             • Always passes the first time.
             • Makes a record of violations (in a local file).



Thursday, September 30, 2010
Test::Perl::Critic::Progressive

             • TAP-compatible test module.
             • Always passes the first time.
             • Makes a record of violations (in a local file).
             • Subsequent runs will fail if new violations are
                     added.




Thursday, September 30, 2010
Test::Perl::Critic::Progressive

             • TAP-compatible test module.
             • Always passes the first time.
             • Makes a record of violations (in a local file).
             • Subsequent runs will fail if new violations are
                     added.
             • Works best as part of continuous integration
                     system.

Thursday, September 30, 2010
Extending Perl::Critic




Thursday, September 30, 2010
Extending Perl::Critic
             • Each Policy is a module in the Perl::Critic::Policy
                     namespace.




Thursday, September 30, 2010
Extending Perl::Critic
             • Each Policy is a module in the Perl::Critic::Policy
                     namespace.
             • Each Policy extends the Perl::Critic::Policy
                     abstract base class.




Thursday, September 30, 2010
Extending Perl::Critic
             • Each Policy is a module in the Perl::Critic::Policy
                     namespace.
             • Each Policy extends the Perl::Critic::Policy
                     abstract base class.
             • Automatically loaded via Module::Pluggable.


Thursday, September 30, 2010
Extending Perl::Critic
             • Each Policy is a module in the Perl::Critic::Policy
                     namespace.
             • Each Policy extends the Perl::Critic::Policy
                     abstract base class.
             • Automatically loaded via Module::Pluggable.
             • Sometimes as little as 20 lines of code.

Thursday, September 30, 2010
Extending Perl::Critic
             • Each Policy is a module in the Perl::Critic::Policy
                     namespace.
             • Each Policy extends the Perl::Critic::Policy
                     abstract base class.
             • Automatically loaded via Module::Pluggable.
             • Sometimes as little as 20 lines of code.
             • See POD for step-by-step tutorial.
Thursday, September 30, 2010
Extending Perl::Critic




Thursday, September 30, 2010
Extending Perl::Critic


             • Policies do not have to be from PBP.




Thursday, September 30, 2010
Extending Perl::Critic


             • Policies do not have to be from PBP.
             • Your Policies can conflict with existing ones.



Thursday, September 30, 2010
Extending Perl::Critic


             • Policies do not have to be from PBP.
             • Your Policies can conflict with existing ones.
             • Policies can be general or domain-specific.


Thursday, September 30, 2010
Suppose We Want To
                      Ban Really Complex
                     Boolean Expressions...
      if ($foo && ($bar || $baz) || !($bar && $baz) || $qux) {
      ...
      }




Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;


                                        Put Policy in
use strict;
                               Perl::Critic::Policy namespace
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;


                                        Policies organized by
use strict;
                                        PBP table of contents
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;                  Some boilerplate
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;                  Extend the Policy baseclass

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;
                                    Define default attributes
sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;



use strict;
use warnings;
use Readonly;

use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;



sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }


Thursday, September 30, 2010
sub default_severity{ return $SEVERITY_HIGH };
sub default_theme{ return qw(complexity) };
sub supported_params{ return () }

Readonly::Scalar my $DESC =>
 'Too many boolean operators';
Readonly::Scalar my $EXPL =>
 'Complex expressions are hard to read';


Thursday, September 30, 2010
Just declaring some variables to
                                  store the Description and
                                          Explanation

sub default_severity{ return $SEVERITY_HIGH };
sub default_theme{ return qw(complexity) };
sub supported_params{ return () }

Readonly::Scalar my $DESC =>
 'Too many boolean operators';
Readonly::Scalar my $EXPL =>
 'Complex expressions are hard to read';


Thursday, September 30, 2010
sub default_severity{ return $SEVERITY_HIGH };
sub default_theme{ return qw(complexity) };
sub supported_params{ return () }

Readonly::Scalar my $DESC =>
 'Too many boolean operators';
Readonly::Scalar my $EXPL =>
 'Complex expressions are hard to read';


Thursday, September 30, 2010
How Does PPI See Your Code?
            USAGE: ppidump ‘some source code’
            USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
  PPI::Statement::Compound
   PPI::Token::Word 	 'if'
    PPI::Structure::Condition 	 ... )
                               (
      PPI::Statement::Expression
       PPI::Token::Symbol 	 '$foo'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$bar'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$baz'
    PPI::Structure::Block 	 { ... }
Thursday, September 30, 2010
How Does PPI See Your Code?
            USAGE: ppidump ‘some source code’
            USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
  PPI::Statement::Compound
   PPI::Token::Word 	 'if'               This is where we
    PPI::Structure::Condition 	 ... )
                               (
                                         want to look for
      PPI::Statement::Expression
       PPI::Token::Symbol 	 '$foo'           violations
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$bar'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$baz'
    PPI::Structure::Block 	 { ... }
Thursday, September 30, 2010
How Does PPI See Your Code?
            USAGE: ppidump ‘some source code’
            USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
  PPI::Statement::Compound
   PPI::Token::Word 	 'if'
    PPI::Structure::Condition 	 ... )
                               (
      PPI::Statement::Expression
       PPI::Token::Symbol 	 '$foo'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$bar'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$baz'
    PPI::Structure::Block 	 { ... }
Thursday, September 30, 2010
How Does PPI See Your Code?
            USAGE: ppidump ‘some source code’
            USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
  PPI::Statement::Compound
   PPI::Token::Word 	 'if'
    PPI::Structure::Condition 	 ... )
                               (
      PPI::Statement::Expression
       PPI::Token::Symbol 	 '$foo'      The offensive code
       PPI::Token::Operator '&&'         is in a child node
       PPI::Token::Symbol 	 '$bar'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$baz'
    PPI::Structure::Block 	 { ... }
Thursday, September 30, 2010
How Does PPI See Your Code?
            USAGE: ppidump ‘some source code’
            USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
  PPI::Statement::Compound
   PPI::Token::Word 	 'if'
    PPI::Structure::Condition 	 ... )
                               (
      PPI::Statement::Expression
       PPI::Token::Symbol 	 '$foo'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$bar'
       PPI::Token::Operator '&&'
       PPI::Token::Symbol 	 '$baz'
    PPI::Structure::Block 	 { ... }
Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
Declare which types of PPI
                               elements we are interested in
 sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }                   As document is traversed, we
                         get called each time a
                     PPI::Structure::Conditional is
                              encountered

Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
                     We also get a reference to
 }
                       the entire document




Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }

       Your job is to figure out if this
        $elem is violating the Policy

Thursday, September 30, 2010
sub applies_to { return ‘PPI::Structure::Condition’ }


 sub violates {
   my ( $self, $elem, $doc ) = @_;
   # meat goes here!
 }




Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}

sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}                       Search recursively for all
                               Operators
sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}

sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}              Count those that are
                               boolean
sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}

sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}
                               Using regex to determine
sub _is_boolean {                  type of operator
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}

sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}    Return a violation if count
                        exceeds maximum
sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
sub violates {
  my ( $self, $elem, $doc ) = @_;
  my $ops = $elem->find(‘PPI::Token::Operator’);
  my $count = grep { _is_boolean($_) } @{$ops}
  if ($count > 3) {
    return $self->violation($DESC, $EXPL, $elem )
  }
}

sub _is_boolean {
  my ($elem) = @_;
  return $elem->content()
   =~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
Testing Your Policy




Thursday, September 30, 2010
Testing Your Policy

             • Use Test::Perl::Critic::Policy.




Thursday, September 30, 2010
Testing Your Policy

             • Use Test::Perl::Critic::Policy.
             • Specify test cases using POD-like notation.




Thursday, September 30, 2010
Testing Your Policy

             • Use Test::Perl::Critic::Policy.
             • Specify test cases using POD-like notation.
             • Write Perl as Perl, not strings.



Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits       Give test a name
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0     How many violations to                   expect
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut
                                                 Write offending code
     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
                                    Code region ends                     when
     ## failures 2                  next “## name” is                    found
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
     #----------------------------------------
     ## name Within limits
     ## failures 0
     ## cut

     if ($foo && $bar) {}
     while ($foo or $bar || $baz) {}

     #----------------------------------------
     ## name Too many operators
     ## failures 2
     ## cut

     if ($foo && $bar or not $baz and $quxx) {}
     while ($foo or $bar || $baz && quxx and not $quip) {}


Thursday, September 30, 2010
Questions?



Thursday, September 30, 2010
Thank You!



Thursday, September 30, 2010
Jeffrey Thalhammer



                                  S o f t w a r e   S y s t e m s




                               jeff@imaginative-software.com


Thursday, September 30, 2010

More Related Content

Similar to Perl Critic In Depth

OSMC 2010 | OpenNMS Kickstart by Ronny Trommer
OSMC 2010 | OpenNMS Kickstart by Ronny TrommerOSMC 2010 | OpenNMS Kickstart by Ronny Trommer
OSMC 2010 | OpenNMS Kickstart by Ronny TrommerNETWAYS
 
RubyConf UY 2010
RubyConf UY 2010RubyConf UY 2010
RubyConf UY 2010Brian Ford
 
Aaron Quint - Ruby Kaigi Presentation
Aaron Quint - Ruby Kaigi PresentationAaron Quint - Ruby Kaigi Presentation
Aaron Quint - Ruby Kaigi Presentationquirkey
 
Large problems, Mostly Solved
Large problems, Mostly SolvedLarge problems, Mostly Solved
Large problems, Mostly Solvedericholscher
 
Web Development With Ruby - From Simple To Complex
Web Development With Ruby - From Simple To ComplexWeb Development With Ruby - From Simple To Complex
Web Development With Ruby - From Simple To ComplexBrian Hogan
 

Similar to Perl Critic In Depth (10)

Python_book.pdf
Python_book.pdfPython_book.pdf
Python_book.pdf
 
Python
PythonPython
Python
 
Python
PythonPython
Python
 
Python
PythonPython
Python
 
Noboxing plugin
Noboxing pluginNoboxing plugin
Noboxing plugin
 
OSMC 2010 | OpenNMS Kickstart by Ronny Trommer
OSMC 2010 | OpenNMS Kickstart by Ronny TrommerOSMC 2010 | OpenNMS Kickstart by Ronny Trommer
OSMC 2010 | OpenNMS Kickstart by Ronny Trommer
 
RubyConf UY 2010
RubyConf UY 2010RubyConf UY 2010
RubyConf UY 2010
 
Aaron Quint - Ruby Kaigi Presentation
Aaron Quint - Ruby Kaigi PresentationAaron Quint - Ruby Kaigi Presentation
Aaron Quint - Ruby Kaigi Presentation
 
Large problems, Mostly Solved
Large problems, Mostly SolvedLarge problems, Mostly Solved
Large problems, Mostly Solved
 
Web Development With Ruby - From Simple To Complex
Web Development With Ruby - From Simple To ComplexWeb Development With Ruby - From Simple To Complex
Web Development With Ruby - From Simple To Complex
 

More from Jeffrey Ryan Thalhammer

More from Jeffrey Ryan Thalhammer (6)

I Got 99 Problems But CPAN Ain't One
I Got 99 Problems But CPAN Ain't OneI Got 99 Problems But CPAN Ain't One
I Got 99 Problems But CPAN Ain't One
 
Pinto+Stratopan+Love
Pinto+Stratopan+LovePinto+Stratopan+Love
Pinto+Stratopan+Love
 
Lessons Learned About Software Development
Lessons Learned About Software DevelopmentLessons Learned About Software Development
Lessons Learned About Software Development
 
Dependency Management With Pinto
Dependency Management With PintoDependency Management With Pinto
Dependency Management With Pinto
 
CPAN For Private Code
CPAN For Private CodeCPAN For Private Code
CPAN For Private Code
 
Evolving Tests
Evolving TestsEvolving Tests
Evolving Tests
 

Perl Critic In Depth

  • 1. Jeffrey Thalhammer S o f t w a r e S y s t e m s jeff@imaginative-software.com Thursday, September 30, 2010
  • 2. Some Ways Are Better Than Others San Francisco Perl Mongers July 27, 2010 Thursday, September 30, 2010
  • 4. print ‘Hello, my name is $namen’; Thursday, September 30, 2010
  • 6. if(not $foo && $bar or $baz){ ... } Thursday, September 30, 2010
  • 8. What Is Perl::Critic? Thursday, September 30, 2010
  • 9. What Is Perl::Critic? • Static Source Code Analyzer Thursday, September 30, 2010
  • 10. What Is Perl::Critic? • Static Source Code Analyzer • Like “lint” but for Perl code Thursday, September 30, 2010
  • 11. What Is Perl::Critic? • Static Source Code Analyzer • Like “lint” but for Perl code • Finds bugs & enforces style Thursday, September 30, 2010
  • 12. What Is Perl::Critic? • Static Source Code Analyzer • Like “lint” but for Perl code • Finds bugs & enforces style • Mostly based on Perl Best Practices Thursday, September 30, 2010
  • 14. Genesis • Large legacy code base (~500k lines) Thursday, September 30, 2010
  • 15. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years Thursday, September 30, 2010
  • 16. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years • Many developers with various skill levels Thursday, September 30, 2010
  • 17. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years • Many developers with various skill levels • “Worst code I’ve ever seen.” Thursday, September 30, 2010
  • 18. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years • Many developers with various skill levels • “Worst code I’ve ever seen.” • “Most profitable code I’ve ever seen.” Thursday, September 30, 2010
  • 20. Genesis • Highly inconsistent (anti-patterns) Thursday, September 30, 2010
  • 21. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code Thursday, September 30, 2010
  • 22. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code • Much copy & pasted code Thursday, September 30, 2010
  • 23. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code • Much copy & pasted code • Cargo cult Thursday, September 30, 2010
  • 25. But How Can I Get Others To Learn Perl Best Practices? Thursday, September 30, 2010
  • 27. Introducing PPI • By Adam Kennedy (a.k.a. alias) Thursday, September 30, 2010
  • 28. Introducing PPI • By Adam Kennedy (a.k.a. alias) • “Parse Perl Independently” Thursday, September 30, 2010
  • 29. Introducing PPI • By Adam Kennedy (a.k.a. alias) • “Parse Perl Independently” • Parse and lex Perl code into DOM Thursday, September 30, 2010
  • 30. Introducing PPI • By Adam Kennedy (a.k.a. alias) • “Parse Perl Independently” • Parse and lex Perl code into DOM • Only perl can parse Perl Thursday, September 30, 2010
  • 31. Introducing PPI • By Adam Kennedy (a.k.a. alias) • “Parse Perl Independently” • Parse and lex Perl code into DOM • Only perl can parse Perl • PPI comes pretty darn close Thursday, September 30, 2010
  • 32. And Perl::Critic Was Born Thursday, September 30, 2010
  • 33. Getting Feet Wet File: Bar.pm 1 package Foo; 2 3 sub showGreeting ($) { 4 my $name = shift; 5 return undef if not $name; 6 open FH, “>out.txt”; 7 print FH ‘Hello $namen’; 8 } Thursday, September 30, 2010
  • 34. Getting Feet Wet USAGE: perlcritic FILENAME [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Package declaration must match filename at line 1, column 1. Correct the filename or package statement. (Severity: 5) Subroutine prototypes used at line 3, column 1. See page 194 of PBP. (Severity: 5) Code before strictures are enabled at line 3, column 1. See page 429 of PBP. (Severity: 5) "return" statement with explicit "undef" at line 5, column 3. See page 199 of PBP. (Severity: 5) Bareword file handle opened at line 6, column 3. See pages 202,204 of PBP. (Severity: 5) Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) Thursday, September 30, 2010
  • 35. A Closer Look Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) Thursday, September 30, 2010
  • 36. A Closer Look Description Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) Thursday, September 30, 2010
  • 37. A Closer Look Description Location Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) Thursday, September 30, 2010
  • 38. A Closer Look Description Location Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) PBP Reference Thursday, September 30, 2010
  • 39. A Closer Look Description Location Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5) PBP Severity Reference Thursday, September 30, 2010
  • 40. Fixing Violations File: Bar.pm 1 package Bar; 2 3 use strict; 4 use warnings; 5 6 sub showGreeting { 7 my $name = shift; 8 return if not $name; 9 open my $fh, ‘>’, “out.txt”; 10 print $fh ‘Hello $name’; 11 } Thursday, September 30, 2010
  • 41. Try Again USAGE: perlcritic FILENAME [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Bar.pm source OK Thursday, September 30, 2010
  • 42. Tightening The Screws USAGE: perlcritic --serverity N FILENAME [jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm Module does not end with "1;" at line 6, column 1. Must end with a recognizable true value. (Severity: 4) Subroutine "showGreeting" does not end with "return" at line 6, column 1. See page 197 of PBP. (Severity: 4) Close filehandles as soon as possible after opening them at line 9, column 1. See page 209 of PBP. (Severity: 4) Thursday, September 30, 2010
  • 43. File: Bar.pm 1 package Bar; 2 3 use strict; 4 use warnings; 5 6 sub showGreeting { 7 my $name = shift; 8 return if not $name; 9 open my $fh, ‘>’, $name; 10 print $fh ‘Hello $name’; 11 close $fh; 12 return 1; 13 } 14 15 1; Thursday, September 30, 2010
  • 44. One More Time USAGE: perlcritic --serverity N FILENAME [jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm Bar.pm source OK Thursday, September 30, 2010
  • 45. USAGE: perlcritic --serverity N FILENAME Thursday, September 30, 2010
  • 46. USAGE: perlcritic --serverity N FILENAME [jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2) Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2) Thursday, September 30, 2010
  • 47. USAGE: perlcritic --serverity N FILENAME [jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2) Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2) [jeff@callahan:/Users/jeff]$ perlcritic --severity 1 Bar.pm RCS keywords $Id$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2) Subroutine "showGreeting" is not all lower case or all upper case at line 6, column 1. See pages 45,46 of PBP. (Severity: 1) Return value of "open" ignored at line 9, column 1. Check the return value of "open" for success. (Severity: 3) Return value of flagged function ignored - open at line 9, column 1. See pages 208,278 of PBP. (Severity: 1) File handle for "print" or "printf" is not braced at line 10, column 1. See page 217 of PBP. (Severity: 1) Return value of flagged function ignored - print at line 10, column 1. See pages 208,278 of PBP. (Severity: 1) String *may* require interpolation at line 10, column 11. See page 51 of PBP. (Severity: 1) Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2) Thursday, September 30, 2010
  • 48. Five Levels Of Severity Thursday, September 30, 2010
  • 49. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice Thursday, September 30, 2010
  • 50. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice • 1 (Lowest): Purely cosmetic or highly controversial Thursday, September 30, 2010
  • 51. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice • 1 (Lowest): Purely cosmetic or highly controversial • Severities are determined by Policy authors Thursday, September 30, 2010
  • 52. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice • 1 (Lowest): Purely cosmetic or highly controversial • Severities are determined by Policy authors • Also have names (brutal, cruel, harsh, stern, gentle) Thursday, September 30, 2010
  • 53. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice • 1 (Lowest): Purely cosmetic or highly controversial • Severities are determined by Policy authors • Also have names (brutal, cruel, harsh, stern, gentle) • Can be configured Thursday, September 30, 2010
  • 54. Configuration perlcritic [-12345 | --brutal | --cruel | --harsh | --stern | --gentle] [--severity number | name] [{-p | --profile} file | --noprofile] [--top [ number ]] [--theme expression] [--include pattern] [--exclude pattern] [{-s | --single-policy} pattern] [--only | --noonly] [--profile-strictness {warn|fatal|quiet}] [--force | --noforce] [--statistics] [--statistics-only] [--count | -C] [--verbose {number | format}] [--color | --nocolor] [--pager pager] [--quiet] [--color-severity-highest color_specification] [--color-severity-high color_specification] [--color-severity-medium color_specification] [--color-severity-low color_specification] [--color-severity-lowest color_specification] [--files-with-violations | -l] [--files-without-violations | -L] {FILE | DIRECTORY | STDIN} perlcritic --profile-proto perlcritic { --list | --list-enabled | --list-themes | --doc pattern [...] } perlcritic { --help | --options | --man | --version } Thursday, September 30, 2010
  • 56. Configuration • .perlcriticrc file Thursday, September 30, 2010
  • 57. Configuration • .perlcriticrc file • In home directory or project directory Thursday, September 30, 2010
  • 58. Configuration • .perlcriticrc file • In home directory or project directory • INI-style syntax Thursday, September 30, 2010
  • 59. Configuration File: ~/.perlcriticrc # Global options here severity = 2 pager = /usr/bin/less color = 1 These will be the defaults, unless overridden at the command-line Thursday, September 30, 2010
  • 60. USAGE: perlcritic FILENAME [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2) No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2) Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2) Default severity is now 2, as defined in .perlcriticrc file Thursday, September 30, 2010
  • 61. Getting More Information USAGE: perlcritic --verbose 8 FILENAME [jeff@callahan:/Users/jeff]$ perlcritic --verbose 8 Bar.pm [Miscellanea::RequireRcsKeywords] RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. (Severity: 2) [Modules::RequireVersionVar] No "$VERSION" variable found at line 1, column 1. (Severity: 2) [InputOutput::RequireCheckedClose] Return value of "close" ignored at line 11, column 1. (Severity: 2) Now showing name of the Policy Thursday, September 30, 2010
  • 62. Configuration File: ~/.perlcriticrc # Global options here severity = 2 verbose = 8 # Policy options here [Modules::RequireVersionVar] severity = 1 # These policies are disabled [-Miscellanea::RequireRcsKeywords] [-InputOutput::RequireCheckedClose] Thursday, September 30, 2010
  • 63. Now running with severity = 2, and verbose = 8 [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Bar.pm source OK Thursday, September 30, 2010
  • 64. Eleven Levels Of Verbosity Thursday, September 30, 2010
  • 65. Eleven Levels Of Verbosity • Increasing levels of detail Thursday, September 30, 2010
  • 66. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” Thursday, September 30, 2010
  • 67. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” • Some of the available details: Thursday, September 30, 2010
  • 68. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” • Some of the available details: • Message, diagnostic (PBP pages), POD extract Thursday, September 30, 2010
  • 69. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” • Some of the available details: • Message, diagnostic (PBP pages), POD extract • Line, column, file name, file path, severity Thursday, September 30, 2010
  • 70. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” • Some of the available details: • Message, diagnostic (PBP pages), POD extract • Line, column, file name, file path, severity • Source code, PPI element, policy name Thursday, September 30, 2010
  • 71. Eleven Levels Of Verbosity • Increasing levels of detail • Can be customized with printf-style formats: “%m near %s at line %l in file %f” • Some of the available details: • Message, diagnostic (PBP pages), POD extract • Line, column, file name, file path, severity • Source code, PPI element, policy name • Integrates with editor’s compile-mode (vim, emacs) Thursday, September 30, 2010
  • 72. Lots of Policies Thursday, September 30, 2010
  • 73. Lots of Policies • 128 Policies in Perl::Critic core Thursday, September 30, 2010
  • 74. Lots of Policies • 128 Policies in Perl::Critic core • 85 Policies taken directly from PBP Thursday, September 30, 2010
  • 75. Lots of Policies • 128 Policies in Perl::Critic core • 85 Policies taken directly from PBP • Dozens of add-on Policies in CPAN Thursday, September 30, 2010
  • 76. Lots of Policies • 128 Policies in Perl::Critic core • 85 Policies taken directly from PBP • Dozens of add-on Policies in CPAN • Covering all aspects of programming Thursday, September 30, 2010
  • 77. Just A Taste Thursday, September 30, 2010
  • 78. Just A Taste • BuiltinFunctions::ProhibitVoidMap Thursday, September 30, 2010
  • 79. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode Thursday, September 30, 2010
  • 80. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames Thursday, September 30, 2010
  • 81. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity Thursday, September 30, 2010
  • 82. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode Thursday, September 30, 2010
  • 83. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode • RegularExpressions::ProhibitFixedStringMatches Thursday, September 30, 2010
  • 84. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode • RegularExpressions::ProhibitFixedStringMatches • Documentation::RequirePodSections Thursday, September 30, 2010
  • 85. Tips For Choosing Policies Thursday, September 30, 2010
  • 86. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. Thursday, September 30, 2010
  • 87. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. • Start with --severity 5 and work your way down. Thursday, September 30, 2010
  • 88. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. • Start with --severity 5 and work your way down. • Use the --doc option to view Policy documentation. Thursday, September 30, 2010
  • 89. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. • Start with --severity 5 and work your way down. • Use the --doc option to view Policy documentation. • Disable or reconfigure Policies that you don’t agree with. Thursday, September 30, 2010
  • 90. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. • Start with --severity 5 and work your way down. • Use the --doc option to view Policy documentation. • Disable or reconfigure Policies that you don’t agree with. • Don’t try to do it all at once. Thursday, September 30, 2010
  • 91. Bending The Rules #!/usr/bin/perl print “Hin” if $^O eq ‘darwin’; print “Hellon” if $^O =~ /win/i; Thursday, September 30, 2010
  • 92. Bending The Rules #!/usr/bin/perl print “Hin” if $^O eq ‘darwin’; print “Hellon” if $^O =~ /win/i; • This would violate ProhibitPostfixControls Thursday, September 30, 2010
  • 93. Bending The Rules #!/usr/bin/perl print “Hin” if $^O eq ‘darwin’; print “Hellon” if $^O =~ /win/i; • This would violate ProhibitPostfixControls • But we really like the way it reads Thursday, September 30, 2010
  • 94. Bending The Rules #!/usr/bin/perl ## no critic print “Hin” if $^O eq ‘darwin’; print “Hellon” if $^O =~ /win/i; ## use critic • ## no critic disables Perl::Critic until end of block or until ## use critic Thursday, September 30, 2010
  • 95. Bending The Rules #!/usr/bin/perl frobulate($this) if $that; ## no critic • When attached to a line of code ## no critic disables Perl::Critic for that line only Thursday, September 30, 2010
  • 96. Bending The Rules #!/usr/bin/perl frobulate($this) if $that; ## no critic (ProhibitPostfixControls) You can also specify exactly which Policies to disable Thursday, September 30, 2010
  • 97. Perl::Critic Applied As command-line tool... $> perlcritic --verbose 8 MyModule.pm As pragma... #!/usr/bin/perl use strict; use warnings; use criticism ‘brutal’; ... Thursday, September 30, 2010
  • 98. Perl::Critic Applied As test library... #!/usr/bin/perl use Test::Perl::Critic; all_critic_ok(); Thursday, September 30, 2010
  • 99. Tips For Working With Legacy Code Thursday, September 30, 2010
  • 100. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. Thursday, September 30, 2010
  • 101. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. • Segregate new code from old code. Thursday, September 30, 2010
  • 102. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. • Segregate new code from old code. • Use “no critic” annotations around blocks of nasty legacy code. Thursday, September 30, 2010
  • 103. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. • Segregate new code from old code. • Use “no critic” annotations around blocks of nasty legacy code. • Don’t mix clean-up commits with regular work. Thursday, September 30, 2010
  • 104. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. • Segregate new code from old code. • Use “no critic” annotations around blocks of nasty legacy code. • Don’t mix clean-up commits with regular work. • Use Test::Perl::Critic::Progressive to chip away at the problem. Thursday, September 30, 2010
  • 106. Test::Perl::Critic::Progressive • TAP-compatible test module. Thursday, September 30, 2010
  • 107. Test::Perl::Critic::Progressive • TAP-compatible test module. • Always passes the first time. Thursday, September 30, 2010
  • 108. Test::Perl::Critic::Progressive • TAP-compatible test module. • Always passes the first time. • Makes a record of violations (in a local file). Thursday, September 30, 2010
  • 109. Test::Perl::Critic::Progressive • TAP-compatible test module. • Always passes the first time. • Makes a record of violations (in a local file). • Subsequent runs will fail if new violations are added. Thursday, September 30, 2010
  • 110. Test::Perl::Critic::Progressive • TAP-compatible test module. • Always passes the first time. • Makes a record of violations (in a local file). • Subsequent runs will fail if new violations are added. • Works best as part of continuous integration system. Thursday, September 30, 2010
  • 112. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. Thursday, September 30, 2010
  • 113. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. • Each Policy extends the Perl::Critic::Policy abstract base class. Thursday, September 30, 2010
  • 114. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. • Each Policy extends the Perl::Critic::Policy abstract base class. • Automatically loaded via Module::Pluggable. Thursday, September 30, 2010
  • 115. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. • Each Policy extends the Perl::Critic::Policy abstract base class. • Automatically loaded via Module::Pluggable. • Sometimes as little as 20 lines of code. Thursday, September 30, 2010
  • 116. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. • Each Policy extends the Perl::Critic::Policy abstract base class. • Automatically loaded via Module::Pluggable. • Sometimes as little as 20 lines of code. • See POD for step-by-step tutorial. Thursday, September 30, 2010
  • 118. Extending Perl::Critic • Policies do not have to be from PBP. Thursday, September 30, 2010
  • 119. Extending Perl::Critic • Policies do not have to be from PBP. • Your Policies can conflict with existing ones. Thursday, September 30, 2010
  • 120. Extending Perl::Critic • Policies do not have to be from PBP. • Your Policies can conflict with existing ones. • Policies can be general or domain-specific. Thursday, September 30, 2010
  • 121. Suppose We Want To Ban Really Complex Boolean Expressions... if ($foo && ($bar || $baz) || !($bar && $baz) || $qux) { ... } Thursday, September 30, 2010
  • 122. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 123. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; Put Policy in use strict; Perl::Critic::Policy namespace use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 124. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 125. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; Policies organized by use strict; PBP table of contents use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 126. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 127. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; Some boilerplate use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 128. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 129. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; Extend the Policy baseclass use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 130. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 131. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; Define default attributes sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 132. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean; use strict; use warnings; use Readonly; use Perl::Critic::Utils ‘:severities’; use base ‘Perl::Critic::Policy’; sub default_severity{ return $SEVERITY_HIGH } sub default_theme{ return qw(complexity) } sub supported_params{ return () } Thursday, September 30, 2010
  • 133. sub default_severity{ return $SEVERITY_HIGH }; sub default_theme{ return qw(complexity) }; sub supported_params{ return () } Readonly::Scalar my $DESC => 'Too many boolean operators'; Readonly::Scalar my $EXPL => 'Complex expressions are hard to read'; Thursday, September 30, 2010
  • 134. Just declaring some variables to store the Description and Explanation sub default_severity{ return $SEVERITY_HIGH }; sub default_theme{ return qw(complexity) }; sub supported_params{ return () } Readonly::Scalar my $DESC => 'Too many boolean operators'; Readonly::Scalar my $EXPL => 'Complex expressions are hard to read'; Thursday, September 30, 2010
  • 135. sub default_severity{ return $SEVERITY_HIGH }; sub default_theme{ return qw(complexity) }; sub supported_params{ return () } Readonly::Scalar my $DESC => 'Too many boolean operators'; Readonly::Scalar my $EXPL => 'Complex expressions are hard to read'; Thursday, September 30, 2010
  • 136. How Does PPI See Your Code? USAGE: ppidump ‘some source code’ USAGE: ppidump STDIN $> ./tools/ppidump 'if($foo && $bar && $baz){}' PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ... ) ( PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... } Thursday, September 30, 2010
  • 137. How Does PPI See Your Code? USAGE: ppidump ‘some source code’ USAGE: ppidump STDIN $> ./tools/ppidump 'if($foo && $bar && $baz){}' PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' This is where we PPI::Structure::Condition ... ) ( want to look for PPI::Statement::Expression PPI::Token::Symbol '$foo' violations PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... } Thursday, September 30, 2010
  • 138. How Does PPI See Your Code? USAGE: ppidump ‘some source code’ USAGE: ppidump STDIN $> ./tools/ppidump 'if($foo && $bar && $baz){}' PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ... ) ( PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... } Thursday, September 30, 2010
  • 139. How Does PPI See Your Code? USAGE: ppidump ‘some source code’ USAGE: ppidump STDIN $> ./tools/ppidump 'if($foo && $bar && $baz){}' PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ... ) ( PPI::Statement::Expression PPI::Token::Symbol '$foo' The offensive code PPI::Token::Operator '&&' is in a child node PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... } Thursday, September 30, 2010
  • 140. How Does PPI See Your Code? USAGE: ppidump ‘some source code’ USAGE: ppidump STDIN $> ./tools/ppidump 'if($foo && $bar && $baz){}' PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ... ) ( PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... } Thursday, September 30, 2010
  • 141. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 142. Declare which types of PPI elements we are interested in sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 143. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 144. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } As document is traversed, we get called each time a PPI::Structure::Conditional is encountered Thursday, September 30, 2010
  • 145. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 146. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! We also get a reference to } the entire document Thursday, September 30, 2010
  • 147. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 148. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Your job is to figure out if this $elem is violating the Policy Thursday, September 30, 2010
  • 149. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  • 150. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 151. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } Search recursively for all Operators sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 152. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 153. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } Count those that are boolean sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 154. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 155. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } Using regex to determine sub _is_boolean { type of operator my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 156. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 157. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } Return a violation if count exceeds maximum sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 158. sub violates { my ( $self, $elem, $doc ) = @_; my $ops = $elem->find(‘PPI::Token::Operator’); my $count = grep { _is_boolean($_) } @{$ops} if ($count > 3) { return $self->violation($DESC, $EXPL, $elem ) } } sub _is_boolean { my ($elem) = @_; return $elem->content() =~ m/^ and | or | not | && | || $/x; } Thursday, September 30, 2010
  • 159. Testing Your Policy Thursday, September 30, 2010
  • 160. Testing Your Policy • Use Test::Perl::Critic::Policy. Thursday, September 30, 2010
  • 161. Testing Your Policy • Use Test::Perl::Critic::Policy. • Specify test cases using POD-like notation. Thursday, September 30, 2010
  • 162. Testing Your Policy • Use Test::Perl::Critic::Policy. • Specify test cases using POD-like notation. • Write Perl as Perl, not strings. Thursday, September 30, 2010
  • 163. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 164. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits Give test a name ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 165. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 166. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 How many violations to expect ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 167. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 168. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut Write offending code if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 169. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 170. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators Code region ends when ## failures 2 next “## name” is found ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 171. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run #---------------------------------------- ## name Within limits ## failures 0 ## cut if ($foo && $bar) {} while ($foo or $bar || $baz) {} #---------------------------------------- ## name Too many operators ## failures 2 ## cut if ($foo && $bar or not $baz and $quxx) {} while ($foo or $bar || $baz && quxx and not $quip) {} Thursday, September 30, 2010
  • 174. Jeffrey Thalhammer S o f t w a r e S y s t e m s jeff@imaginative-software.com Thursday, September 30, 2010