File Coverage

blib/lib/Perl/Critic/Policy/ValuesAndExpressions/ProhibitLongChainsOfMethodCalls.pm
Criterion Covered Total %
statement 48 49 97.9
branch 17 18 94.4
condition 9 9 100.0
subroutine 13 13 100.0
pod 4 5 80.0
total 91 94 96.8


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls;
2              
3 40     40   26973 use 5.010001;
  40         165  
4 40     40   265 use strict;
  40         88  
  40         866  
5 40     40   215 use warnings;
  40         146  
  40         913  
6 40     40   206 use Readonly;
  40         104  
  40         2003  
7              
8 40     40   271 use Perl::Critic::Utils qw{ :characters :severities };
  40         84  
  40         2045  
9 40     40   11877 use Perl::Critic::Utils::PPI qw{ is_ppi_expression_or_generic_statement };
  40         121  
  40         2458  
10              
11 40     40   290 use parent 'Perl::Critic::Policy';
  40         119  
  40         251  
12              
13             our $VERSION = '1.148';
14              
15             #-----------------------------------------------------------------------------
16              
17             Readonly::Scalar my $EXPL =>
18             q{Long chains of method calls indicate code that is too tightly coupled};
19              
20             #-----------------------------------------------------------------------------
21              
22             sub supported_parameters {
23             return (
24             {
25 96     96 0 2115 name => 'max_chain_length',
26             description => 'The number of chained calls to allow.',
27             default_string => '3',
28             behavior => 'integer',
29             integer_minimum => 1,
30             },
31             );
32             }
33              
34 82     82 1 409 sub default_severity { return $SEVERITY_LOW }
35 74     74 1 445 sub default_themes { return qw( core maintenance ) }
36 36     36 1 137 sub applies_to { return qw{ PPI::Statement }; }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _max_chain_length {
41 254     254   495 my ( $self ) = @_;
42              
43 254         601 return $self->{_max_chain_length};
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub violates {
49 361     361 1 808 my ( $self, $elem, undef ) = @_;
50              
51 361 100       906 return if not is_ppi_expression_or_generic_statement($elem);
52              
53 254         556 my $chain_length = 0;
54 254         576 my $max_chain_length = $self->_max_chain_length();
55 254         680 my @children = $elem->schildren();
56 254         3883 my $child = shift @children;
57              
58 254         783 while ($child) {
59             # if it looks like we've got a subroutine call, drop the parameter
60             # list.
61 998 100 100     3955 if (
      100        
62             $child->isa('PPI::Token::Word')
63             and @children
64             and $children[0]->isa('PPI::Structure::List')
65             ) {
66 67         475 shift @children;
67             }
68              
69 998 100 100     4335 if (
70             $child->isa('PPI::Token::Word')
71             or $child->isa('PPI::Token::Symbol')
72             ) {
73 456 100       1354 if ( @children ) {
74 404 100       1343 if ( $children[0]->isa('PPI::Token::Operator') ) {
    100          
75 231 100       677 if ( q{->} eq $children[0]->content() ) {
76 76         343 $chain_length++;
77 76         131 shift @children;
78             }
79             }
80             elsif ( not $children[0]->isa('PPI::Token::Structure') ) {
81 133         288 $chain_length = 0;
82             }
83             }
84             }
85             else {
86 542 100       1330 if ($chain_length > $max_chain_length) {
87             return
88 8         61 $self->violation(
89             "Found method-call chain of length $chain_length.",
90             $EXPL,
91             $elem,
92             );
93             }
94              
95 534         879 $chain_length = 0;
96             }
97              
98 990         3156 $child = shift @children;
99             }
100              
101 246 50       633 if ($chain_length > $max_chain_length) {
102             return
103 0         0 $self->violation(
104             "Found method-call chain of length $chain_length.",
105             $EXPL,
106             $elem,
107             );
108             }
109              
110 246         757 return;
111             }
112              
113              
114             1;
115              
116             __END__
117              
118             #-----------------------------------------------------------------------------
119              
120             =pod
121              
122             =for stopwords MSCHWERN
123              
124             =head1 NAME
125              
126             Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls - Long chains of method calls indicate tightly coupled code.
127              
128              
129             =head1 AFFILIATION
130              
131             This Policy is part of the core L<Perl::Critic|Perl::Critic>
132             distribution.
133              
134              
135             =head1 DESCRIPTION
136              
137             A long chain of method calls usually indicates that the code knows too
138             much about the interrelationships between objects. If the code is
139             able to directly navigate far down a network of objects, then when the
140             network changes structure in the future, the code will need to be
141             modified to deal with the change. The code is too tightly coupled and
142             is brittle.
143              
144              
145             $x = $y->a; #ok
146             $x = $y->a->b; #ok
147             $x = $y->a->b->c; #questionable, but allowed by default
148             $x = $y->a->b->c->d; #not ok
149              
150              
151             =head1 CONFIGURATION
152              
153             This policy has one option: C<max_chain_length> which controls how far
154             the code is allowed to navigate. The default value is 3.
155              
156              
157             =head1 TO DO
158              
159             Add a C<class_method_exemptions> option to allow for things like
160              
161             File::Find::Rule
162             ->name('*.blah')
163             ->not_name('thingy')
164             ->readable()
165             ->directory()
166             ->in(@roots);
167              
168              
169             =head1 AUTHOR
170              
171             Elliot Shank C<< <perl@galumph.com> >>
172              
173              
174             =head1 COPYRIGHT
175              
176             Copyright (c) 2007-2011 Elliot Shank.
177              
178             This program is free software; you can redistribute it and/or modify
179             it under the same terms as Perl itself. The full text of this license
180             can be found in the LICENSE file included with this module.
181              
182             =cut
183              
184             # Local Variables:
185             # mode: cperl
186             # cperl-indent-level: 4
187             # fill-column: 78
188             # indent-tabs-mode: nil
189             # c-indentation-style: bsd
190             # End:
191             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :