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   757 use 5.010001;
  40         130  
4 40     40   219 use strict;
  40         75  
  40         872  
5 40     40   143 use warnings;
  40         70  
  40         2248  
6 40     40   176 use English qw(-no_match_vars);
  40         85  
  40         300  
7 40     40   12728 use Readonly;
  40         70  
  40         1955  
8              
9 40     40   174 use Exporter 'import';
  40         64  
  40         1200  
10              
11 40     40   159 use Perl::Critic::Utils qw( :characters :data_conversion );
  40         66  
  40         2054  
12 40     40   8852 use Perl::Critic::Exception::Fatal::Internal qw( throw_internal );
  40         75  
  40         2127  
13             use Perl::Critic::Exception::Configuration::Option::Global::ParameterValue
14 40     40   208 qw( throw_global_value );
  40         82  
  40         976  
15              
16             #-----------------------------------------------------------------------------
17              
18             our $VERSION = '1.156';
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 321     321 1 17965 my ( $class, %args ) = @_;
41 321         841 my $self = bless {}, $class;
42 321         1512 $self->_init( %args );
43 313         1334 return $self;
44             }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub _init {
49              
50 321     321   852 my ($self, %args) = @_;
51 321   66     1278 my $rule = $args{-rule} || $EMPTY;
52              
53 321 100       1644 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 313         692 $self->{_rule} = cook_rule( $rule );
61              
62 313         670 return $self;
63             }
64              
65             #-----------------------------------------------------------------------------
66              
67             sub rule {
68 5     5 1 12 my $self = shift;
69 5         24 return $self->{_rule};
70             }
71              
72             #-----------------------------------------------------------------------------
73              
74             sub policy_is_thematic {
75              
76 13485     13485 1 27248 my ($self, %args) = @_;
77             my $policy = $args{-policy}
78 13485   33     37194 || throw_internal 'The -policy argument is required';
79 13485 50       246547 ref $policy
80             || throw_internal 'The -policy must be an object';
81              
82 13485 100       36690 my $rule = $self->{_rule} or return 1;
83 10006         23075 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         16378 my $as_code = $rule; #Making a copy, so $rule is preserved
92 10006 100       32613 $as_code =~ s/ ( [\w\d]+ ) /exists $themes{$1} || 0/gexms;
  12616         37386  
93 10006         427043 my $is_thematic = eval $as_code; ## no critic (ProhibitStringyEval)
94              
95 10006 100       31359 if ($EVAL_ERROR) {
96 1         9 throw_global_value
97             option_name => $CONFIG_KEY,
98             option_value => $rule,
99             message_suffix => q{contains a syntax error.};
100             }
101              
102 10005         39406 return $is_thematic;
103             }
104              
105             #-----------------------------------------------------------------------------
106              
107             sub cook_rule {
108 609     609 1 10858 my ($raw_rule) = @_;
109 609 50       1130 return if not defined $raw_rule;
110              
111             #Translate logical operators
112 609         1196 $raw_rule =~ s{\b not \b}{!}ixmsg; # "not" -> "!"
113 609         854 $raw_rule =~ s{\b and \b}{&&}ixmsg; # "and" -> "&&"
114 609         1580 $raw_rule =~ s{\b or \b}{||}ixmsg; # "or" -> "||"
115              
116             #Translate algebra operators (for backward compatibility)
117 609         779 $raw_rule =~ s{\A [-] }{!}ixmsg; # "-" -> "!" e.g. difference
118 609         814 $raw_rule =~ s{ [-] }{&& !}ixmsg; # "-" -> "&& !" e.g. difference
119 609         810 $raw_rule =~ s{ [*] }{&&}ixmsg; # "*" -> "&&" e.g. intersection
120 609         841 $raw_rule =~ s{ [+] }{||}ixmsg; # "+" -> "||" e.g. union
121              
122 609         957 my $cooked_rule = lc $raw_rule; #Is now cooked!
123 609         1537 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 :