File Coverage

blib/lib/Perl/Critic/Policy/ValuesAndExpressions/ProhibitLongChainsOfMethodCalls.pm
Criterion Covered Total %
statement 45 49 91.8
branch 15 18 83.3
condition 8 9 88.8
subroutine 13 13 100.0
pod 4 5 80.0
total 85 94 90.4


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls;
2              
3 40     40   25407 use 5.010001;
  40         136  
4 40     40   181 use strict;
  40         72  
  40         706  
5 40     40   126 use warnings;
  40         63  
  40         1395  
6 40     40   156 use Readonly;
  40         71  
  40         2198  
7              
8 40     40   202 use Perl::Critic::Utils qw{ :characters :severities };
  40         70  
  40         1986  
9 40     40   9274 use Perl::Critic::Utils::PPI qw{ is_ppi_expression_or_generic_statement };
  40         71  
  40         1720  
10              
11 40     40   173 use parent 'Perl::Critic::Policy';
  40         71  
  40         203  
12              
13             our $VERSION = '1.156';
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 91     91 0 1083 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 75     75 1 222 sub default_severity { return $SEVERITY_LOW }
35 74     74 1 210 sub default_themes { return qw( core maintenance ) }
36 31     31 1 88 sub applies_to { return qw{ PPI::Statement }; }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _max_chain_length {
41 176     176   251 my ( $self ) = @_;
42              
43 176         292 return $self->{_max_chain_length};
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub violates {
49 283     283 1 427 my ( $self, $elem, undef ) = @_;
50              
51 283 100       499 return if not is_ppi_expression_or_generic_statement($elem);
52              
53 176         226 my $chain_length = 0;
54 176         315 my $max_chain_length = $self->_max_chain_length();
55 176         386 my @children = $elem->schildren();
56 176         2045 my $child = shift @children;
57              
58 176         389 while ($child) {
59             # if it looks like we've got a subroutine call, drop the parameter
60             # list.
61 743 100 66     2084 if (
      100        
62             $child->isa('PPI::Token::Word')
63             and @children
64             and $children[0]->isa('PPI::Structure::List')
65             ) {
66 16         73 shift @children;
67             }
68              
69 743 100 100     2246 if (
70             $child->isa('PPI::Token::Word')
71             or $child->isa('PPI::Token::Symbol')
72             ) {
73 294 100       489 if ( @children ) {
74 284 100       819 if ( $children[0]->isa('PPI::Token::Operator') ) {
    100          
75 134 50       265 if ( q{->} eq $children[0]->content() ) {
76 0         0 $chain_length++;
77 0         0 shift @children;
78             }
79             }
80             elsif ( not $children[0]->isa('PPI::Token::Structure') ) {
81 133         164 $chain_length = 0;
82             }
83             }
84             }
85             else {
86 449 50       672 if ($chain_length > $max_chain_length) {
87             return
88 0         0 $self->violation(
89             "Found method-call chain of length $chain_length.",
90             $EXPL,
91             $elem,
92             );
93             }
94              
95 449         507 $chain_length = 0;
96             }
97              
98 743         1745 $child = shift @children;
99             }
100              
101 176 50       291 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 176         398 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 :