How can I use Smart::Comments in a module I load without changing its source?
command-line, module, perl
Solution
You almost certainly want to add `use Smart::Comments` to modules that contain such and then flip the switch in your environment by setting `$Smart_Comments` appropriately.
Stash-munging, `import`-hijacking monkey-patching is madness.
But maybe you're into that sort of thing. Say you have `Foo.pm`:
package Foo;
use Exporter 'import';
our @EXPORT = qw/ foo /;
#use Smart::Comments;
sub foo {
my @result;
for (my $i = 0; $i < 5; $i++) {
### $i
push @result => $i if $i % 2 == 0;
}
wantarray ? @result : \@result;
}
1;
Ordinary usage:
$ perl -MFoo -e 'print foo, "\n"'
024
Ordinary is dull and boring, of course. With `run-foo`, we take bold, dashing steps!
#! /usr/bin/perl
use warnings;
use strict;
BEGIN {
unshift @INC => \&inject_smart_comments;
my %direct;
open my $fh, "<", $0 or die "$0: open: $!";
while (<$fh>) {
++$direct{$1} if /^\s*use\s+([A-Z][:\w]*)/;
}
close $fh;
sub inject_smart_comments {
my(undef,$path) = @_;
s/[\/\\]/::/g, s/\.pm$// for my $mod = $path;
if ($direct{$mod}) {
open my $fh, "<", $path or die "$0: open $path: $!";
return sub {
return 0 unless defined($_ = <$fh>);
s{^(\s*package\s+[A-Z][:\w]*\s*;\s*)$}
{$1 use Smart::Comments;\n};
return 1;
};
}
}
}
use Foo;
print foo, "\n";
(Please pardon the compactness: I shrunk it so it would all fit in an unscrolled block.)
Output:
$ ./run-foo
### $i: 0
### $i: 1
### $i: 2
### $i: 3
### $i: 4
024
¡Viva!
With `@INC` hooks we can substitute our own or modified sources. The code watches for attempts to `require` modules directly used by the program. On a hit, `inject_smart_comments` returns an iterator that yields one line at a time. When this crafty, artful iterator sees the package declaration, it appends an innocent-looking `use Smart::Comments` to the chunk, making it appear as though it were in the module's source all along.
By trying to parse Perl code with regular expressions, the code will break if the package declaration isn't on a line by itself, for example. Season to taste.
Problem
How can I specify that Smart::Comments be loaded for my original script, as well as for any of the modules it directly loads. However, since it is a source-filter, it would probably wreck havoc if applied to every module loaded by every other loaded module. For example, my script includes ``` use Neu::Image; ``` I would like to load `Smart::Comments` for `Neu::Image` as well, but specifying ``` $ perl -MSmart::Comments script.pl ``` does not load `Smart::Comments` for `Neu::Image`. This behavior is described in the Smart::Comments documentation: If you're debugging an application you can also invoke it with the module from the command-line: ``` perl -MSmart::Comments $application.pl ``` Of course, this only enables smart comments in the application file itself, not in any modules that the application loads. A few other things that I've looked at already: - Perl Command-Line Options - perldoc perlrun (I searched it for the word "module") WORKAROUND As gbacon mentions, Smart::Comments provides an environment variable option that would allow turning it on or off. However, I would like to be able to turn it on without modifying the original source, if possible.