Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Smart::Comments in a module I load without changing its source?

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.

like image 777
Christopher Bottoms Avatar asked Jan 28 '10 18:01

Christopher Bottoms


People also ask

Where is @INC in Perl?

Perl interpreter is compiled with a specific @INC default value. To find out this value, run env -i perl -V command ( env -i ignores the PERL5LIB environmental variable - see #2) and in the output you will see something like this: $ env -i perl -V ... @INC: /usr/lib/perl5/site_perl/5.18.

How do we create packages and modules in Perl?

BEGIN and END BlocksEvery BEGIN block is executed after the perl script is loaded and compiled but before any other statement is executed. Every END block is executed just before the perl interpreter exits. The BEGIN and END blocks are particularly useful when creating Perl modules.


2 Answers

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.

like image 57
Greg Bacon Avatar answered Oct 08 '22 19:10

Greg Bacon


It doesn't seem like this idea makes any sense. If you are utilizing Smart::Comments in a module, why would you not want to use Smart::Comments in that module's source? Even if you could get Smart::Comments to apply to all modules loaded in a script via -M, it probably wouldn't be a good idea because:

  • You're obfuscating the fact that your modules are using smart comments by not including the use line in their source.
  • You could potentially introduce bizarre behavior from modules you use in your script which happen to have what look like smart comments, but aren't really. If a module doesn't contain smart comments, you should not force them down its throat.

As gbacon said, the right way to do this is to use the module in each of your modules that make use of it, and then suppress them with an environment variable when you don't want the output.

Also as he said, it's still probably possible to do this with some "Stash-munging, import-hijacking monkey-patching" madness, but that's a lot of work. I don't think anyone is going to put the effort into giving you a solution along those lines when it is not a good idea in the first place.

like image 43
Adam Bellaire Avatar answered Oct 08 '22 19:10

Adam Bellaire