Hi,
I had this idea about a better Exporter. A friendlier one that wouldn't need globals and wouldn't need redundant definitions. One that has a :all built in (empty /pattern/ is an ugly way to do it, imho).
I prototyped it with the synopsis that you find if you scroll down a bit, and then created the module. I hacked up some documentation. And here it is: Exporter::Dream.
Please let me know what you think about the code and documentation. I intend to release the module on CPAN - is the name Exporter::Dream acceptable?
Note that this module doesn't provide a way to implement configuration through use statements. CGI::Carp uses fatalsToBrowser not to export a symbol, but to instate a handler. Some other modules use items in import()'s argument list to set flags. Should provisions for this be made, or should an exporter module just export, and not bother with configuration?
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{defa +ult}; my %exported; my $prefix = ''; while (my $symbol = shift @symbols) { if ($symbol eq '_prefix') { $prefix = shift @symbols; next; } my $real = exists $map->{$symbol} ? $map->{$symbol} : $sym +bol; 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 E +xporter, it is not subclassed, but it simply exports a custom import() into you +r 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<use Exporter::Dream> should be a key-value list +. Each key serves as a tag, used to group exportable symbols. The values in t +his key-value list should be array references. There are a few special tags: =over 10 =item all If you don't provide an C<all> tag yourself, Dream::Exporter will gene +rate one for you. It will contain all exportable symbols. =item default The C<default> tag will be used if the user supplies no list to the C< +use> 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<bar> if C<foo> 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 twic +e under the same name, so you can use tags that overlap. If you supply any lis +t to the C<use> statement, C<:default> is no longer used if not specified e +xplicitly. 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 <juerd@cpan.org> =cut
Edit by tye to add READMORE
In reply to Exporter::Dream by Juerd
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |