package Exporter::Dream; use strict; use Carp; no strict 'refs'; our $VERSION = '0.00_01'; my %types = ('' => qw/CODE @ ARRAY $ SCALAR % HASH * GLOB/); sub import { my (undef, %tags) = @_; my $caller = (caller)[0]; my $map = delete($tags{_map}) || {}; my %available; @available{ keys %$map, grep !/^:/, map @$_, values %tags } = (); $tags{all} = [ keys %available ] if not exists $tags{all}; *{"$caller\::import"} = sub { my ($me, @symbols) = @_; my $caller = (caller)[0]; @symbols = (':default') if @symbols == 0 and exists $tags{default}; my %exported; my $prefix = ''; while (my $symbol = shift @symbols) { if ($symbol eq '_prefix') { $prefix = shift @symbols; next; } my $real = exists $map->{$symbol} ? $map->{$symbol} : $symbol; next if exists $exported{$real}; if ($real =~ /^:(.*)/) { croak "Unknown tag: $1" if not exists $tags{$1}; push @symbols, @{ $tags{$1} }; } else { if (ref $real) { $symbol =~ s/^[\@\$%*]//; *{"$caller\::$prefix$symbol"} = $real; } else { croak "Unknown symbol: $real\n" unless exists $available{$symbol}; my ($sigil, $name) = $real =~ /^([\@\$%*]?)(.*)/; $symbol =~ s/^[\@\$%*]//; *{"$caller\::$prefix$symbol"} = *{"$me\::$name"}{ $types{$sigil} }; } } undef $exported{$symbol}; } }; } 1; =head1 NAME Exporter::Dream - Another way of exporting symbols =head1 SYNOPSIS package MyModule::HTTP; use Exporter::Dream default => [ qw(get) ], other => [ qw(post head) ]; use MyModule::HTTP qw(:all); use MyModule::HTTP qw(:default post); use MyModule::HTTP qw(post); use MyModule::HTTP _prefix => 'http_', qw(get post); use MyModule::HTTP qw(get post), _prefix => 'http_', qw(head); use MyModule::HTTP _prefix => 'foo', qw(get post), _prefix => 'bar', qw(get head); package MyModule::Foo; use Exporter::Dream default => [ qw($foo $bar quux) ], _map => { '$foo' => \$my_foo, '$bar' => \$my_bar, quux => sub { print "Hello, world!\n" } }; package MyModule::Constants; use Exporter::Dream default => [ qw(:all) ], _map => { FOO => sub () { 1 }, BAR => sub () { 2 }, OK => sub () { 1 }, FAILURE => sub () { 0 } }; =head1 DESCRIPTION This module serves as an easy, clean alternative to Exporter. Unlike Exporter, it is not subclassed, but it simply exports a custom import() into your namespace. With Exporter::Dream, you don't need to use any package global in your module. Even the subs you export can be lexically scoped. =head2 use Exporter::Dream LIST The list supplied to C should be a key-value list. Each key serves as a tag, used to group exportable symbols. The values in this key-value list should be array references. There are a few special tags: =over 10 =item all If you don't provide an C tag yourself, Dream::Exporter will generate one for you. It will contain all exportable symbols. =item default The C tag will be used if the user supplies no list to the C statement. =item _map With _map you should not use an array reference, but a hash reference. Here, you can rewrite symbols to other names or even define one on the spot by using a reference. You can C<< foo => 'bar' >> to export C if C is requested. =back =head2 Exportable symbols Every symbol specified in a tag's array, or used as a key in _map's hash is exportable. =head2 Symbol types You can export subs, scalars, arrays, hashes and typeglobs. Do not use an ampersand (C<&>) for subs. All other types must have the proper sigil. =head2 Importing from a module that uses Exporter::Dream You can use either a symbol name (without the sigil if it is a sub, or with the appropriate sigil if it is not), or a tag name prefixed with a colon. It is possible to import a symbol twice, but a symbol is never exported twice under the same name, so you can use tags that overlap. If you supply any list to the C statement, C<:default> is no longer used if not specified explicitly. To avoid name clashes, it is possible to have symbols prefixed. Supply C<_prefix> followed by the prefix that you want. Multiple can be used. use Some::Module qw(foo bar), _prefix => 'some_', qw(quux); imports Some::Module::foo as foo, Some::Module::bar as bar, and Some::Module::quux as some_quux. See the SYNOPSIS for more examples. =head1 AUTHOR Juerd =cut