YAPC::NA 2007 - Customizing And Extending Perl Critic
Upcoming SlideShare
Loading in...5
×
 

YAPC::NA 2007 - Customizing And Extending Perl Critic

on

  • 5,305 views

A presentation on developing a custom Policy for the Perl Critic system using a test-driven development approach.

A presentation on developing a custom Policy for the Perl Critic system using a test-driven development approach.

Statistics

Views

Total Views
5,305
Views on SlideShare
5,305
Embed Views
0

Actions

Likes
1
Downloads
71
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    YAPC::NA 2007 - Customizing And Extending Perl Critic YAPC::NA 2007 - Customizing And Extending Perl Critic Presentation Transcript

    • Customizing and Extending Perl Critic YAPC::NA 2007 Houston, TX Josh McAdams
    • Extending Perl Critic Perl Critic is a very powerful and customizable system in its off-the-CPAN state; however, there are times when you need to create your own policies to enforce your own coding standards. We will see how to do that by creating a variant of one of the core modules: BuiltinFunctions::RequireBlockGrep . We'll call our policy BuiltinFunctions::RequireBlockGrepAndMap and make it force block map s in addition to grep s.
    • Extending Perl Critic use strict; use warnings; use Test::More tests => 1; use_ok( 'Perl::Critic::Policy::' . 'BuiltinFunctions::RequireBlockGrepAndMap' ); t/initial-setup.t
    • Extending Perl Critic --(0)> prove -Ilib t/initial-setup.t t/initial-setup.... # Failed test 'use Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap;' # at t/initial-setup.t line 5. # Tried to use 'Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap'. # Error: Can't locate Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm in @INC (...) line 2. # BEGIN failed--compilation aborted at t/initial-setup.t line 5. # Looks like you failed 1 test of 1. t/initial-setup....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/initial-setup.t 1 256 1 1 1 Failed 1/1 test scripts. 1/1 subtests failed. Files=1, Tests=1, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU) Failed 1/1 test programs. 1/1 subtests failed.
    • Extending Perl Critic package Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap ; use warnings; use strict; 1; lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....ok All tests successful.
    • Extending Perl Critic use strict; use warnings; use Test::More tests => 2; use_ok( 'Perl::Critic::Policy::' . 'BuiltinFunctions::RequireBlockGrepAndMap' ); my $policy = Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap ->new(); is($policy->get_severity(), 4, 'high severity set'); t/initial-setup.t
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....Can't locate object method "new" via package "Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap" at t/initial-setup.t line 10. # Looks like you planned 2 tests but only ran 1. # Looks like your test died just after 1. t/initial-setup....dubious Test returned status 255 (wstat 65280, 0xff00) DIED. FAILED test 2 Failed 1/2 tests, 50.00% okay Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/initial-setup.t 255 65280 2 2 2 Failed 1/1 test scripts. 1/2 subtests failed. Files=1, Tests=2, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU) Failed 1/1 test programs. 1/2 subtests failed.
    • Extending Perl Critic package Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap; use warnings; use strict; use base qw(Perl::Critic::Policy); use Perl::Critic::Utils; sub new { my ($class, %args) = @_; return $class->SUPER::new(%args); } sub default_severity { return $SEVERITY_HIGH; } 1; lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....ok All tests successful. Files=1, Tests=2, 0 wallclock secs ( 0.12 cusr + 0.03 csys = 0.15 CPU)
    • Extending Perl Critic Severity Level Variables Exported By Perl::Critic::Utils $SEVERITY_HIGHEST == 5 $SEVERITY_HIGH == 4 $SEVERITY_MEDIUM == 3 $SEVERITY_LOW == 2 $SEVERITY_LOWEST == 1
    • Extending Perl Critic ... is_deeply( [$policy->get_themes()], [qw(almost_core pbp)], 'proper themes set' ); t/initial-setup.t
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....NOK 3 # Failed test 'proper themes set' # at t/initial-setup.t line 16. # Structures begin differing at: # $got->[0] = Does not exist # $expected->[0] = 'almost_core' # Looks like you failed 1 test of 3. t/initial-setup....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 3 Failed 1/3 tests, 66.67% okay Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/initial-setup.t 1 256 3 1 3 Failed 1/1 test scripts. 1/3 subtests failed. Files=1, Tests=3, 0 wallclock secs ( 0.12 cusr + 0.03 csys = 0.15 CPU) Failed 1/1 test programs. 1/3 subtests failed.
    • Extending Perl Critic sub default_themes { return qw( almost_core pbp ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....ok All tests successful. Files=1, Tests=3, 0 wallclock secs ( 0.12 cusr + 0.03 csys = 0.15 CPU)
    • Extending Perl Critic ... is_deeply( [$policy->applies_to()], [qw(PPI::Token::Word)], 'applies only to words' ); t/initial-setup.t
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....NOK 4 # Failed test 'applies only to words' # at t/initial-setup.t line 18. # Structures begin differing at: # $got->[0] = 'PPI::Element' # $expected->[0] = 'PPI::Token::Word' # Looks like you failed 1 test of 4. t/initial-setup....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 4 Failed 1/4 tests, 75.00% okay Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/initial-setup.t 1 256 4 1 4 Failed 1/1 test scripts. 1/4 subtests failed. Files=1, Tests=4, 0 wallclock secs ( 0.12 cusr + 0.03 csys = 0.15 CPU) Failed 1/1 test programs. 1/4 subtests failed.
    • Extending Perl Critic sub applies_to { return 'PPI::Token::Word'; } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/initial-setup....ok All tests successful. Files=1, Tests=4, 0 wallclock secs ( 0.12 cusr + 0.03 csys = 0.15 CPU)
    • Extending Perl Critic PPI::Document PPI::Document::File PPI::Document::Fragment PPI::Document::Normalized PPI::Element PPI::Statement PPI::Statement::Break PPI::Statement::Compound PPI::Statement::Data PPI::Statement::End PPI::Statement::Expression PPI::Statement::Include PPI::Statement::Null PPI::Statement::Package PPI::Statement::Scheduled PPI::Statement::Sub PPI::Statement::Unknown PPI::Statement::UnmatchedBrace PPI::Statement::Variable PPI::Structure PPI::Structure::Block PPI::Structure::Condition PPI::Structure::Constructor PPI::Token::Quote::Single PPI::Token::QuoteLike PPI::Token::QuoteLike::Backtick PPI::Token::QuoteLike::Command PPI::Token::QuoteLike::Readline PPI::Token::QuoteLike::Regexp PPI::Token::QuoteLike::Words PPI::Token::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute PPI::Token::Regexp::Transliterate PPI::Token::Separator PPI::Token::Structure PPI::Token::Symbol PPI::Token::Unknown PPI::Token::Whitespace PPI::Token::Word PPI::Structure::ForLoop PPI::Structure::List PPI::Structure::Subscript PPI::Structure::Unknown PPI::Token PPI::Token::ArrayIndex PPI::Token::Attribute PPI::Token::Cast PPI::Token::Comment PPI::Token::DashedWord PPI::Token::Data PPI::Token::End PPI::Token::HereDoc PPI::Token::Label PPI::Token::Magic PPI::Token::Number PPI::Token::Operator PPI::Token::Pod PPI::Token::Prototype PPI::Token::Quote PPI::Token::Quote::Double PPI::Token::Quote::Interpolate PPI::Token::Quote::Literal PPI Modules
    • Extending Perl Critic use warnings; use strict; use Test::More tests => 1; use Perl::Critic::TestUtils qw(pcritique); my $custom_policy = 'BuiltinFunctions::RequireBlockGrepAndMap'; my $code = 'grep { $_ }'; my $violation_count = pcritique( $custom_policy, $code ); ok(!$violation_count, 'allowed block grep'); t/exercise-policy.t
    • Extending Perl Critic Some Perl::Critic::TestUtils Help Methods - block_perlcriticrc - critique - pcritique - fcritique
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....Can't call abstract method at /opt/local/lib/perl5/site_perl/5.8.7/Perl/Critic/Policy.pm line 98 Perl::Critic::Policy::violates('Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMa...', 'PPI::Token::Word=HASH(0x1a01b40)', 'Perl::Critic::Document=HASH(0x183d338)') called at /opt/local/lib/perl5/site_perl/5.8.7/Perl/Critic.pm line 165 Perl::Critic::_critique('Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMa...', 'Perl::Critic::Document=HASH(0x183d338)', 'HASH(0x19fc194)') called at /opt/local/lib/perl5/site_perl/5.8.7/Perl/Critic.pm line 121 Perl::Critic::critique('Perl::Critic=HASH(0x1800f88)', 'SCALAR(0x1810eac)') called at /opt/local/lib/perl5/site_perl/5.8.7/Perl/Critic/TestUtils.pm line 53 Perl::Critic::TestUtils::pcritique('BuiltinFunctions::RequireBlockGrepAndMap', 'SCALAR(0x1810eac)') called at t/exercise-policy.t line 9 # Looks like your test died before it could output anything. t/exercise-policy....dubious Test returned status 255 (wstat 65280, 0xff00) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 255 65280 1 2 1 Failed 1/2 test scripts. 1/5 subtests failed. Files=2, Tests=5, 1 wallclock secs ( 0.99 cusr + 0.24 csys = 1.23 CPU) Failed 1/2 test programs. 1/5 subtests failed.
    • Extending Perl Critic sub violates {} lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=5, 2 wallclock secs ( 0.98 cusr + 0.22 csys = 1.20 CPU)
    • Extending Perl Critic my $custom_policy = 'BuiltinFunctions::RequireBlockGrepAndMap'; { my $code = 'grep { $_ }'; my $violation_count = pcritique( $custom_policy, $code ); is($violation_count, 0, 'allowed block grep'); } { my $code = 'grep "$_"'; my $violation_count = pcritique( $custom_policy, $code ); is($violation_count, 1, 'disallowed string grep'); } t/exercise-policy.t
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 2 # Failed test 'disallowed string grep' # at t/exercise-policy.t line 17. # got: '0' # expected: '1' # Looks like you failed 1 test of 2. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 2 Failed 1/2 tests, 50.00% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 2 1 2 Failed 1/2 test scripts. 1/6 subtests failed. Files=2, Tests=6, 2 wallclock secs ( 1.00 cusr + 0.22 csys = 1.22 CPU) Failed 1/2 test programs. 1/6 subtests failed.
    • Extending Perl Critic my $description = q{Expression form of "grep" and "map"}; my $explanation = [169]; sub violates { my ( $self, $element, $document ) = @_; return unless $element eq 'grep'; my $sibling = $element->snext_sibling(); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element, ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=6, 1 wallclock secs ( 1.00 cusr + 0.22 csys = 1.22 CPU)
    • Extending Perl Critic Some PPI Utility Methods (from PPI::Element) - parent - next_sibling - snext_sibling - previous_sibling - sprevious_sibling - first_token - last_token - next_token - previous_token
    • Extending Perl Critic use warnings; use strict; use Test::More; use Perl::Critic::TestUtils qw(subtests_in_tree fcritique pcritique); my $custom_policy = 'BuiltinFunctions::RequireBlockGrepAndMap'; my $subtests = subtests_in_tree('t/BuiltinFunctions'); my $test_count = 0; $test_count += @{$subtests->{$_}} for ( keys %{$subtests} ); plan tests => $test_count; ... t/exercise-policy.t
    • Extending Perl Critic for my $policy ( sort keys %$subtests ) { for my $subtest ( @{ $subtests->{$policy} } ) { local $TODO = $subtest->{TODO}; my $desc = join( ' - ', $policy, "line $subtest->{lineno}", $subtest->{name} ); my $violations = $subtest->{filename} ? eval { fcritique( $policy, $subtest->{code}, $subtest->{filename}, $subtest->{parms} ); } : eval { pcritique( $policy, $subtest->{code}, $subtest->{parms} ) }; my $err = $@; if ( $subtest->{error} ) { if ( 'Regexp' eq ref $subtest->{error} ) { like( $err, $subtest->{error}, $desc ); } else { ok( $err, $desc ); } } else { die $err if $err; is( $violations, $subtest->{failures}, $desc ); } } } t/exercise-policy.t
    • Extending Perl Critic ## name allowed block grep ## failures 0 ## cut grep { $_ } ## name disallowed string grep ## failures 1 ## cut grep "$_" t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=6, 2 wallclock secs ( 1.01 cusr + 0.22 csys = 1.23 CPU)
    • Extending Perl Critic ## name A Test Name ## parms { allow_y => 0 } ## TODO Should pass later ## error 1 ## error /Can't load Foo::Bar/ ## filename lib/Foo/Bar.pm ## cut Options For .run Files
    • Extending Perl Critic ... ## name grep as a method call ## failures 0 ## cut $object->grep('this'); t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 3 # Failed test 'BuiltinFunctions::RequireBlockGrepAndMap - line 13 - grep as a method call' # at t/exercise-policy.t line 41. # got: '1' # expected: '0' # Looks like you failed 1 test of 3. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 3 Failed 1/3 tests, 66.67% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 3 1 3 Failed 1/2 test scripts. 1/7 subtests failed. Files=2, Tests=7, 2 wallclock secs ( 1.00 cusr + 0.24 csys = 1.24 CPU) Failed 1/2 test programs. 1/7 subtests failed.
    • Extending Perl Critic use Perl::Critic::Utils; ... sub violates { my ( $self, $element, $document ) = @_; return unless $element eq 'grep'; return if is_method_call($element); my $sibling = $element->snext_sibling(); return if $sibling ->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=7, 2 wallclock secs ( 1.01 cusr + 0.24 csys = 1.25 CPU)
    • Extending Perl Critic Some Useful Methods From Perl::Critic::Utils - is_perl_global - is_perl_builtin - is_hash_key - is_method_call - is_subroutine_name - is_function_call - first_arg - parse_arg_list
    • Extending Perl Critic ... ## name grep as a hash key ## failures 0 ## cut $my_hash{ grep } = 1; t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok 1/4Can't call method "isa" without a package or object reference at lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm line 36. # Looks like you planned 4 tests but only ran 3. # Looks like your test died just after 3. t/exercise-policy....dubious Test returned status 255 (wstat 65280, 0xff00) DIED. FAILED test 4 Failed 1/4 tests, 75.00% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 255 65280 4 2 4 Failed 1/2 test scripts. 1/8 subtests failed. Files=2, Tests=8, 1 wallclock secs ( 1.00 cusr + 0.22 csys = 1.22 CPU) Failed 1/2 test programs. 1/8 subtests failed.
    • Extending Perl Critic sub violates { my ( $self, $element, $document ) = @_; return unless $element eq 'grep'; return if is_method_call($element); return if is_hash_key($element); my $sibling = $element->snext_sibling(); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=8, 2 wallclock secs ( 1.02 cusr + 0.25 csys = 1.27 CPU)
    • Extending Perl Critic ... ## name grep is a sub name ## failures 0 ## cut sub grep { return } t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=9, 2 wallclock secs ( 1.02 cusr + 0.22 csys = 1.24 CPU)
    • Extending Perl Critic ... ## name grep is a sub prototype name ## failures 0 ## cut sub grep; t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 6 # Failed test 'BuiltinFunctions::RequireBlockGrepAndMap - line 31 - grep is a sub prototype name' # at t/exercise-policy.t line 41. # got: '1' # expected: '0' # Looks like you failed 1 test of 6. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 6 Failed 1/6 tests, 83.33% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 6 1 6 Failed 1/2 test scripts. 1/10 subtests failed. Files=2, Tests=10, 1 wallclock secs ( 1.04 cusr + 0.22 csys = 1.26 CPU) Failed 1/2 test programs. 1/10 subtests failed.
    • Extending Perl Critic sub violates { my ( $self, $element, $document ) = @_; return unless $element eq 'grep'; return if is_method_call($element); return if is_hash_key($element); return if is_subroutine_name($element); my $sibling = $element->snext_sibling(); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element, ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=10, 2 wallclock secs ( 1.02 cusr + 0.22 csys = 1.24 CPU)
    • Extending Perl Critic ... ## name grep has parens ## failures 0 ## cut grep ( { $_ } @list ); t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 7 # Failed test 'BuiltinFunctions::RequireBlockGrepAndMap - line 37 - grep has parens' # at t/exercise-policy.t line 41. # got: '1' # expected: '0' # Looks like you failed 1 test of 7. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 7 Failed 1/7 tests, 85.71% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 7 1 7 Failed 1/2 test scripts. 1/11 subtests failed. Files=2, Tests=11, 2 wallclock secs ( 1.03 cusr + 0.22 csys = 1.25 CPU) Failed 1/2 test programs. 1/11 subtests failed.
    • Extending Perl Critic sub violates { my ( $self, $element, $document ) = @_; return unless $element eq 'grep'; return if is_method_call($element); return if is_hash_key($element); return if is_subroutine_name($element); my $sibling = $element->snext_sibling(); $sibling = $sibling->schild(0) if $sibling->isa('PPI::Structure::List'); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element, ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=11, 2 wallclock secs ( 1.04 cusr + 0.24 csys = 1.28 CPU)
    • Extending Perl Critic ... ## name block map ## failures 0 ## cut map { $_ } t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=12, 2 wallclock secs ( 1.04 cusr + 0.22 csys = 1.26 CPU)
    • Extending Perl Critic ... ## name string map ## failures 1 ## cut map "$_" t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 9 # Failed test 'BuiltinFunctions::RequireBlockGrepAndMap - line 49 - string map' # at t/exercise-policy.t line 41. # got: '0' # expected: '1' # Looks like you failed 1 test of 9. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 9 Failed 1/9 tests, 88.89% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 9 1 9 Failed 1/2 test scripts. 1/13 subtests failed. Files=2, Tests=13, 1 wallclock secs ( 1.05 cusr + 0.22 csys = 1.27 CPU) Failed 1/2 test programs. 1/13 subtests failed.
    • Extending Perl Critic sub violates { my ( $self, $element, $document ) = @_; return unless grep { $element eq $_ } qw[grep map]; return if is_method_call($element); return if is_hash_key($element); return if is_subroutine_name($element); my $sibling = $element->snext_sibling(); $sibling = $sibling->schild(0) if $sibling->isa('PPI::Structure::List'); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element, ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=13, 1 wallclock secs ( 1.05 cusr + 0.23 csys = 1.28 CPU)
    • Extending Perl Critic ... ## name string map allowed ## failures 0 ## parms { allow => ['map'] } ## cut map "$_" t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....NOK 10 # Failed test 'BuiltinFunctions::RequireBlockGrepAndMap - line 55 - string map allowed' # at t/exercise-policy.t line 41. # got: '1' # expected: '0' # Looks like you failed 1 test of 10. t/exercise-policy....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 10 Failed 1/10 tests, 90.00% okay t/initial-setup......ok Failed Test Stat Wstat Total Fail List of Failed ------------------------------------------------------------------------------- t/exercise-policy.t 1 256 10 1 10 Failed 1/2 test scripts. 1/14 subtests failed. Files=2, Tests=14, 2 wallclock secs ( 1.05 cusr + 0.22 csys = 1.27 CPU) Failed 1/2 test programs. 1/14 subtests failed.
    • Extending Perl Critic sub new { my ($class, %args) = @_; my $self = $class->SUPER::new(%args); $args{allow} = [] unless exists $args{allow}; for my $function (qw(grep map)) { push @{$self->{filter}}, $function unless grep { $_ eq $function } @{$args{allow}}; } return $self; } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic sub violates { my ( $self, $element, $document ) = @_; return unless grep { $element eq $_ } @{$self->{filter} || []}; return if is_method_call($element); return if is_hash_key($element); return if is_subroutine_name($element); my $sibling = $element->snext_sibling(); $sibling = $sibling->schild(0) if $sibling->isa('PPI::Structure::List'); return if $sibling->isa('PPI::Structure::Block'); return $self->violation( $description, $explanation, $element, ); } lib/Perl/Critic/Policy/BuiltinFunctions/RequireBlockGrepAndMap.pm
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=14, 2 wallclock secs ( 1.06 cusr + 0.24 csys = 1.30 CPU)
    • Extending Perl Critic ... ## name string grep allowed ## failures 0 ## parms { allow => ['grep'] } ## cut grep "$_" ## name string grep and map allowed ## failures 0 ## parms { allow => ['grep', 'map'] } ## cut grep "$_"; map "$_"; ## name string grep and map not allowed ## failures 2 ## cut grep "$_"; map "$_"; t/BuiltinFunctions/RequireBlockGrepAndMap.run
    • Extending Perl Critic --(0)> prove -Ilib t/* t/exercise-policy....ok t/initial-setup......ok All tests successful. Files=2, Tests=17, 2 wallclock secs ( 1.08 cusr + 0.22 csys = 1.30 CPU)
    • Extending Perl Critic That's it for coding our module. We could of course add some POD. =pod =head1 NAME Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrepAndMap =head1 DESCRIPTION The expression form of C<grep> and C<map> is awkward and hard to read. Use the block forms instead. @matches = grep /pattern/, @list; #not ok @matches = grep { /pattern/ } @list; #ok @mapped = map transform($_), @list; #not ok @mapped = map { transform($_) } @list; #ok =cut
    • Extending Perl Critic ... and more tests!
    • Extending Perl Critic - Use Perl::Critic::TestUtils to test your policies - Have your policies subclass Perl::Critic::Policy - Set a default severity for your policies - Set a default tag set for your policies - Configure your policy to apply to specific PPI elements - Create a violates subroutine that does your validation and throws a Perl::Critic::Violation when the policy finds a problem - Use Perl::Critic::Utils to help create your policy - User PPI::Element methods to traverse the PPI DOM In Review