File Coverage

blib/lib/Perl/Critic/Policy/Moose/ProhibitMultipleWiths.pm
Criterion Covered Total %
statement 47 48 97.9
branch 13 16 81.2
condition n/a
subroutine 13 14 92.8
pod 5 6 83.3
total 78 84 92.8


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Moose::ProhibitMultipleWiths;
2              
3 1     1   1014 use strict;
  1         2  
  1         34  
4 1     1   6 use warnings;
  1         2  
  1         48  
5              
6             our $VERSION = '1.04';
7              
8 1     1   14 use Readonly ();
  1         2  
  1         31  
9              
10 1     1   7 use Perl::Critic::Utils qw< :booleans :severities $EMPTY >;
  1         2  
  1         74  
11 1     1   225 use Perl::Critic::Utils::PPI qw< is_ppi_generic_statement >;
  1         2  
  1         72  
12              
13 1     1   6 use base 'Perl::Critic::Policy';
  1         1  
  1         682  
14              
15             Readonly::Scalar my $DESCRIPTION => 'Multiple calls to with() were made.';
16             Readonly::Scalar my $EXPLANATION =>
17             q<Roles cannot protect against name conflicts if they are not composed.>;
18              
19             sub supported_parameters {
20             return (
21             {
22 13     13 0 48495 name => 'equivalent_modules',
23             description =>
24             q<The additional modules to treat as equivalent to "Moose", "Moose::Role", or "MooseX::Role::Parameterized".>,
25             default_string => 'Moose Moose::Role MooseX::Role::Parameterized',
26             behavior => 'string list',
27             list_always_present_values =>
28             [qw< Moose Moose::Role MooseX::Role::Parameterized >],
29             },
30             );
31             }
32              
33 5     5 1 57 sub default_severity { return $SEVERITY_HIGH; }
34 0     0 1 0 sub default_themes { return qw( bugs moose roles ); }
35 12     12 1 539 sub applies_to { return 'PPI::Document' }
36              
37             sub prepare_to_scan_document {
38 13     13 1 90218 my ( $self, $document ) = @_;
39              
40 13         41 return $self->_is_interesting_document($document);
41             }
42              
43             sub _is_interesting_document {
44 28     28   53 my ( $self, $document ) = @_;
45              
46 28         36 foreach my $module ( keys %{ $self->{_equivalent_modules} } ) {
  28         111  
47 64 100       8518 return $TRUE if $document->uses_module($module);
48             }
49              
50 2         24 return $FALSE;
51             }
52              
53             sub violates {
54 12     12 1 117 my ( $self, undef, $document ) = @_;
55              
56 12         22 my @violations;
57 12         53 foreach my $namespace ( $document->namespaces() ) {
58             SUBDOCUMENT:
59 15         21828 foreach my $subdocument (
60             $document->subdocuments_for_namespace($namespace) ) {
61             next SUBDOCUMENT
62 15 100       187 if not $self->_is_interesting_document($subdocument);
63              
64 14         3448 my $with_statements = $subdocument->find( \&_is_with_statement );
65              
66 14 100       345 next SUBDOCUMENT if not $with_statements;
67 13 100       19 next SUBDOCUMENT if @{$with_statements} < 2;
  13         61  
68              
69 5         11 my $second_with = $with_statements->[1];
70 5         26 push
71             @violations,
72             $self->violation( $DESCRIPTION, $EXPLANATION, $second_with );
73             }
74             }
75              
76 12         717 return @violations;
77             }
78              
79             sub _is_with_statement {
80 305     305   5830 my ( undef, $element ) = @_;
81              
82 305 100       780 return $FALSE if not is_ppi_generic_statement($element);
83              
84 18         205 my $current_token = $element->schild(0);
85 18 50       262 return $FALSE if not $current_token;
86 18 50       72 return $FALSE if not $current_token->isa('PPI::Token::Word');
87 18 50       49 return $FALSE if $current_token->content() ne 'with';
88              
89 18         118 return $TRUE;
90             }
91              
92             1;
93              
94             # ABSTRACT: Require role composition
95              
96             __END__
97              
98             =pod
99              
100             =head1 NAME
101              
102             Perl::Critic::Policy::Moose::ProhibitMultipleWiths - Require role composition
103              
104             =head1 VERSION
105              
106             version 1.04
107              
108             =head1 DESCRIPTION
109              
110             L<Moose::Role>s are, among other things, the answer to name conflicts plaguing
111             multiple inheritance and mix-ins. However, to enjoy this protection, you must
112             compose your roles together. Roles do not generate conflicts if they are
113             consumed individually.
114              
115             Pass all of your roles to a single L<with|Moose/with> statement.
116              
117             # ok
118             package Foo;
119              
120             use Moose::Role;
121              
122             with qw< Bar Baz >;
123              
124             # not ok
125             package Foo;
126              
127             use Moose::Role;
128              
129             with 'Bar';
130             with 'Baz';
131              
132             =head1 AFFILIATION
133              
134             This policy is part of L<Perl::Critic::Moose>.
135              
136             =head1 CONFIGURATION
137              
138             There is a single option, C<equivalent_modules>. This allows you to specify
139             modules that should be treated the same as L<Moose> and L<Moose::Role>, if,
140             say, you were doing something with L<Moose::Exporter>. For example, if you
141             were to have this in your F<.perlcriticrc> file:
142              
143             [Moose::ProhibitMultipleWiths]
144             equivalent_modules = MyCompany::Moose MooseX::NewThing
145              
146             then the following code would result in a violation:
147              
148             package Baz;
149              
150             use MyCompany::Moose;
151              
152             with 'Bing';
153             with 'Bong';
154              
155             =head1 AUTHORS
156              
157             =over 4
158              
159             =item *
160              
161             Elliot Shank <perl@galumph.com>
162              
163             =item *
164              
165             Dave Rolsky <autarch@urth.org>
166              
167             =back
168              
169             =head1 COPYRIGHT AND LICENSE
170              
171             This software is copyright (c) 2008 - 2015 by Elliot Shank.
172              
173             This is free software; you can redistribute it and/or modify it under
174             the same terms as the Perl 5 programming language system itself.
175              
176             =cut