File Coverage

blib/lib/Keyword/DEVELOPMENT.pm
Criterion Covered Total %
statement 33 44 75.0
branch 9 20 45.0
condition n/a
subroutine 7 7 100.0
pod n/a
total 49 71 69.0


line stmt bran cond sub pod time code
1             package Keyword::DEVELOPMENT;
2              
3 3     3   223416 use 5.012; # required for pluggable keywords
  3         27  
4 3     3   17 use warnings;
  3         6  
  3         89  
5 3     3   15 use Carp 'croak';
  3         6  
  3         172  
6 3     3   1604 use Keyword::Simple;
  3         76193  
  3         1156  
7              
8             =head1 NAME
9              
10             Keyword::DEVELOPMENT - Have code blocks which don't exist unless you ask for them.
11              
12             =head1 VERSION
13              
14             Version 0.06
15              
16             =cut
17              
18             our $VERSION = '0.07';
19              
20             =head1 SYNOPSIS
21              
22             use Keyword::DEVELOPMENT;
23              
24             sub foo {
25             my $self = shift;
26             DEVELOPMENT {
27             $self->expensive_debugging_code;
28             }
29             ...
30             }
31              
32             =head1 EXPORT
33              
34             =head2 DEVELOPMENT
35              
36             This module exports one keyword, C. This keyword takes a code
37             block.
38              
39             If the environment variable C is set to a true
40             value, the code block is executed. Otherwise, the entire block is removed at
41             compile-time, thus ensuring that there is no runtime overhead for the block.
42              
43             ALternatively, you can set the C variable to a
44             valid Perl regular expression to only run C blocks in packages
45             matching the regex.
46              
47             This is primarily a development tool for performance-critical code.
48              
49             =cut
50              
51             my %CALLER;
52              
53             sub import {
54 3     3   32 my $class = shift;
55 3         7 my $caller = caller;
56              
57 3         5 my $include_production;
58 3 100       12 if ( grep { '-production' eq $_ } @_ ) {
  1         6  
59 1         1 $include_production = 1;
60 1         2 $CALLER{PRODUCTION} = $include_production;
61             }
62              
63 3         12 my $match = $ENV{PERL_KEYWORD_DEVELOPMENT_MATCH};
64 3 50       13 if ( defined $match ) {
65 0         0 $ENV{PERL_KEYWORD_DEVELOPMENT} = 1;
66 0         0 my $success = eval { qr/$match/; 1 };
  0         0  
  0         0  
67 0 0       0 unless ($success) {
68 0         0 my $error = $@;
69 0         0 croak(
70             "PERL_KEYWORD_DEVELOPMENT_MATCH environment variable '$ENV{PERL_KEYWORD_DEVELOPMENT_MATCH}' is not a valid regex: $error"
71             );
72             }
73             }
74              
75             Keyword::Simple::define 'DEVELOPMENT', sub {
76 2     2   52 my $in_development = 0;
77 2         5 my ($ref) = @_;
78 2 100       8 if ( $ENV{PERL_KEYWORD_DEVELOPMENT} ) {
79 1 50       12 if ( defined $match ) {
80 0 0       0 $in_development = $caller =~ /$match/ ? 1 : 0;
81             }
82             else {
83 1         4 $in_development = 1;
84             }
85             }
86 2         1675 substr( $$ref, 0, 0 ) = "if ($in_development)";
87 3         24 };
88              
89 3 100       212 if ($include_production) {
90             Keyword::Simple::define 'PRODUCTION', sub {
91 1     1   3 my $in_development = 0;
92 1         2 my ($ref) = @_;
93 1 50       3 if ( $ENV{PERL_KEYWORD_DEVELOPMENT} ) {
94 0 0       0 if ( defined $match ) {
95 0 0       0 $in_development = $caller =~ /$match/ ? 1 : 0;
96             }
97             else {
98 0         0 $in_development = 1;
99             }
100             }
101 1         1243 substr( $$ref, 0, 0 ) = "unless ($in_development)";
102 1         4 };
103             }
104             }
105              
106             =head1 EXAMPLE
107              
108             Consider the following code:
109              
110             #!/usr/bin/env perl
111              
112             BEGIN {
113             # just in case someone turned this off
114             $ENV{PERL_KEYWORD_DEVELOPMENT} = 1;
115             }
116             use lib 'lib';
117             use Keyword::DEVELOPMENT;
118              
119             my $value = 0;
120             DEVELOPMENT {
121             sleep 10;
122             $value = 1;
123             }
124              
125             print "Our value is $value";
126              
127             Running this code should print the following after about 10 seconds:
128              
129             Our value is 1
130              
131             However, if you set C to C<0> in the C block, it prints:
132              
133             Our value is 0
134              
135             To know that we really have B overhead during production, run the code under the debugger
136             with C set to C<0>.
137              
138             $ perl -d development.pl
139              
140             Loading DB routines from perl5db.pl version 1.49_04
141             Editor support available.
142              
143             Enter h or 'h h' for help, or 'man perldebug' for more help.
144              
145             main::(development.pl:10): my $value = 0;
146             auto(-1) DB<1> {{v
147             DB<2> n
148             main::(development.pl:10): my $value = 0;
149             auto(-1) DB<2> v
150             7: use lib 'lib';
151             8: use Keyword::DEVELOPMENT;
152             9
153             10==> my $value = 0;
154              
155             11 # PERL_KEYWORD_DEVELOPMENT was false, so the development code was removed.
156             12 #KDCT:_:_:1 DEVELOPMENT
157             13 #line 14 development.pl
158             14
159             15
160             16: print "Our value is $value";
161             DB<2>
162              
163             As you can see, there are only comments there, no code.
164              
165             Note the handy line directive on line 13 to ensure your line numbers remain
166             correct. If you're not familiar with line directives, see
167             L
168              
169             =head1 PRODUCTION
170              
171             As an if/else, you use pass C<-production> in the import list and get a
172             C keyword, too. A C block will always fire when a
173             C block does not, and vice versa.
174              
175             use Test::More;
176             use Keyword::DEVELOPMENT '-production';
177              
178             my $value = 0;
179             DEVELOPMENT {
180             $value = 1;
181             fail "DEVELOPMENT should be off, so we shouldn't get to here";
182             }
183             is $value, 0, 'Our DEVELOPMENT function should not be called';
184              
185             $value = 0;
186             PRODUCTION {
187             $value = 1;
188             pass "DEVELOPMENT should be off, so PRODUCTION blocks should fire";
189             }
190             ok $value, '... and be able to alter variables in its scope.';
191             done_testing;
192              
193             =head1 MATCHING PACKAGES
194              
195             If you use C extensively, you may find that the
196             C block is called too frequently. As of version 0.05, an
197             experimental feature has been added to allow you to only invoke C
198             blocks in packages matching a regex. Set the C
199             variable to a Perl regular expression instead of the
200             C variable. Only packages whose names match the
201             regular expression will have their C block triggered.
202              
203             PERL_KEYWORD_DEVELOPMENT_MATCH='^(?:Our::Codebase::|Our::MonkeyPatches::)' \
204             perl some_code.pl
205              
206             The above will only run C for packages whose package names start
207             with C or C.
208              
209             =head1 ALTERNATIVES
210              
211             As SawyerX pointed out, can replicate the functionality of this module in pure
212             Perl, if desired:
213              
214             use constant PRODUCTION => !!$ENV{PRODUCTION};
215             do {expensive_debugging_code()} unless PRODUCTION;
216              
217             Versus:
218              
219             use Keyword::DEVELOPMENT;
220             DEVELOPMENT {expensive_debugging_code()};
221              
222             The first version works because the line is removed entirely from the source
223             code using constant-folding (if C evaluates to false during
224             compile time, the entire line will be omitted).
225              
226             I think C is less fragile in that you never need to
227             remember the C statement modifier. However, we do rely on
228             the pluggable keyword functionality introduced in 5.012. Be warned!
229              
230             =head1 AUTHOR
231              
232             Curtis "Ovid" Poe, C<< >>
233              
234             =head1 BUGS AND LIMITATIONS
235              
236             Please report any bugs or feature requests to C
237             rt.cpan.org>, or through the web interface at
238             L. I will
239             be notified, and then you'll automatically be notified of progress on your bug
240             as I make changes.
241              
242             =head1 SUPPORT
243              
244             You can find documentation for this module with the perldoc command.
245              
246             perldoc Keyword::DEVELOPMENT
247              
248             You can also look for information at:
249              
250             =over 4
251              
252             =item * RT: CPAN's request tracker (report bugs here)
253              
254             L
255              
256             =item * AnnoCPAN: Annotated CPAN documentation
257              
258             L
259              
260             =item * CPAN Ratings
261              
262             L
263              
264             =item * Search CPAN
265              
266             L
267              
268             =back
269              
270             =head1 ACKNOWLEDGEMENTS
271              
272             Thanks to Damian Conway for the excellent C module.
273              
274             =head1 LICENSE AND COPYRIGHT
275              
276             Copyright 2017 Curtis "Ovid" Poe.
277              
278             This program is free software; you can redistribute it and/or modify it
279             under the terms of the the Artistic License (2.0). You may obtain a
280             copy of the full license at:
281              
282             L
283              
284             Any use, modification, and distribution of the Standard or Modified
285             Versions is governed by this Artistic License. By using, modifying or
286             distributing the Package, you accept this license. Do not use, modify,
287             or distribute the Package, if you do not accept this license.
288              
289             If your Modified Version has been derived from a Modified Version made
290             by someone other than you, you are nevertheless required to ensure that
291             your Modified Version complies with the requirements of this license.
292              
293             This license does not grant you the right to use any trademark, service
294             mark, tradename, or logo of the Copyright Holder.
295              
296             This license includes the non-exclusive, worldwide, free-of-charge
297             patent license to make, have made, use, offer to sell, sell, import and
298             otherwise transfer the Package with respect to any patent claims
299             licensable by the Copyright Holder that are necessarily infringed by the
300             Package. If you institute patent litigation (including a cross-claim or
301             counterclaim) against any party alleging that the Package constitutes
302             direct or contributory patent infringement, then this Artistic License
303             to you shall terminate on the date that such litigation is filed.
304              
305             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
306             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
307             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
308             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
309             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
310             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
311             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
312             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
313              
314             =cut
315              
316             1; # End of Keyword::DEVELOPMENT