Perl Critic In Depth

3,547
-1

Published on

1 Comment
7 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,547
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
53
Comments
1
Likes
7
Embeds 0
No embeds

No notes for slide

Perl Critic In Depth

  1. 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. 2. Some Ways Are Better Than Others San Francisco Perl Mongers July 27, 2010 Thursday, September 30, 2010
  3. 3. HaveYou Ever?... Thursday, September 30, 2010
  4. 4. print ‘Hello, my name is $namen’; Thursday, September 30, 2010
  5. 5. eval ‘reqire Some::Module’; Thursday, September 30, 2010
  6. 6. if(not $foo && $bar or $baz){ ... } Thursday, September 30, 2010
  7. 7. if($this == ‘that’){ ... } Thursday, September 30, 2010
  8. 8. What Is Perl::Critic? Thursday, September 30, 2010
  9. 9. What Is Perl::Critic? • Static Source Code Analyzer Thursday, September 30, 2010
  10. 10. What Is Perl::Critic? • Static Source Code Analyzer • Like “lint” but for Perl code Thursday, September 30, 2010
  11. 11. What Is Perl::Critic? • Static Source Code Analyzer • Like “lint” but for Perl code • Finds bugs & enforces style Thursday, September 30, 2010
  12. 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
  13. 13. Genesis Thursday, September 30, 2010
  14. 14. Genesis • Large legacy code base (~500k lines) Thursday, September 30, 2010
  15. 15. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years Thursday, September 30, 2010
  16. 16. Genesis • Large legacy code base (~500k lines) • Evolved organically over 10 years • Many developers with various skill levels Thursday, September 30, 2010
  17. 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. 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
  19. 19. Genesis Thursday, September 30, 2010
  20. 20. Genesis • Highly inconsistent (anti-patterns) Thursday, September 30, 2010
  21. 21. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code Thursday, September 30, 2010
  22. 22. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code • Much copy & pasted code Thursday, September 30, 2010
  23. 23. Genesis • Highly inconsistent (anti-patterns) • Extremely complicated code • Much copy & pasted code • Cargo cult Thursday, September 30, 2010
  24. 24. Behold! Thursday, September 30, 2010
  25. 25. But How Can I Get Others To Learn Perl Best Practices? Thursday, September 30, 2010
  26. 26. Introducing PPI Thursday, September 30, 2010
  27. 27. Introducing PPI • By Adam Kennedy (a.k.a. alias) Thursday, September 30, 2010
  28. 28. Introducing PPI • By Adam Kennedy (a.k.a. alias) • “Parse Perl Independently” Thursday, September 30, 2010
  29. 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. 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. 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. 32. And Perl::Critic Was Born Thursday, September 30, 2010
  33. 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. 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. 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. 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. 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. 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. 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. 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. 41. Try Again USAGE: perlcritic FILENAME [jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Bar.pm source OK Thursday, September 30, 2010
  42. 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. 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. 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. 45. USAGE: perlcritic --serverity N FILENAME Thursday, September 30, 2010
  46. 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. 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. 48. Five Levels Of Severity Thursday, September 30, 2010
  49. 49. Five Levels Of Severity • 5 (Highest): Likely bug or widely accepted practice Thursday, September 30, 2010
  50. 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. 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. 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. 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. 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
  55. 55. Configuration Thursday, September 30, 2010
  56. 56. Configuration • .perlcriticrc file Thursday, September 30, 2010
  57. 57. Configuration • .perlcriticrc file • In home directory or project directory Thursday, September 30, 2010
  58. 58. Configuration • .perlcriticrc file • In home directory or project directory • INI-style syntax Thursday, September 30, 2010
  59. 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. 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. 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. 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. 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. 64. Eleven Levels Of Verbosity Thursday, September 30, 2010
  65. 65. Eleven Levels Of Verbosity • Increasing levels of detail Thursday, September 30, 2010
  66. 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. 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. 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. 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. 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. 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. 72. Lots of Policies Thursday, September 30, 2010
  73. 73. Lots of Policies • 128 Policies in Perl::Critic core Thursday, September 30, 2010
  74. 74. Lots of Policies • 128 Policies in Perl::Critic core • 85 Policies taken directly from PBP Thursday, September 30, 2010
  75. 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. 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. 77. Just A Taste Thursday, September 30, 2010
  78. 78. Just A Taste • BuiltinFunctions::ProhibitVoidMap Thursday, September 30, 2010
  79. 79. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode Thursday, September 30, 2010
  80. 80. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames Thursday, September 30, 2010
  81. 81. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity Thursday, September 30, 2010
  82. 82. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode Thursday, September 30, 2010
  83. 83. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode • RegularExpressions::ProhibitFixedStringMatches Thursday, September 30, 2010
  84. 84. Just A Taste • BuiltinFunctions::ProhibitVoidMap • ControlStructures::ProhibitUnreachableCode • NamingConventions::ProhibitAmbiguousNames • Subroutines::ProhibitExcessComplexity • CodeLayout::RequireTidyCode • RegularExpressions::ProhibitFixedStringMatches • Documentation::RequirePodSections Thursday, September 30, 2010
  85. 85. Tips For Choosing Policies Thursday, September 30, 2010
  86. 86. Tips For Choosing Policies • Use --profile-proto option to generate your first .perlcriticrc file. Thursday, September 30, 2010
  87. 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. 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. 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. 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. 91. Bending The Rules #!/usr/bin/perl print “Hin” if $^O eq ‘darwin’; print “Hellon” if $^O =~ /win/i; Thursday, September 30, 2010
  92. 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. 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. 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. 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. 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. 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. 98. Perl::Critic Applied As test library... #!/usr/bin/perl use Test::Perl::Critic; all_critic_ok(); Thursday, September 30, 2010
  99. 99. Tips For Working With Legacy Code Thursday, September 30, 2010
  100. 100. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. Thursday, September 30, 2010
  101. 101. Tips For Working With Legacy Code • Use --statistics option to identify hotspots. • Segregate new code from old code. Thursday, September 30, 2010
  102. 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. 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. 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
  105. 105. Test::Perl::Critic::Progressive Thursday, September 30, 2010
  106. 106. Test::Perl::Critic::Progressive • TAP-compatible test module. Thursday, September 30, 2010
  107. 107. Test::Perl::Critic::Progressive • TAP-compatible test module. • Always passes the first time. Thursday, September 30, 2010
  108. 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. 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. 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
  111. 111. Extending Perl::Critic Thursday, September 30, 2010
  112. 112. Extending Perl::Critic • Each Policy is a module in the Perl::Critic::Policy namespace. Thursday, September 30, 2010
  113. 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. 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. 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. 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
  117. 117. Extending Perl::Critic Thursday, September 30, 2010
  118. 118. Extending Perl::Critic • Policies do not have to be from PBP. Thursday, September 30, 2010
  119. 119. Extending Perl::Critic • Policies do not have to be from PBP. • Your Policies can conflict with existing ones. Thursday, September 30, 2010
  120. 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. 121. Suppose We Want To Ban Really Complex Boolean Expressions... if ($foo && ($bar || $baz) || !($bar && $baz) || $qux) { ... } Thursday, September 30, 2010
  122. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 141. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  142. 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. 143. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  144. 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. 145. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  146. 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. 147. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  148. 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. 149. sub applies_to { return ‘PPI::Structure::Condition’ } sub violates { my ( $self, $elem, $doc ) = @_; # meat goes here! } Thursday, September 30, 2010
  150. 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. 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. 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. 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. 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. 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. 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. 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. 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. 159. Testing Your Policy Thursday, September 30, 2010
  160. 160. Testing Your Policy • Use Test::Perl::Critic::Policy. Thursday, September 30, 2010
  161. 161. Testing Your Policy • Use Test::Perl::Critic::Policy. • Specify test cases using POD-like notation. Thursday, September 30, 2010
  162. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
  172. 172. Questions? Thursday, September 30, 2010
  173. 173. Thank You! Thursday, September 30, 2010
  174. 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

×