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   27719 use 5.010001;
  40         178  
4 40     40   245 use strict;
  40         118  
  40         833  
5 40     40   222 use warnings;
  40         101  
  40         1039  
6 40     40   236 use Readonly;
  40         113  
  40         2057  
7              
8 40     40   282 use Perl::Critic::Utils qw{ :characters :severities };
  40         92  
  40         2014  
9 40     40   12045 use Perl::Critic::Utils::PPI qw{ is_ppi_expression_or_generic_statement };
  40         108  
  40         2354  
10              
11 40     40   298 use parent 'Perl::Critic::Policy';
  40         139  
  40         246  
12              
13             our $VERSION = '1.146';
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 2118 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 357 sub default_severity { return $SEVERITY_LOW }
35 74     74 1 339 sub default_themes { return qw( core maintenance ) }
36 36     36 1 158 sub applies_to { return qw{ PPI::Statement }; }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _max_chain_length {
41 254     254   495 my ( $self ) = @_;
42              
43 254         593 return $self->{_max_chain_length};
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub violates {
49 361     361 1 788 my ( $self, $elem, undef ) = @_;
50              
51 361 100       908 return if not is_ppi_expression_or_generic_statement($elem);
52              
53 254         505 my $chain_length = 0;
54 254         608 my $max_chain_length = $self->_max_chain_length();
55 254         688 my @children = $elem->schildren();
56 254         3755 my $child = shift @children;
57              
58 254         759 while ($child) {
59             # if it looks like we've got a subroutine call, drop the parameter
60             # list.
61 998 100 100     3803 if (
      100        
62             $child->isa('PPI::Token::Word')
63             and @children
64             and $children[0]->isa('PPI::Structure::List')
65             ) {
66 67         510 shift @children;
67             }
68              
69 998 100 100     4306 if (
70             $child->isa('PPI::Token::Word')
71             or $child->isa('PPI::Token::Symbol')
72             ) {
73 456 100       1015 if ( @children ) {
74 404 100       1440 if ( $children[0]->isa('PPI::Token::Operator') ) {
    100          
75 231 100       641 if ( q{->} eq $children[0]->content() ) {
76 76         348 $chain_length++;
77 76         133 shift @children;
78             }
79             }
80             elsif ( not $children[0]->isa('PPI::Token::Structure') ) {
81 133         271 $chain_length = 0;
82             }
83             }
84             }
85             else {
86 542 100       1232 if ($chain_length > $max_chain_length) {
87             return
88 8         57 $self->violation(
89             "Found method-call chain of length $chain_length.",
90             $EXPL,
91             $elem,
92             );
93             }
94              
95 534         871 $chain_length = 0;
96             }
97              
98 990         3022 $child = shift @children;
99             }
100              
101 246 50       593 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         798 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 :