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   930 use 5.010001;
  40         171  
4 40     40   286 use strict;
  40         115  
  40         893  
5 40     40   262 use warnings;
  40         133  
  40         1263  
6 40     40   265 use English qw(-no_match_vars);
  40         118  
  40         328  
7 40     40   14548 use Readonly;
  40         139  
  40         2083  
8              
9 40     40   280 use Exporter 'import';
  40         125  
  40         1408  
10              
11 40     40   285 use Perl::Critic::Utils qw{ :characters :data_conversion };
  40         140  
  40         2188  
12 40     40   10829 use Perl::Critic::Exception::Fatal::Internal qw{ &throw_internal };
  40         101  
  40         4121  
13             use Perl::Critic::Exception::Configuration::Option::Global::ParameterValue
14 40     40   337 qw{ &throw_global_value };
  40         124  
  40         1315  
15              
16             #-----------------------------------------------------------------------------
17              
18             our $VERSION = '1.148';
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 32451 my ( $class, %args ) = @_;
41 2962         9304 my $self = bless {}, $class;
42 2962         13701 $self->_init( %args );
43 2954         15743 return $self;
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub _init {
49              
50 2962     2962   7275 my ($self, %args) = @_;
51 2962   66     15544 my $rule = $args{-rule} || $EMPTY;
52              
53 2962 100       14173 if ( $rule =~ m/$RULE_INVALID_CHARACTER_REGEX/xms ) {
54 8         45 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         8434 $self->{_rule} = cook_rule( $rule );
61              
62 2954         8193 return $self;
63             }
64              
65             #-----------------------------------------------------------------------------
66              
67             sub rule {
68 5     5 1 18 my $self = shift;
69 5         25 return $self->{_rule};
70             }
71              
72             #-----------------------------------------------------------------------------
73              
74             sub policy_is_thematic {
75              
76 13340     13340 1 36580 my ($self, %args) = @_;
77             my $policy = $args{-policy}
78 13340   33     52477 || throw_internal 'The -policy argument is required';
79 13340 50       334781 ref $policy
80             || throw_internal 'The -policy must be an object';
81              
82 13340 100       44225 my $rule = $self->{_rule} or return 1;
83 10006         41049 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         19924 my $as_code = $rule; #Making a copy, so $rule is preserved
92 10006 100       47234 $as_code =~ s/ ( [\w\d]+ ) /exists $themes{$1} || 0/gexms;
  12616         56783  
93 10006         461585 my $is_thematic = eval $as_code; ## no critic (ProhibitStringyEval)
94              
95 10006 100       37364 if ($EVAL_ERROR) {
96 1         11 throw_global_value
97             option_name => $CONFIG_KEY,
98             option_value => $rule,
99             message_suffix => q{contains a syntax error.};
100             }
101              
102 10005         47512 return $is_thematic;
103             }
104              
105             #-----------------------------------------------------------------------------
106              
107             sub cook_rule {
108 5891     5891 1 20217 my ($raw_rule) = @_;
109 5891 50       13851 return if not defined $raw_rule;
110              
111             #Translate logical operators
112 5891         11428 $raw_rule =~ s{\b not \b}{!}ixmsg; # "not" -> "!"
113 5891         10577 $raw_rule =~ s{\b and \b}{&&}ixmsg; # "and" -> "&&"
114 5891         9040 $raw_rule =~ s{\b or \b}{||}ixmsg; # "or" -> "||"
115              
116             #Translate algebra operators (for backward compatibility)
117 5891         8265 $raw_rule =~ s{\A [-] }{!}ixmsg; # "-" -> "!" e.g. difference
118 5891         8688 $raw_rule =~ s{ [-] }{&& !}ixmsg; # "-" -> "&& !" e.g. difference
119 5891         9652 $raw_rule =~ s{ [*] }{&&}ixmsg; # "*" -> "&&" e.g. intersection
120 5891         8947 $raw_rule =~ s{ [+] }{||}ixmsg; # "+" -> "||" e.g. union
121              
122 5891         10258 my $cooked_rule = lc $raw_rule; #Is now cooked!
123 5891         17084 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 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 :