File Coverage

blib/lib/Perl/Critic/Theme.pm
Criterion Covered Total %
statement 61 61 100.0
branch 10 12 83.3
condition 3 6 50.0
subroutine 14 14 100.0
pod 4 4 100.0
total 92 97 94.8


line stmt bran cond sub pod time code
1             package Perl::Critic::Theme;
2              
3 40     40   948 use 5.010001;
  40         172  
4 40     40   267 use strict;
  40         104  
  40         967  
5 40     40   239 use warnings;
  40         112  
  40         1388  
6 40     40   273 use English qw(-no_match_vars);
  40         118  
  40         339  
7 40     40   15219 use Readonly;
  40         103  
  40         2029  
8              
9 40     40   280 use Exporter 'import';
  40         109  
  40         1330  
10              
11 40     40   261 use Perl::Critic::Utils qw{ :characters :data_conversion };
  40         140  
  40         2246  
12 40     40   11310 use Perl::Critic::Exception::Fatal::Internal qw{ &throw_internal };
  40         116  
  40         4151  
13             use Perl::Critic::Exception::Configuration::Option::Global::ParameterValue
14 40     40   337 qw{ &throw_global_value };
  40         120  
  40         1284  
15              
16             #-----------------------------------------------------------------------------
17              
18             our $VERSION = '1.146';
19              
20             #-----------------------------------------------------------------------------
21              
22             Readonly::Array our @EXPORT_OK => qw{
23             $RULE_INVALID_CHARACTER_REGEX
24             cook_rule
25             };
26              
27             #-----------------------------------------------------------------------------
28              
29             Readonly::Scalar our $RULE_INVALID_CHARACTER_REGEX =>
30             qr/ ( [^()\s\w\d+\-*&|!] ) /xms;
31              
32             #-----------------------------------------------------------------------------
33              
34             Readonly::Scalar my $CONFIG_KEY => 'theme';
35              
36             #-----------------------------------------------------------------------------
37              
38             sub new {
39              
40 2962     2962 1 34422 my ( $class, %args ) = @_;
41 2962         7883 my $self = bless {}, $class;
42 2962         13831 $self->_init( %args );
43 2954         15787 return $self;
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub _init {
49              
50 2962     2962   7463 my ($self, %args) = @_;
51 2962   66     14387 my $rule = $args{-rule} || $EMPTY;
52              
53 2962 100       15193 if ( $rule =~ m/$RULE_INVALID_CHARACTER_REGEX/xms ) {
54 8         51 throw_global_value
55             option_name => $CONFIG_KEY,
56             option_value => $rule,
57             message_suffix => qq{contains an invalid character: "$1".};
58             }
59              
60 2954         7941 $self->{_rule} = cook_rule( $rule );
61              
62 2954         7399 return $self;
63             }
64              
65             #-----------------------------------------------------------------------------
66              
67             sub rule {
68 5     5 1 14 my $self = shift;
69 5         29 return $self->{_rule};
70             }
71              
72             #-----------------------------------------------------------------------------
73              
74             sub policy_is_thematic {
75              
76 13340     13340 1 38516 my ($self, %args) = @_;
77             my $policy = $args{-policy}
78 13340   33     54938 || throw_internal 'The -policy argument is required';
79 13340 50       336231 ref $policy
80             || throw_internal 'The -policy must be an object';
81              
82 13340 100       45508 my $rule = $self->{_rule} or return 1;
83 10006         41217 my %themes = hashify( $policy->get_themes() );
84              
85             # This bit of magic turns the rule into a perl expression that can be
86             # eval-ed for truth. Each theme name in the rule is translated to 1 or 0
87             # if the $policy belongs in that theme. For example:
88             #
89             # 'bugs && (pbp || core)' ...could become... '1 && (0 || 1)'
90              
91 10006         20419 my $as_code = $rule; #Making a copy, so $rule is preserved
92 10006 100       47669 $as_code =~ s/ ( [\w\d]+ ) /exists $themes{$1} || 0/gexms;
  12616         54924  
93 10006         457305 my $is_thematic = eval $as_code; ## no critic (ProhibitStringyEval)
94              
95 10006 100       38953 if ($EVAL_ERROR) {
96 1         13 throw_global_value
97             option_name => $CONFIG_KEY,
98             option_value => $rule,
99             message_suffix => q{contains a syntax error.};
100             }
101              
102 10005         48471 return $is_thematic;
103             }
104              
105             #-----------------------------------------------------------------------------
106              
107             sub cook_rule {
108 5891     5891 1 19983 my ($raw_rule) = @_;
109 5891 50       13140 return if not defined $raw_rule;
110              
111             #Translate logical operators
112 5891         10626 $raw_rule =~ s{\b not \b}{!}ixmsg; # "not" -> "!"
113 5891         9642 $raw_rule =~ s{\b and \b}{&&}ixmsg; # "and" -> "&&"
114 5891         9246 $raw_rule =~ s{\b or \b}{||}ixmsg; # "or" -> "||"
115              
116             #Translate algebra operators (for backward compatibility)
117 5891         8285 $raw_rule =~ s{\A [-] }{!}ixmsg; # "-" -> "!" e.g. difference
118 5891         7959 $raw_rule =~ s{ [-] }{&& !}ixmsg; # "-" -> "&& !" e.g. difference
119 5891         8079 $raw_rule =~ s{ [*] }{&&}ixmsg; # "*" -> "&&" e.g. intersection
120 5891         8278 $raw_rule =~ s{ [+] }{||}ixmsg; # "+" -> "||" e.g. union
121              
122 5891         10730 my $cooked_rule = lc $raw_rule; #Is now cooked!
123 5891         16432 return $cooked_rule;
124             }
125              
126              
127             1;
128              
129             __END__
130              
131             #-----------------------------------------------------------------------------
132              
133             =pod
134              
135             =head1 NAME
136              
137             Perl::Critic::Theme - Construct thematic sets of policies.
138              
139              
140             =head1 DESCRIPTION
141              
142             This is a helper class for evaluating theme expressions into sets of
143             Policy objects. There are no user-serviceable parts here.
144              
145              
146             =head1 INTERFACE SUPPORT
147              
148             This is considered to be a non-public class. Its interface is subject
149             to change without notice.
150              
151              
152             =head1 METHODS
153              
154             =over
155              
156             =item C<< new( -rule => $rule_expression ) >>
157              
158             Returns a reference to a new Perl::Critic::Theme object. C<-rule> is
159             a string expression that evaluates to true or false for each Policy..
160             See L<"THEME RULES"> for more information.
161              
162              
163             =item C<< policy_is_thematic( -policy => $policy ) >>
164              
165             Given a reference to a L<Perl::Critic::Policy|Perl::Critic::Policy>
166             object, this method returns evaluates the rule against the themes that
167             are associated with the Policy. Returns 1 if the Policy satisfies the
168             rule, 0 otherwise.
169              
170              
171             =item C< rule() >
172              
173             Returns the rule expression that was used to construct this Theme.
174             The rule may have been translated into a normalized expression. See
175             L<"THEME RULES"> for more information.
176              
177             =back
178              
179              
180             =head2 THEME RULES
181              
182             A theme rule is a simple boolean expression, where the operands are
183             the names of any of the themes associated with the
184             Perl::Critic::Polices.
185              
186             Theme names can be combined with logical operators to form arbitrarily
187             complex expressions. Precedence is the same as normal mathematics,
188             but you can use parentheses to enforce precedence as well. Supported
189             operators are:
190              
191             Operator Altertative Example
192             ----------------------------------------------------------------
193             && and 'pbp && core'
194             || or 'pbp || (bugs && security)'
195             ! not 'pbp && ! (portability || complexity)
196              
197             See L<Perl::Critic/"CONFIGURATION"> for more information about
198             customizing the themes for each Policy.
199              
200              
201             =head1 SUBROUTINES
202              
203             =over
204              
205             =item C<cook_rule( $rule )>
206              
207             Standardize a rule into a almost executable Perl code. The "almost"
208             comes from the fact that theme names are left as is.
209              
210              
211             =back
212              
213              
214             =head1 CONSTANTS
215              
216             =over
217              
218             =item C<$RULE_INVALID_CHARACTER_REGEX>
219              
220             A regular expression that will return the first character in the
221             matched expression that is not valid in a rule.
222              
223              
224             =back
225              
226              
227             =head1 AUTHOR
228              
229             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
230              
231              
232             =head1 COPYRIGHT
233              
234             Copyright (c) 2006-2011 Imaginative Software Systems
235              
236             This program is free software; you can redistribute it and/or modify
237             it under the same terms as Perl itself. The full text of this license
238             can be found in the LICENSE file included with this module.
239              
240             =cut
241              
242             ##############################################################################
243             # Local Variables:
244             # mode: cperl
245             # cperl-indent-level: 4
246             # fill-column: 78
247             # indent-tabs-mode: nil
248             # c-indentation-style: bsd
249             # End:
250             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :