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

Replies are listed 'Best First'.
Re: Exporter::Dream
by mowgli (Friar) on Jan 05, 2003 at 02:11 UTC
    This looks quite interesting indeed - as complicated as necessary, yet as simple as possible. I think the name Exporter::Dream, if used on cpan, would be less than optimal, though; imo, a module's name should, if possible, give a concise description of what the module does - i.e., it should be as informative as possible for someone who does not know the module yet.

    If it wasn't past 3 in the morning, I might actually be able to come up with a suggestion or two, too; I'll try to do so tomorrow, after a good night's sleep. :)

    -- mowgli

(bbfu) (Exporter::VA) Re: Exporter::Dream
by bbfu (Curate) on Jan 05, 2003 at 05:07 UTC

    Have you looked at John M. Dlugosz's Exporter::VA module? It also eschews inheritance, and has the option to leave off the package global, as well has having quite a few other useful features. Certainly worth checking out.

    bbfu
    Black flowers blossum
    Fearless on my breath

      In specific, Exporter::VA v1.2 is ready for Christmas. Now what? will do everything Exporter::Dream does, except for automaticly creating the :all tag. It also has the advantage of already exsiting, and doing pretty much anything you'd want an Exporter to do.

      You might also look at Exporter::Simple. Check out vague... it doesn't have anything to do with this, but it's funny!

      Update: vauge=>vague, link now works. Fixed Exporter::VA link; it's not on CPAN yet.


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

        In specific, Exporter::VA will do everything Exporter::Dream does, except for automaticly creating the :all tag. It also has the advantage of already exsiting, and doing pretty much anything you'd want an Exporter to do.

        It's a huge exporter with lots of options, various prefixes and (imho) unclear syntax. Although it does indeed do much of what Exporter::Dream does, I think the two can happily live together (I hope ::VA will get something like _prefix too. It's trivial to implement, and makes things much nicer). I'm not after versioning or configuration through the import method, especially not with v-strings :).

        You say Exporter::VA has the advantage of already existing. While that is true, it is of not much use until it is uploaded to CPAN. You used a cpan:// link, so you probably thought it was already there. Either it is not, or search.cpan.org is broken.

        - Yes, I reinvent wheels.
        - Spam: Visit eurotraQ.
        

        Actually, it's in my directory on CPAN, but not indexed. The namespace registration seems to take a while.

Re: Exporter::Dream
by Aristotle (Chancellor) on Jan 06, 2003 at 05:37 UTC

    I like it, but I don't like the name one bit. I would propose ::Simple, but that's already taken so I pondered a bit, and came up with Exporter::Tidy. I believe that would be a good name to describe your module's goal as well as its characteristics.

    I agree it can coexist with Exporter::VA which is a nice module when you want its features - but I wouldn't want to be dragging its weight around for things like modules used in small CGI scripts. What I like about this module is how it takes care of the cases where one might be tempted to write one's own import.

    As for configuration directives, it should be as simple as introducing a config => [] tag whose coderefs get called immediately, rather than exported. A bit of extra syntax to be able to pass parameters from the importing use might be good.

    And finally, I'd like to see negations - being able to say f.ex use Module qw(:all !foo); to have all symbols but &foo imported.

    Makeshifts last the longest.

Re: Exporter::Dream
by tadman (Prior) on Jan 07, 2003 at 23:38 UTC
    While the module itself seems good, the underscore option prefix does seem rather unruly. Since function names can't start with a dash, and yet hash elements can, maybe you could use that to indicate options. CGI uses this technique, for example.
    use MyModule::HTTP -prefix => 'foo', qw(get post), -prefix => 'bar', qw(get head);
    This also makes them feel, in my opinion, more switch like, comparable to command-line options. Underscores seem too unconventional.

      I'm sorry. I just hate abusing unary minus for that. I try to avoid modules that use it. You can't safely export subs that start with an underscore now, but I like to think of that as a feature: subs that start with underscore are supposed to be private. And if you want to export a symbol called _prefix, you're simply out of luck :).

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

Re: Exporter::Dream
by Anonymous Monk on Jan 06, 2003 at 18:40 UTC

    What an awful name (I'm not usually this blunt but it is worse than calling it Exporter::Dandy). Exporter::Prefixing would at least be descriptive. Why don't you roll these few extra features into one of the other exporting modules?

    Having lots of choices of similar modules might be nice for people writing scripts (and it might not). But it isn't very nice for people writing modules. Exporters are only used by modules.

    We already have a lots of reluctance among users to download modules because of the many headaches they run into. Now you want people downloading modules to end up downloading 4 or 5 different Exporter modules as well? I don't think the person trying to get a module installed will appreciate this situation.

    Please try to review your own modules with at least as much criticism as you seem to have in abundance for the modules of other people.

    I like your couple of little enhancements but don't find them to be nearly enough to justify a new module, especially one for use by module authors.

      What an awful name (I'm not usually this blunt but it is worse than calling it Exporter::Dandy).

      This name was chosen primarily because I couldn't think of any other name. So far, no one likes the name (including me, actually), so you can be sure it won't be put on CPAN using this name. I like Aristotle's suggestion (Exporter::Tidy). Do you have any suggestions?

      Exporter::Prefixing would at least be descriptive.

      That would describe one of its features, and not some of the others. If I were to release a module specifically to be able to prefix, it would not be a whole new exporter. It'd be something that wraps a lot like

      # untested, written in browser package Exporter::PrefixWrapper; use Carp; sub import { my (undef, $module, $prefix, @symbols) = @_; my $caller = (caller)[0]; eval "package $caller\::__FOO; use $module \@symbols;"; croak $@ if $@; for my $symbol (keys %{ "$caller\::__FOO::" }) { for my $type (qw/CODE SCALAR ARRAY HASH GLOB/) { next if not *{ "$caller\::__FOO::$symbol" }{$type}; *{ "$caller\::$prefix$symbol" } = *{ "$caller\::__FOO::$symbol" }{$type}; } } } 1; =head1 SYNOPSIS use Exporter::PrefixWrapper LWP::Simple => 'lwp_', qw(get $ua); $lwp_ua->agent('FooBar/0.01'); my $page = lwp_get "http://juerd.nl/" =cut
      so that it can be used with existing exporters.

      Having lots of choices of similar modules might be nice for people writing scripts (and it might not). But it isn't very nice for people writing modules.

      I don't see why not. Most of what I write is modular, and I like having alternatives to choose from.

      Now you want people downloading modules to end up downloading 4 or 5 different Exporter modules as well? I don't think the person trying to get a module installed will appreciate this situation.

      This is not a problem. People (should) use CPANPLUS (or CPAN.pm) to make downloading modules and prerequisites easy.

      I like your couple of little enhancements but don't find them to be nearly enough to justify a new module, especially one for use by module authors.

      I want to introduce new syntax as well as a simpler module. I tried to see if this could be added to an existing module, but that would create a mess. Other exporting modules are heavier or have different syntax.

      • Exporter uses @EXPORT etcetera
      • Exporter::Lite uses @EXPORT etcetera
      • Exporter::Simple uses attributes
      • Exporter::VA is heavy
      Then there's Exporter::Cluster, but that's to create bundles of imported subroutines.

      Exporter::Dream (whatever name it will have) is too different to be integrated into an existing exporter module.

      Thanks for commenting. Please consider signing up if you can (that is: if you own your own thoughts).

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

        Please consider signing up if you can (that is: if you own your own thoughts).

        What does that mean?