summaryrefslogtreecommitdiff
path: root/lib/Exporter/Tiny.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Exporter/Tiny.pm')
-rw-r--r--lib/Exporter/Tiny.pm833
1 files changed, 833 insertions, 0 deletions
diff --git a/lib/Exporter/Tiny.pm b/lib/Exporter/Tiny.pm
new file mode 100644
index 0000000..6e77338
--- /dev/null
+++ b/lib/Exporter/Tiny.pm
@@ -0,0 +1,833 @@
+package Exporter::Tiny;
+
+use 5.006001;
+use strict;
+use warnings; no warnings qw(void once uninitialized numeric redefine);
+
+our $AUTHORITY = 'cpan:TOBYINK';
+our $VERSION = '0.042';
+our @EXPORT_OK = qw< mkopt mkopt_hash _croak _carp >;
+
+sub _croak ($;@) { require Carp; my $fmt = shift; @_ = sprintf($fmt, @_); goto \&Carp::croak }
+sub _carp ($;@) { require Carp; my $fmt = shift; @_ = sprintf($fmt, @_); goto \&Carp::carp }
+
+my $_process_optlist = sub
+{
+ my $class = shift;
+ my ($global_opts, $opts, $want, $not_want) = @_;
+
+ while (@$opts)
+ {
+ my $opt = shift @{$opts};
+ my ($name, $value) = @$opt;
+
+ ($name =~ m{\A\!(/.+/[msixpodual]+)\z}) ?
+ do {
+ my @not = $class->_exporter_expand_regexp($1, $value, $global_opts);
+ ++$not_want->{$_->[0]} for @not;
+ } :
+ ($name =~ m{\A\!(.+)\z}) ?
+ (++$not_want->{$1}) :
+ ($name =~ m{\A[:-](.+)\z}) ?
+ push(@$opts, $class->_exporter_expand_tag($1, $value, $global_opts)) :
+ ($name =~ m{\A/.+/[msixpodual]+\z}) ?
+ push(@$opts, $class->_exporter_expand_regexp($name, $value, $global_opts)) :
+ # else ?
+ push(@$want, $opt);
+ }
+};
+
+sub import
+{
+ my $class = shift;
+ my $global_opts = +{ @_ && ref($_[0]) eq q(HASH) ? %{+shift} : () };
+ $global_opts->{into} = caller unless exists $global_opts->{into};
+
+ my @want;
+ my %not_want; $global_opts->{not} = \%not_want;
+ my @args = do { no strict qw(refs); @_ ? @_ : @{"$class\::EXPORT"} };
+ my $opts = mkopt(\@args);
+ $class->$_process_optlist($global_opts, $opts, \@want, \%not_want);
+
+ my $permitted = $class->_exporter_permitted_regexp($global_opts);
+ $class->_exporter_validate_opts($global_opts);
+
+ for my $wanted (@want)
+ {
+ next if $not_want{$wanted->[0]};
+
+ my %symbols = $class->_exporter_expand_sub(@$wanted, $global_opts, $permitted);
+ $class->_exporter_install_sub($_, $wanted->[1], $global_opts, $symbols{$_})
+ for keys %symbols;
+ }
+}
+
+sub unimport
+{
+ my $class = shift;
+ my $global_opts = +{ @_ && ref($_[0]) eq q(HASH) ? %{+shift} : () };
+ $global_opts->{into} = caller unless exists $global_opts->{into};
+ $global_opts->{is_unimport} = 1;
+
+ my @want;
+ my %not_want; $global_opts->{not} = \%not_want;
+ my @args = do { our %TRACKED; @_ ? @_ : keys(%{$TRACKED{$class}{$global_opts->{into}}}) };
+ my $opts = mkopt(\@args);
+ $class->$_process_optlist($global_opts, $opts, \@want, \%not_want);
+
+ my $permitted = $class->_exporter_permitted_regexp($global_opts);
+ $class->_exporter_validate_unimport_opts($global_opts);
+
+ my $expando = $class->can('_exporter_expand_sub');
+ $expando = undef if $expando == \&_exporter_expand_sub;
+
+ for my $wanted (@want)
+ {
+ next if $not_want{$wanted->[0]};
+
+ if ($wanted->[1])
+ {
+ _carp("Passing options to unimport '%s' makes no sense", $wanted->[0])
+ unless (ref($wanted->[1]) eq 'HASH' and not keys %{$wanted->[1]});
+ }
+
+ my %symbols = defined($expando)
+ ? $class->$expando(@$wanted, $global_opts, $permitted)
+ : ($wanted->[0] => sub { "dummy" });
+ $class->_exporter_uninstall_sub($_, $wanted->[1], $global_opts)
+ for keys %symbols;
+ }
+}
+
+# Called once per import/unimport, passed the "global" import options.
+# Expected to validate the options and carp or croak if there are problems.
+# Can also take the opportunity to do other stuff if needed.
+#
+sub _exporter_validate_opts { 1 }
+sub _exporter_validate_unimport_opts { 1 }
+
+# Called after expanding a tag or regexp to merge the tag's options with
+# any sub-specific options.
+#
+sub _exporter_merge_opts
+{
+ my $class = shift;
+ my ($tag_opts, $global_opts, @stuff) = @_;
+
+ $tag_opts = {} unless ref($tag_opts) eq q(HASH);
+ _croak('Cannot provide an -as option for tags')
+ if exists $tag_opts->{-as};
+
+ my $optlist = mkopt(\@stuff);
+ for my $export (@$optlist)
+ {
+ next if defined($export->[1]) && ref($export->[1]) ne q(HASH);
+
+ my %sub_opts = ( %{ $export->[1] or {} }, %$tag_opts );
+ $sub_opts{-prefix} = sprintf('%s%s', $tag_opts->{-prefix}, $export->[1]{-prefix})
+ if exists($export->[1]{-prefix}) && exists($tag_opts->{-prefix});
+ $sub_opts{-suffix} = sprintf('%s%s', $export->[1]{-suffix}, $tag_opts->{-suffix})
+ if exists($export->[1]{-suffix}) && exists($tag_opts->{-suffix});
+ $export->[1] = \%sub_opts;
+ }
+ return @$optlist;
+}
+
+# Given a tag name, looks it up in %EXPORT_TAGS and returns the list of
+# associated functions. The default implementation magically handles tags
+# "all" and "default". The default implementation interprets any undefined
+# tags as being global options.
+#
+sub _exporter_expand_tag
+{
+ no strict qw(refs);
+
+ my $class = shift;
+ my ($name, $value, $globals) = @_;
+ my $tags = \%{"$class\::EXPORT_TAGS"};
+
+ return $class->_exporter_merge_opts($value, $globals, $tags->{$name}->($class, @_))
+ if ref($tags->{$name}) eq q(CODE);
+
+ return $class->_exporter_merge_opts($value, $globals, @{$tags->{$name}})
+ if exists $tags->{$name};
+
+ return $class->_exporter_merge_opts($value, $globals, @{"$class\::EXPORT"}, @{"$class\::EXPORT_OK"})
+ if $name eq 'all';
+
+ return $class->_exporter_merge_opts($value, $globals, @{"$class\::EXPORT"})
+ if $name eq 'default';
+
+ $globals->{$name} = $value || 1;
+ return;
+}
+
+# Given a regexp-like string, looks it up in @EXPORT_OK and returns the
+# list of matching functions.
+#
+sub _exporter_expand_regexp
+{
+ no strict qw(refs);
+ our %TRACKED;
+
+ my $class = shift;
+ my ($name, $value, $globals) = @_;
+ my $compiled = eval("qr$name");
+
+ my @possible = $globals->{is_unimport}
+ ? keys( %{$TRACKED{$class}{$globals->{into}}} )
+ : @{"$class\::EXPORT_OK"};
+
+ $class->_exporter_merge_opts($value, $globals, grep /$compiled/, @possible);
+}
+
+# Helper for _exporter_expand_sub. Returns a regexp matching all subs in
+# the exporter package which are available for export.
+#
+sub _exporter_permitted_regexp
+{
+ no strict qw(refs);
+ my $class = shift;
+ my $re = join "|", map quotemeta, sort {
+ length($b) <=> length($a) or $a cmp $b
+ } @{"$class\::EXPORT"}, @{"$class\::EXPORT_OK"};
+ qr{^(?:$re)$}ms;
+}
+
+# Given a sub name, returns a hash of subs to install (usually just one sub).
+# Keys are sub names, values are coderefs.
+#
+sub _exporter_expand_sub
+{
+ my $class = shift;
+ my ($name, $value, $globals, $permitted) = @_;
+ $permitted ||= $class->_exporter_permitted_regexp($globals);
+
+ no strict qw(refs);
+
+ if ($name =~ $permitted)
+ {
+ my $generator = $class->can("_generate_$name");
+ return $name => $class->$generator($name, $value, $globals) if $generator;
+
+ my $sub = $class->can($name);
+ return $name => $sub if $sub;
+ }
+
+ $class->_exporter_fail(@_);
+}
+
+# Called by _exporter_expand_sub if it is unable to generate a key-value
+# pair for a sub.
+#
+sub _exporter_fail
+{
+ my $class = shift;
+ my ($name, $value, $globals) = @_;
+ return if $globals->{is_unimport};
+ _croak("Could not find sub '%s' exported by %s", $name, $class);
+}
+
+# Actually performs the installation of the sub into the target package. This
+# also handles renaming the sub.
+#
+sub _exporter_install_sub
+{
+ my $class = shift;
+ my ($name, $value, $globals, $sym) = @_;
+
+ my $into = $globals->{into};
+ my $installer = $globals->{installer} || $globals->{exporter};
+
+ $name = $value->{-as} || $name;
+ unless (ref($name) eq q(SCALAR))
+ {
+ my ($prefix) = grep defined, $value->{-prefix}, $globals->{prefix}, q();
+ my ($suffix) = grep defined, $value->{-suffix}, $globals->{suffix}, q();
+ $name = "$prefix$name$suffix";
+ }
+
+ return ($$name = $sym) if ref($name) eq q(SCALAR);
+ return ($into->{$name} = $sym) if ref($into) eq q(HASH);
+
+ no strict qw(refs);
+
+ if (exists &{"$into\::$name"} and \&{"$into\::$name"} != $sym)
+ {
+ my ($level) = grep defined, $value->{-replace}, $globals->{replace}, q(0);
+ my $action = {
+ carp => \&_carp,
+ 0 => \&_carp,
+ '' => \&_carp,
+ warn => \&_carp,
+ nonfatal => \&_carp,
+ croak => \&_croak,
+ fatal => \&_croak,
+ die => \&_croak,
+ }->{$level} || sub {};
+
+ $action->(
+ $action == \&_croak
+ ? "Refusing to overwrite existing sub '%s::%s' with sub '%s' exported by %s"
+ : "Overwriting existing sub '%s::%s' with sub '%s' exported by %s",
+ $into,
+ $name,
+ $_[0],
+ $class,
+ );
+ }
+
+ our %TRACKED;
+ $TRACKED{$class}{$into}{$name} = $sym;
+
+ no warnings qw(prototype);
+ $installer
+ ? $installer->($globals, [$name, $sym])
+ : (*{"$into\::$name"} = $sym);
+}
+
+sub _exporter_uninstall_sub
+{
+ our %TRACKED;
+ my $class = shift;
+ my ($name, $value, $globals, $sym) = @_;
+ my $into = $globals->{into};
+ ref $into and return;
+
+ no strict qw(refs);
+
+ # Cowardly refuse to uninstall a sub that differs from the one
+ # we installed!
+ my $our_coderef = $TRACKED{$class}{$into}{$name};
+ my $cur_coderef = exists(&{"$into\::$name"}) ? \&{"$into\::$name"} : -1;
+ return unless $our_coderef == $cur_coderef;
+
+ my $stash = \%{"$into\::"};
+ my $old = delete $stash->{$name};
+ my $full_name = join('::', $into, $name);
+ foreach my $type (qw(SCALAR HASH ARRAY IO)) # everything but the CODE
+ {
+ next unless defined(*{$old}{$type});
+ *$full_name = *{$old}{$type};
+ }
+
+ delete $TRACKED{$class}{$into}{$name};
+}
+
+sub mkopt
+{
+ my $in = shift or return [];
+ my @out;
+
+ $in = [map(($_ => ref($in->{$_}) ? $in->{$_} : ()), sort keys %$in)]
+ if ref($in) eq q(HASH);
+
+ for (my $i = 0; $i < @$in; $i++)
+ {
+ my $k = $in->[$i];
+ my $v;
+
+ ($i == $#$in) ? ($v = undef) :
+ !defined($in->[$i+1]) ? (++$i, ($v = undef)) :
+ !ref($in->[$i+1]) ? ($v = undef) :
+ ($v = $in->[++$i]);
+
+ push @out, [ $k => $v ];
+ }
+
+ \@out;
+}
+
+sub mkopt_hash
+{
+ my $in = shift or return;
+ my %out = map +($_->[0] => $_->[1]), @{ mkopt($in) };
+ \%out;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf-8
+
+=for stopwords frobnicate greps regexps
+
+=head1 NAME
+
+Exporter::Tiny - an exporter with the features of Sub::Exporter but only core dependencies
+
+=head1 SYNOPSIS
+
+ package MyUtils;
+ use base "Exporter::Tiny";
+ our @EXPORT = qw(frobnicate);
+ sub frobnicate { my $n = shift; ... }
+ 1;
+
+ package MyScript;
+ use MyUtils "frobnicate" => { -as => "frob" };
+ print frob(42);
+ exit;
+
+=head1 DESCRIPTION
+
+Exporter::Tiny supports many of Sub::Exporter's external-facing features
+including renaming imported functions with the C<< -as >>, C<< -prefix >> and
+C<< -suffix >> options; explicit destinations with the C<< into >> option;
+and alternative installers with the C<< installler >> option. But it's written
+in only about 40% as many lines of code and with zero non-core dependencies.
+
+Its internal-facing interface is closer to Exporter.pm, with configuration
+done through the C<< @EXPORT >>, C<< @EXPORT_OK >> and C<< %EXPORT_TAGS >>
+package variables.
+
+Exporter::Tiny performs most of its internal duties (including resolution
+of tag names to sub names, resolution of sub names to coderefs, and
+installation of coderefs into the target package) as method calls, which
+means they can be overridden to provide interesting behaviour.
+
+=head2 Utility Functions
+
+These are really for internal use, but can be exported if you need them.
+
+=over
+
+=item C<< mkopt(\@array) >>
+
+Similar to C<mkopt> from L<Data::OptList>. It doesn't support all the
+fancy options that Data::OptList does (C<moniker>, C<require_unique>,
+C<must_be> and C<name_test>) but runs about 50% faster.
+
+=item C<< mkopt_hash(\@array) >>
+
+Similar to C<mkopt_hash> from L<Data::OptList>. See also C<mkopt>.
+
+=back
+
+=head1 TIPS AND TRICKS IMPORTING FROM EXPORTER::TINY
+
+For the purposes of this discussion we'll assume we have a module called
+C<< MyUtils >> which exports one function, C<< frobnicate >>. C<< MyUtils >>
+inherits from Exporter::Tiny.
+
+Many of these tricks may seem familiar from L<Sub::Exporter>. That is
+intentional. Exporter::Tiny doesn't attempt to provide every feature of
+Sub::Exporter, but where it does it usually uses a fairly similar API.
+
+=head2 Basic importing
+
+ # import "frobnicate" function
+ use MyUtils "frobnicate";
+
+ # import all functions that MyUtils offers
+ use MyUtils -all;
+
+=head2 Renaming imported functions
+
+ # call it "frob"
+ use MyUtils "frobnicate" => { -as => "frob" };
+
+ # call it "my_frobnicate"
+ use MyUtils "frobnicate" => { -prefix => "my_" };
+
+ # can set a prefix for *all* functions imported from MyUtils
+ # by placing the options hashref *first*.
+ use MyUtils { prefix => "my_" }, "frobnicate";
+ # (note the lack of hyphen before `prefix`.)
+
+ # call it "frobnicate_util"
+ use MyUtils "frobnicate" => { -suffix => "_util" };
+ use MyUtils { suffix => "_util" }, "frobnicate";
+
+ # import it twice with two different names
+ use MyUtils
+ "frobnicate" => { -as => "frob" },
+ "frobnicate" => { -as => "frbnct" };
+
+=head2 Lexical subs
+
+ {
+ use Sub::Exporter::Lexical lexical_installer => { -as => "lex" };
+ use MyUtils { installer => lex }, "frobnicate";
+
+ frobnicate(...); # ok
+ }
+
+ frobnicate(...); # not ok
+
+=head2 Import functions into another package
+
+ use MyUtils { into => "OtherPkg" }, "frobnicate";
+
+ OtherPkg::frobincate(...);
+
+=head2 Import functions into a scalar
+
+ my $func;
+ use MyUtils "frobnicate" => { -as => \$func };
+
+ $func->(...);
+
+=head2 Import functions into a hash
+
+OK, Sub::Exporter doesn't do this...
+
+ my %funcs;
+ use MyUtils { into => \%funcs }, "frobnicate";
+
+ $funcs{frobnicate}->(...);
+
+=head2 DO NOT WANT!
+
+This imports everything except "frobnicate":
+
+ use MyUtils qw( -all !frobnicate );
+
+Negated imports always "win", so the following will not import
+"frobnicate", no matter how many times you repeat it...
+
+ use MyUtils qw( !frobnicate frobnicate frobnicate frobnicate );
+
+=head2 Importing by regexp
+
+Here's how you could import all functions beginning with an "f":
+
+ use MyUtils qw( /^F/i );
+
+Or import everything except functions beginning with a "z":
+
+ use MyUtils qw( -all !/^Z/i );
+
+Note that regexps are always supplied as I<strings> starting with
+C<< "/" >>, and not as quoted regexp references (C<< qr/.../ >>).
+
+=head2 Unimporting
+
+You can unimport the functions that MyUtils added to your namespace:
+
+ no MyUtils;
+
+Or just specific ones:
+
+ no MyUtils qw(frobnicate);
+
+If you renamed a function when you imported it, you should unimport by
+the new name:
+
+ use MyUtils frobnicate => { -as => "frob" };
+ ...;
+ no MyUtils "frob";
+
+Unimporting using tags and regexps should mostly do what you want.
+
+=head1 TIPS AND TRICKS EXPORTING USING EXPORTER::TINY
+
+Simple configuration works the same as L<Exporter>; inherit from this module,
+and use the C<< @EXPORT >>, C<< @EXPORT_OK >> and C<< %EXPORT_TAGS >>
+package variables to list subs to export.
+
+=head2 Generators
+
+Exporter::Tiny has always allowed exported subs to be generated (like
+L<Sub::Exporter>), but until version 0.025 did not have an especially nice
+API for it.
+
+Now, it's easy. If you want to generate a sub C<foo> to export, list it in
+C<< @EXPORT >> or C<< @EXPORT_OK >> as usual, and then simply give your
+exporter module a class method called C<< _generate_foo >>.
+
+ push @EXPORT_OK, 'foo';
+
+ sub _generate_foo {
+ my $class = shift;
+ my ($name, $args, $globals) = @_;
+
+ return sub {
+ ...;
+ }
+ }
+
+You can also generate tags:
+
+ my %constants;
+ BEGIN {
+ %constants = (FOO => 1, BAR => 2);
+ }
+ use constant \%constants;
+
+ $EXPORT_TAGS{constants} = sub {
+ my $class = shift;
+ my ($name, $args, $globals) = @_;
+
+ return keys(%constants);
+ };
+
+=head2 Overriding Internals
+
+An important difference between L<Exporter> and Exporter::Tiny is that
+the latter calls all its internal functions as I<< class methods >>. This
+means that your subclass can I<< override them >> to alter their behaviour.
+
+The following methods are available to be overridden. Despite being named
+with a leading underscore, they are considered public methods. (The underscore
+is there to avoid accidentally colliding with any of your own function names.)
+
+=over
+
+=item C<< _exporter_validate_opts($globals) >>
+
+This method is called once each time C<import> is called. It is passed a
+reference to the global options hash. (That is, the optional leading hashref
+in the C<use> statement, where the C<into> and C<installer> options can be
+provided.)
+
+You may use this method to munge the global options, or validate them,
+throwing an exception or printing a warning.
+
+The default implementation does nothing interesting.
+
+=item C<< _exporter_validate_unimport_opts($globals) >>
+
+Like C<_exporter_validate_opts>, but called for C<unimport>.
+
+=item C<< _exporter_merge_opts($tag_opts, $globals, @exports) >>
+
+Called to merge options which have been provided for a tag into the
+options provided for the exports that the tag expanded to.
+
+=item C<< _exporter_expand_tag($name, $args, $globals) >>
+
+This method is called to expand an import tag (e.g. C<< ":constants" >>).
+It is passed the tag name (minus the leading ":"), an optional hashref
+of options (like C<< { -prefix => "foo_" } >>), and the global options
+hashref.
+
+It is expected to return a list of ($name, $args) arrayref pairs. These
+names can be sub names to export, or further tag names (which must have
+their ":"). If returning tag names, be careful to avoid creating a tag
+expansion loop!
+
+The default implementation uses C<< %EXPORT_TAGS >> to expand tags, and
+provides fallbacks for the C<< :default >> and C<< :all >> tags.
+
+=item C<< _exporter_expand_regexp($regexp, $args, $globals) >>
+
+Like C<_exporter_expand_regexp>, but given a regexp-like string instead
+of a tag name.
+
+The default implementation greps through C<< @EXPORT_OK >> for imports,
+and the list of already-imported functions for exports.
+
+=item C<< _exporter_expand_sub($name, $args, $globals) >>
+
+This method is called to translate a sub name to a hash of name => coderef
+pairs for exporting to the caller. In general, this would just be a hash with
+one key and one value, but, for example, L<Type::Library> overrides this
+method so that C<< "+Foo" >> gets expanded to:
+
+ (
+ Foo => sub { $type },
+ is_Foo => sub { $type->check(@_) },
+ to_Foo => sub { $type->assert_coerce(@_) },
+ assert_Foo => sub { $type->assert_return(@_) },
+ )
+
+The default implementation checks that the name is allowed to be exported
+(using the C<_exporter_permitted_regexp> method), gets the coderef using
+the generator if there is one (or by calling C<< can >> on your exporter
+otherwise) and calls C<_exporter_fail> if it's unable to generate or
+retrieve a coderef.
+
+=item C<< _exporter_permitted_regexp($globals) >>
+
+This method is called to retrieve a regexp for validating the names of
+exportable subs. If a sub doesn't match the regexp, then the default
+implementation of C<_exporter_expand_sub> will refuse to export it. (Of
+course, you may override the default C<_exporter_expand_sub>.)
+
+The default implementation of this method assembles the regexp from
+C<< @EXPORT >> and C<< @EXPORT_OK >>.
+
+=item C<< _exporter_fail($name, $args, $globals) >>
+
+Called by C<_exporter_expand_sub> if it can't find a coderef to export.
+
+The default implementation just throws an exception. But you could emit
+a warning instead, or just ignore the failed export.
+
+If you don't throw an exception then you should be aware that this
+method is called in list context, and any list it returns will be treated
+as an C<_exporter_expand_sub>-style hash of names and coderefs for
+export.
+
+=item C<< _exporter_install_sub($name, $args, $globals, $coderef) >>
+
+This method actually installs the exported sub into its new destination.
+Its return value is ignored.
+
+The default implementation handles sub renaming (i.e. the C<< -as >>,
+C<< -prefix >> and C<< -suffix >> functions. This method does a lot of
+stuff; if you need to override it, it's probably a good idea to just
+pre-process the arguments and then call the super method rather than
+trying to handle all of it yourself.
+
+=item C<< _exporter_uninstall_sub($name, $args, $globals) >>
+
+The opposite of C<_exporter_install_sub>.
+
+=back
+
+=head1 DIAGNOSTICS
+
+=over
+
+=item B<< Overwriting existing sub '%s::%s' with sub '%s' exported by %s >>
+
+A warning issued if Exporter::Tiny is asked to export a symbol which
+will result in an existing sub being overwritten. This warning can be
+suppressed using either of the following:
+
+ use MyUtils { replace => 1 }, "frobnicate";
+ use MyUtils "frobnicate" => { -replace => 1 };
+
+Or can be upgraded to a fatal error:
+
+ use MyUtils { replace => "die" }, "frobnicate";
+ use MyUtils "frobnicate" => { -replace => "die" };
+
+=item B<< Refusing to overwrite existing sub '%s::%s' with sub '%s' exported by %s >>
+
+The fatal version of the above warning.
+
+=item B<< Could not find sub '%s' exported by %s >>
+
+You requested to import a sub which the package does not provide.
+
+=item B<< Cannot provide an -as option for tags >>
+
+Because a tag may provide more than one function, it does not make sense
+to request a single name for it. Instead use C<< -prefix >> or C<< -suffix >>.
+
+=item B<< Passing options to unimport '%s' makes no sense >>
+
+When you import a sub, it occasionally makes sense to pass some options
+for it. However, when unimporting, options do nothing, so this warning
+is issued.
+
+=back
+
+=head1 HISTORY
+
+L<Type::Library> had a bunch of custom exporting code which poked coderefs
+into its caller's stash. It needed this to be something more powerful than
+most exporters so that it could switch between exporting Moose, Mouse and
+Moo-compatible objects on request. L<Sub::Exporter> would have been capable,
+but had too many dependencies for the Type::Tiny project.
+
+Meanwhile L<Type::Utils>, L<Types::TypeTiny> and L<Test::TypeTiny> each
+used the venerable L<Exporter.pm|Exporter>. However, this meant they were
+unable to use the features like L<Sub::Exporter>-style function renaming
+which I'd built into Type::Library:
+
+ ## import "Str" but rename it to "String".
+ use Types::Standard "Str" => { -as => "String" };
+
+And so I decided to factor out code that could be shared by all Type-Tiny's
+exporters into a single place: Exporter::TypeTiny.
+
+As of version 0.026, Exporter::TypeTiny was also made available as
+L<Exporter::Tiny>, distributed independently on CPAN. CHOCOLATEBOY had
+convinced me that it was mature enough to live a life of its own.
+
+As of version 0.030, Type-Tiny depends on Exporter::Tiny and
+Exporter::TypeTiny is being phased out.
+
+=head1 OBLIGATORY EXPORTER COMPARISON
+
+Exporting is unlikely to be your application's performance bottleneck, but
+nonetheless here are some comparisons.
+
+B<< Comparative sizes according to L<Devel::SizeMe>: >>
+
+ Exporter 217.1Kb
+ Sub::Exporter::Progressive 263.2Kb
+ Exporter::Tiny 267.7Kb
+ Exporter + Exporter::Heavy 281.5Kb
+ Exporter::Renaming 406.2Kb
+ Sub::Exporter 701.0Kb
+
+B<< Performance exporting a single sub: >>
+
+ Rate SubExp ExpTiny SubExpProg ExpPM
+SubExp 2489/s -- -56% -85% -88%
+ExpTiny 5635/s 126% -- -67% -72%
+SubExpProg 16905/s 579% 200% -- -16%
+ExpPM 20097/s 707% 257% 19% --
+
+(Exporter::Renaming globally changes the behaviour of Exporter.pm, so could
+not be included in the same benchmarks.)
+
+B<< (Non-Core) Dependencies: >>
+
+ Exporter -1
+ Exporter::Renaming 0
+ Exporter::Tiny 0
+ Sub::Exporter::Progressive 0
+ Sub::Exporter 3
+
+B<< Features: >>
+
+ ExpPM ExpTiny SubExp SubExpProg
+ Can export code symbols............. Yes Yes Yes Yes
+ Can export non-code symbols......... Yes
+ Groups/tags......................... Yes Yes Yes Yes
+ Export by regexp.................... Yes Yes
+ Bang prefix......................... Yes Yes
+ Allows renaming of subs............. Yes Yes Maybe
+ Install code into scalar refs....... Yes Yes Maybe
+ Can be passed an "into" parameter... Yes Yes Maybe
+ Can be passed an "installer" sub.... Yes Yes Maybe
+ Config avoids package variables..... Yes
+ Supports generators................. Yes Yes
+ Sane API for generators............. Yes Yes
+ Unimport............................ Yes
+
+(Certain Sub::Exporter::Progressive features are only available if
+Sub::Exporter is installed.)
+
+=head1 BUGS
+
+Please report any bugs to
+L<http://rt.cpan.org/Dist/Display.html?Queue=Exporter-Tiny>.
+
+=head1 SUPPORT
+
+B<< IRC: >> support is available through in the I<< #moops >> channel
+on L<irc.perl.org|http://www.irc.perl.org/channels.html>.
+
+=head1 SEE ALSO
+
+L<Exporter::Shiny>,
+L<Sub::Exporter>,
+L<Exporter>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENCE
+
+This software is copyright (c) 2013-2014 by Toby Inkster.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=head1 DISCLAIMER OF WARRANTIES
+
+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+