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   859 use 5.010001;
  40         184  
4 40     40   277 use strict;
  40         119  
  40         933  
5 40     40   226 use warnings;
  40         114  
  40         1283  
6 40     40   280 use English qw(-no_match_vars);
  40         126  
  40         370  
7 40     40   15483 use Readonly;
  40         104  
  40         1980  
8              
9 40     40   315 use Exporter 'import';
  40         127  
  40         1371  
10              
11 40     40   262 use Perl::Critic::Utils qw( :characters :data_conversion );
  40         127  
  40         2238  
12 40     40   10844 use Perl::Critic::Exception::Fatal::Internal qw( throw_internal );
  40         110  
  40         2562  
13             use Perl::Critic::Exception::Configuration::Option::Global::ParameterValue
14 40     40   306 qw( throw_global_value );
  40         114  
  40         1317  
15              
16             #-----------------------------------------------------------------------------
17              
18             our $VERSION = '1.150';
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 320     320 1 23324 my ( $class, %args ) = @_;
41 320         886 my $self = bless {}, $class;
42 320         1466 $self->_init( %args );
43 312         1629 return $self;
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub _init {
49              
50 320     320   855 my ($self, %args) = @_;
51 320   66     1348 my $rule = $args{-rule} || $EMPTY;
52              
53 320 100       1640 if ( $rule =~ m/$RULE_INVALID_CHARACTER_REGEX/xms ) {
54 8         46 throw_global_value
55             option_name => $CONFIG_KEY,
56             option_value => $rule,
57             message_suffix => qq{contains an invalid character: "$1".};
58             }
59              
60 312         836 $self->{_rule} = cook_rule( $rule );
61              
62 312         836 return $self;
63             }
64              
65             #-----------------------------------------------------------------------------
66              
67             sub rule {
68 5     5 1 16 my $self = shift;
69 5         27 return $self->{_rule};
70             }
71              
72             #-----------------------------------------------------------------------------
73              
74             sub policy_is_thematic {
75              
76 13340     13340 1 35959 my ($self, %args) = @_;
77             my $policy = $args{-policy}
78 13340   33     52145 || throw_internal 'The -policy argument is required';
79 13340 50       313035 ref $policy
80             || throw_internal 'The -policy must be an object';
81              
82 13340 100       41411 my $rule = $self->{_rule} or return 1;
83 10006         37948 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         19897 my $as_code = $rule; #Making a copy, so $rule is preserved
92 10006 100       43822 $as_code =~ s/ ( [\w\d]+ ) /exists $themes{$1} || 0/gexms;
  12616         52352  
93 10006         414869 my $is_thematic = eval $as_code; ## no critic (ProhibitStringyEval)
94              
95 10006 100       34623 if ($EVAL_ERROR) {
96 1         10 throw_global_value
97             option_name => $CONFIG_KEY,
98             option_value => $rule,
99             message_suffix => q{contains a syntax error.};
100             }
101              
102 10005         44961 return $is_thematic;
103             }
104              
105             #-----------------------------------------------------------------------------
106              
107             sub cook_rule {
108 607     607 1 9510 my ($raw_rule) = @_;
109 607 50       1444 return if not defined $raw_rule;
110              
111             #Translate logical operators
112 607         1359 $raw_rule =~ s{\b not \b}{!}ixmsg; # "not" -> "!"
113 607         1160 $raw_rule =~ s{\b and \b}{&&}ixmsg; # "and" -> "&&"
114 607         1079 $raw_rule =~ s{\b or \b}{||}ixmsg; # "or" -> "||"
115              
116             #Translate algebra operators (for backward compatibility)
117 607         941 $raw_rule =~ s{\A [-] }{!}ixmsg; # "-" -> "!" e.g. difference
118 607         965 $raw_rule =~ s{ [-] }{&& !}ixmsg; # "-" -> "&& !" e.g. difference
119 607         937 $raw_rule =~ s{ [*] }{&&}ixmsg; # "*" -> "&&" e.g. intersection
120 607         956 $raw_rule =~ s{ [+] }{||}ixmsg; # "+" -> "||" e.g. union
121              
122 607         1186 my $cooked_rule = lc $raw_rule; #Is now cooked!
123 607         1940 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-2023 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 :