Acme::Globule - Extensible package-local way to override glob()


Acme-Globule documentation Contained in the Acme-Globule distribution.

Index


Code Index:

NAME

Top

Acme::Globule - Extensible package-local way to override glob()

VERSION

Top

version 0.004

SYNOPSIS

Top

 # a simple plugin
 package Acme::Globule::Ping;

 sub globule {
   my($self, $pattern) = @_;
   # somebody did <ping> and so we want to return ('pong')
   return [ "pong" ] if $pattern eq 'ping';
   # they didn't ping, so pass
   return;
 }

 # a simple client
 package main;

 use Acme::Globule qw( Ping );

 # prints "pong'
 print <ping>;
 # prints the location of your home directory
 print <~>;

DESCRIPTION

Top

This package extends glob (and thus <>) to return custom results. It has a plugin mechanism and you define which plugins you wish to use on the import line. Now when you call glob(), these plugins will be tried left-to-right until one claims it, with a fall-through to the standard glob() function.

Each of your packages may use different plugins, and packages that do not import Acme::Globule will get standard glob() behaviour.

Creating a plugin

Top

To create a plugin, create a module Acme::Globule::* and provide a globule() method. The globule method should return an array reference containing the matches, or nothing if it wishes to decline and let the next plugin try it.

BUGS

Top

Any code that uses this module is perverse and therefore contains at least one bug.

This module globally hooks both File::Glob::csh_glob CORE::GLOBAL::glob, and so using this module anywhere in a program will cause all uses of glob() to suffer a slight performance hit even in other modules which do not use it.

glob() within an eval() will probably not do what you expect.

SEE ALSO

Top

Acme::Globule::*, the plugins.

AUTHOR

Top

Peter Corlett <abuse@cabal.org.uk>

COPYRIGHT AND LICENSE

Top


Acme-Globule documentation Contained in the Acme-Globule distribution.

package Acme::Globule;
BEGIN {
  $Acme::Globule::DIST = 'Acme-Globule';
}
BEGIN {
  $Acme::Globule::VERSION = '0.004';
}
# ABSTRACT: Extensible package-local way to override glob()
use warnings;
use strict;

# a quick dance to get at the glob()/<> implementation that we replace with
# a wrapper
use File::Glob qw( csh_glob );
my $csh_glob = \&csh_glob;

use Module::Load;

# This is a hash mapping packages that use us to the Globule plugins they
# requested.
my %clients;

# This is a hash of plugins that have been pulled in so far, and maps to the
# name of the package that actually implements the plugin.
my %plugins;

sub import {
    my($self, @plugins) = @_;
    my($importer) = caller;

    foreach my $plugin (@plugins) {
        unless (defined $plugins{$plugin}) {
            my $pkgname = __PACKAGE__."::$plugin";
            load $pkgname;
            $plugins{$plugin} = $pkgname;
        }
    }

    $clients{$importer} =  \@plugins;
}

sub _new_csh_glob {
    my($pattern) = @_;
    my($caller) = caller;  # contains package of caller, or (eval) etc, but
    # will match an entry in %clients for any package
    # that imported us
    if (my $client = $clients{$caller}) {
        # The caller imported us, so we work through the plugins they requested
        foreach my $plugin (@$client) {
            # Try the pattern against each plugin in turn, until one returns a
            # true value. This is assumed to be an arrayref that contains the
            # result of the glob
            my $result = $plugins{$plugin}->globule($pattern);
            return @$result if $result;
        }
    }
    # Since no plugins matched (or the caller didn't import us), we fall
    # through to the original glob function
    goto &$csh_glob;
}

no warnings;              # we don't want "subroutine redefined" diagnostics
*File::Glob::csh_glob = \&_new_csh_glob;
*CORE::GLOBAL::glob = \&File::Glob::csh_glob;

1;


1;

__END__