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   26960 use 5.010001;
  40         174  
4 40     40   227 use strict;
  40         95  
  40         814  
5 40     40   210 use warnings;
  40         122  
  40         871  
6 40     40   222 use Readonly;
  40         95  
  40         1945  
7              
8 40     40   266 use Perl::Critic::Utils qw{ :characters :severities };
  40         121  
  40         1988  
9 40     40   11611 use Perl::Critic::Utils::PPI qw{ is_ppi_expression_or_generic_statement };
  40         101  
  40         2312  
10              
11 40     40   267 use parent 'Perl::Critic::Policy';
  40         106  
  40         260  
12              
13             our $VERSION = '1.150';
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 90     90 0 2154 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 74     74 1 292 sub default_severity { return $SEVERITY_LOW }
35 74     74 1 291 sub default_themes { return qw( core maintenance ) }
36 30     30 1 89 sub applies_to { return qw{ PPI::Statement }; }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _max_chain_length {
41 174     174   315 my ( $self ) = @_;
42              
43 174         302 return $self->{_max_chain_length};
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub violates {
49 281     281 1 481 my ( $self, $elem, undef ) = @_;
50              
51 281 100       568 return if not is_ppi_expression_or_generic_statement($elem);
52              
53 174         311 my $chain_length = 0;
54 174         329 my $max_chain_length = $self->_max_chain_length();
55 174         423 my @children = $elem->schildren();
56 174         2181 my $child = shift @children;
57              
58 174         412 while ($child) {
59             # if it looks like we've got a subroutine call, drop the parameter
60             # list.
61 735 100 66     2385 if (
      100        
62             $child->isa('PPI::Token::Word')
63             and @children
64             and $children[0]->isa('PPI::Structure::List')
65             ) {
66 15         85 shift @children;
67             }
68              
69 735 100 100     2666 if (
70             $child->isa('PPI::Token::Word')
71             or $child->isa('PPI::Token::Symbol')
72             ) {
73 290 100       536 if ( @children ) {
74 280 100       1209 if ( $children[0]->isa('PPI::Token::Operator') ) {
    100          
75 134 50       327 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 131         219 $chain_length = 0;
82             }
83             }
84             }
85             else {
86 445 50       858 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 445         576 $chain_length = 0;
96             }
97              
98 735         1913 $child = shift @children;
99             }
100              
101 174 50       360 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 174         453 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 :