File Coverage

blib/lib/Perl/Critic/Policy/Moose/ProhibitMultipleWiths.pm
Criterion Covered Total %
statement 50 51 98.0
branch 13 16 81.2
condition n/a
subroutine 14 15 93.3
pod 5 6 83.3
total 82 88 93.1


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