Sub::ExporterCrafting Custom Interfaces
[email protected] SIGNES
What’s an Exporter?
•Something that takes care of the annoying details of importing.
What is Importing?
What is Importing?
•Build it over there, then bring it here.
What is Importing?
•Build it over there, then bring it here.
•For our purposes, “it” is code.
Why Do We Import?
Why Do We Import?
•We want someone else to do the hard, boring work.
Why Do We Import?
•We want someone else to do the hard, boring work.
•And we want it done cheap.
sub strftime { my($pkg,$fmt,$time); ($pkg,$fmt,$time,$tzname) = @_;
my $me = ref($pkg) ? $pkg : bless [];
if(defined $tzname) { $tzname = uc $tzname;
$tzname = sprintf(“%+05d”,$tzname) unless($tzname =~ /\D/);
$epoch = timegm(@{$time}[0..5]);
@$me = gmtime($epoch + tz_offset($tzname) - tz_offset()); } else { @$me = @$time; undef $epoch; }
_subs($me,$fmt); }
Date::Format::strftime
strftime
How Importing Works
How Importing Works
•the client use-s a module
How Importing Works
•the client use-s a module
• the module’s import method is called
How Importing Works
•the client use-s a module
• the module’s import method is called
• something ugly happens
How Importing Works
•the client use-s a module
• the module’s import method is called
• something ugly happens
• the client has more named subs
How Importing Works
•usually that ugliness is Exporter.pm
How Importing Works
•usually that ugliness is Exporter.pm
# the dark and twisted heart of Exporter.pm *{“${callpkg}::$sym”} = \&{“${pkg}::$sym”};
*{"$::$"} = \&{"$::$"};
•the caller gets the same code
•with the same name
*{"$::$"} = \&{"$::$"};
•Exporter.pm churns out identically named and constructed products.
The Factory Model
The Factory Model
•One size fits all
The Factory Model
•One size fits all
• If it doesn’t fit your code, adjust your code.
The Factory Model
•One size fits all
• If it doesn’t fit your code, adjust your code.
•Or abuse the Exporter
The Factory Model
•There’s Only One Way To Do It
The Tool Metaphor
The Tool Metaphor
•“You can’t write good code without good tools.”
The Tool Metaphor
•“You can’t write good code without good tools.”
•Exporters are tools for making tools.
The Tool Metaphor
•“You can’t write good code without good tools.”
•Exporters are tools for making tools.
•Their quality has an impact all the way down the line.
Craftsman Tools
Craftsman Tools
•We want adaptable tools, customized for our current needs.
Craftsman Tools
•We want adaptable tools, customized for our current needs.
•We want tools hand-crafted to our specifications.
Craftsman Tools
•We want adaptable tools, customized for our current needs.
•We want tools hand-crafted to our specifications.
•We want to reduce our labor by having someone else do the boring work.
Sub::Exporter!
Basic Exporting
The Basics
•String::Truncate
• trunc: truncate a string to length
•elide: truncate, ending in “...”
String::Truncate
String::Truncate
$string = “This string is 34 characters long.”;
String::Truncate
$string = “This string is 34 characters long.”;
trunc($string, 10); # This strin
String::Truncate
$string = “This string is 34 characters long.”;
trunc($string, 10); # This strin
elide($string, 10); # This st...
Basic Exports
use String::Truncate qw(elide trunc);
To let your client write:
Basic Exports
package String::Truncate;
use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], };
Basic Groups
use String::Truncate qw(:all)
To let your client write:
Basic Groups
package String::Truncate;
use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], };
Basic Groups
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { all => [qw(elide trunc)] }, };
Basic Groups
use String::Truncate qw(:basic)
To let your client write:
Basic Groups
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [qw(elide trunc)] } };
Basic Defaults
use String::Truncate; # imports “trunc”
To let your client write:
Basic Defaults
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
package String::Truncate;
use Exporter; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@ISA = qw(Exporter); @EXPORT = qw(trunc); @EXPORT_OK = qw(elide trunc); %EXPORT_TAGS = ( all => \@EXPORT_OK, basic => [ qw(elide trunc) ], );
Using Exporter.pm
Renaming Exports
Renaming Exports
•avoid namespace collisions
Renaming Exports
use CGI qw(:standard); use LWP::Simple;
my $head = head(...); # what happens?
Renaming Exports
•avoid unclear or ambiguous names
Renaming Exports
use File::Basename; use XML::Parser;
my $file = fileparse($ARGV[0]);
$parser->parsefile($file);
Renaming Exports
•“privatize” subs imported to classes
Renaming Exports
package XML::Parser::Hypothetical; use File::Basename;
Renaming Exports
use Exporter::Renaming; use String::Truncate qw(elide), Renaming => [ trunc => ‘trunc_str’ ]; no Exporter::Renaming;
Using Exporter::Renaming
Renaming Exports
use String::Truncate qw(trunc), trunc => { -as => ‘trunc_str’ };
To let your client write:
Renaming Exports
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
Wait a second...
use String::Truncate qw(trunc), trunc => { -as => ‘trunc_str’ };
Data::OptListQuick and Dirty Data Structures
Data::OptList
@optlist = ( qw(alfa bravo), charlie => [ 0, 1, 2 ], delta => { a => 1 }, ‘echo’, foxtrox => undef, ‘gulf’,
);
Data::OptList
@optlist = ( qw(alfa bravo), charlie => [ 0, 1, 2 ], delta => { a => 1 }, ‘echo’, foxtrox => undef, ‘gulf’,
);
$as_href = { alfa => undef, bravo => undef, charlie => [ 0, 1, 2], delta => { a => 1 }, echo => undef, foxtrot => undef, gulf => undef, ];
Data::OptList
@optlist = ( qw(alfa bravo), charlie => [ 0, 1, 2 ], delta => { a => 1 }, ‘echo’, foxtrox => undef, ‘gulf’,
);
$as_aref = [ [ alfa => undef ], [ bravo => undef ], [ charlie => [0,1,2] ], [ delta => {a => 1}], [ echo => undef ], [ foxtrot => undef ], [ gulf => undef ], ];
Data::OptList
@optlist = ( qw(aye aye) love => [ qw(chex) ], love => [ qw(milk) ], aye => { sir => ‘!’ }, );
Data::OptList
@optlist = ( qw(aye aye) love => [ qw(chex) ], love => [ qw(milk) ], aye => { sir => ‘!’ }, );
$as_aref = [ [ aye => undef ], [ aye => undef ], [ love => [qw(chex)] ], [ love => [qw(milk)] ], [ aye => {sir => ‘!’}] ];
Data::OptList
$as_href = die “...”;
@optlist = ( qw(aye aye) love => [ qw(chex) ], love => [ qw(milk) ], aye => { sir => ‘!’ }, );
Customizing Exports
Subclassed Exporters
Subclassed Exporters
• import is a method
Subclassed Exporters
• import is a method
• that implies that exporters are classes
Subclassed Exporters
• import is a method
• that implies that exporters are classes
•and that we can subclass them
package String::Truncate::Split; use base qw(String::Truncate);
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
Subclassed Exporters
Subclassed Exporters
• *{“$::$”} = \&{“$::$”};
Subclassed Exporters
• *{“$::$”} = \&{“$::$”};
• @EXPORT has to be defined in the derived class
Subclassed Exporters
• *{“$::$”} = \&{“$::$”};
• @EXPORT has to be defined in the derived class
• the export has to be defined in the exporting package
package String::Truncate::Split; use base qw(String::Truncate);
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
1;
package String::Truncate::Split; use base qw(String::Truncate);
our @EXPORT_OK = @String::Truncate::EXPORT_OK; our @EXPORT = @String::Truncate::EXPORT; our %EXPORT_TAGS = %String::Truncate::EXPORT_TAGS;
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
package String::Truncate::Split; use base qw(String::Truncate);
our @EXPORT_OK = @String::Truncate::EXPORT_OK; our @EXPORT = @String::Truncate::EXPORT; our %EXPORT_TAGS = %String::Truncate::EXPORT_TAGS;
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
*$_ = \&{“String::Truncate::$_”} for grep { not defined &{__PACKAGE__.“::$_”} } @EXPORT;
package String::Truncate::Split; use base qw(String::Truncate);
our @EXPORT_OK = @String::Truncate::EXPORT_OK; our @EXPORT = @String::Truncate::EXPORT; our %EXPORT_TAGS = %String::Truncate::EXPORT_TAGS;
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
do { no strict ‘refs’; *$_ = \&{“String::Truncate::$_”} for grep { not defined &{__PACKAGE__.“::$_”} } @EXPORT; }
Subclassed Exporters
Subclassed Exporters
•Sub::Exporter finds exports with “can”
Subclassed Exporters
•Sub::Exporter finds exports with “can”
• this means you can subclass exporting toolkits, replacing just pieces
package String::Truncate::Split; use base qw(String::Truncate);
sub trunc { my ($string, $length) = @_;
# ...
return ($head, $tail); }
Customizing Exports
Customizing Exports
•What if you want trunc to work differently when you use it?
Customizing Exports
•Some modules do this with package variables.
Package-Level Config
use String::Truncate qw(:all); $String::Truncate::DEFAULT_LENGTH = 20; $String::Truncate::DEFAULT_MARKER = “--”;
Package-Level Config
use String::Truncate qw(:all); $String::Truncate::DEFAULT_LENGTH = 20; $String::Truncate::DEFAULT_MARKER = “--”;
use Tools::Useful;
Package-Level Config
use String::Truncate (); use Tools::Useful;
sub trunc { my ($string, $length) = @_; $length = 20 if not defined $length; String::Truncate::trunc($string, $length) }
Package-Level Config
use String::Truncate (); use Tools::Useful;
sub trunc { local $String::Truncate::DEFAULT_LENGTH = 20; String::Truncate::trunc(@_); }
Custom Imports
use String::Truncate qw(trunc), elide => { -as => ‘trail_off’, marker => ‘etc’, };
Custom Imports
use String::Truncate qw(trunc elide), elide => { -as => ‘trail_off’, marker => ‘etc’, };
Custom Imports
use String::Truncate trunc => { -as => ‘trunc_str’, length => 10 }, elide => { -as => ‘elide_str’, length => 10 };
Custom Imports
use String::Truncate -all => { -suffix => ‘_str’, length => 10 };
Exports to Order
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
Exports to Order
package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
Exports to Order package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => undef, trunc => undef, ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
Exports to Order package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => \’_build_elide’, trunc => \’_build_trunc’, ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
Generating Routines
Generating Routines
sub _build_trunc {
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_;
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub {
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_;
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length;
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest);
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest); }
Generating Routines
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest); } }
Routines ex nihilo
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
$cyphertext
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
$cyphertext = $encyph->(“Top secret message.”);
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
$cyphertext = $encyph->(“Top secret message.”);
sub encypher {
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
$cyphertext = $encyph->(“Top secret message.”);
sub encypher { my $text = shift; $encyph->($text);
Routines ex nihilo
use Cypher::Trivial qw(cyphers);
my ($encyph, $decyph) = cyphers(“secret”);
$cyphertext = $encyph->(“Top secret message.”);
sub encypher { my $text = shift; $encyph->($text); }
Routines ex nihilo
Routines ex nihilo
use Cypher::Trivial
Routines ex nihilo
use Cypher::Trivial encypher => { secret => “secret” };
Routines ex nihilo
use Cypher::Trivial encypher => { secret => “secret” };
encypher(“Top secret message”);
Routines ex nihilo sub _build_encypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $enc; }
sub _build_decypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $dec; }
Routines ex nihilo sub _build_encypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $enc; }
sub _build_decypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $dec; }
Routines ex nihilo package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => \’_build_encypher’, decypher => \’_build_decypher’, cyphers => undef, ], groups => { cyphers => [ qw(encypher decypher) ], } };
Routines ex nihilo
use Cypher::Trivial encypher => { secret => “secret” };
encypher(“Top secret message”);
Routines ex nihilo
use Cypher::Trivial -cyphers => { secret => “secret” };
encypher(“Top secret message”); decypher(“Gbc frperg zrffntr”);
Generating Groups package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => \’_build_encypher’, decypher => \’_build_decypher’, cyphers => undef, ], groups => { cyphers => [ qw(encypher decypher) ], } };
Generating Groups sub _build_encypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $enc; }
sub _build_decypher { my ($class, $name, $arg) = @_; my ($enc, $dec) = cyphers($arg->{secret}); return $dec; }
Generating Groups package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => \’_build_encypher’, decypher => \’_build_decypher’, cyphers => undef, ], groups => { cyphers => \’_build_cyphers’, } };
Generating Groups
Generating Groups
sub _build_cyphers {
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
my %sub;
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
my %sub; @sub{qw(encypher decypher)}
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret});
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret});
return \%sub;
Generating Groups
sub _build_cyphers { my ($class, $name, $arg) = @_;
my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret});
return \%sub; }
Generating Groups
use Cypher::Trivial -cyphers => { secret => “secret” };
Generating Groups
use Cypher::Trivial -cyphers => { secret => “secret” },
-cyphers => { secret => ‘Secret1234’, -suffix => ‘_strong’ } ;
Exporting Methods
ZOMG O NO!
Methods & Exporter.pm
Methods & Exporter.pm
•Exporter.pm: “Do not export method names!”
Methods & Exporter.pm
•Exporter.pm: “Do not export method names!”
• *{“$::$”} = \&{“$::$”};
package Object::Hybrid; use Exporter; @Object::Exporter::ISA = qw(Exporter);
@Object::Exporter::EXPORT_OK = qw(retrieve);
sub retrieve { my ($class, $id) = @_; my $row = $class->get_row(id => $id); bless $row => $class; }
Methods & Exporter.pm
use Object::Hybrid qw(retrieve);
my $object = retrieve(42); my $object = retrieve(49);
Methods & Exporter.pm
package Object::Hybrid; use Exporter; @Object::Exporter::ISA = qw(Exporter);
@Object::Exporter::EXPORT_OK = qw(object);
sub retrieve { my ($class, $id) = @_; my $row = $class->get_row(id => $id); bless $row => $class; }
sub object { __PACKAGE__->retrieve(@_) }
Methods & Exporter.pm
use Object::Hybrid qw(object);
my $object = object(42); my $object = object(49);
Methods & Exporter.pm
use Object::Hybrid;
my $object = Object::Hybrid->object(42);
Methods & Exporter.pm
Methods & Exporter.pm
use Object::Hybrid;
Methods & Exporter.pm
use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
Methods & Exporter.pm
use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
my $object = Object::Hybrid->retrieve(42);
Methods & Exporter.pm
use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
my $object = Object::Hybrid->retrieve(42);
my $thing =
Methods & Exporter.pm
use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
my $object = Object::Hybrid->retrieve(42);
my $thing = Object::Hybrid::With::Much::Derivation
Methods & Exporter.pm
use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
my $object = Object::Hybrid->retrieve(42);
my $thing = Object::Hybrid::With::Much::Derivation ->retrieve(49);
Methods & Exporter.pm
package Object::Hybrid; use Exporter; @Object::Exporter::ISA = qw(Exporter);
@Object::Exporter::EXPORT_OK = qw(object);
sub retrieve { my ($class, $id) = @_; my $row = $class->get_row(id => $id); bless $row => $class; }
sub object { __PACKAGE__->retrieve(@_) }
Methods & Exporter.pm
Currying Methods
use Object::Hybrid qw(object);
use Object::Hybrid::With::Much::Derivation object => { -as => ‘derived_object’ };
my $object = object(42); my $thing = derived_object(49);
Currying Methods
use Object::Hybrid qw(object);
my $object = Object::Hybrid->object(49);
Currying Methods
Currying Methods
Currying Methods
use Sub::Exporter -setup => {
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ],
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ], };
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ], };
sub _build_object {
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ], };
sub _build_object { my ($class, $name, $arg) = @_;
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ], };
sub _build_object { my ($class, $name, $arg) = @_;
return sub { $class->new(@_); }
Currying Methods
use Sub::Exporter -setup => { exports => [ object => \&_build_object ], };
sub _build_object { my ($class, $name, $arg) = @_;
return sub { $class->new(@_); } }
Currying Methods
use Sub::Exporter -setup => { exports => [ object => curry_class(‘new’) ], }
Currying Methods
use Sub::Exporter::Util qw(curry_class); use Sub::Exporter -setup => { exports => [ object => curry_class(‘new’) ], }
Exporting Methods
Exporting Methods
•Sometimes you want to export methods without currying the class.
Exporting Methods
•Sometimes you want to export methods without currying the class.
•Exporters can serve as method crafters.
Exporting Methods package Mixin::Dumper;
use Sub::Exporter -setup => { exports => [ qw(dump) ], groups => { default => [ qw(dump) ] }, };
sub dump { my ($self) = @_; require Data::Dumper; Data::Dumper::Dumper($self); }
Exporting Methods
package Email::Simple::mixin:ReplyText;
use Sub::Exporter -setup => { exports => [ qw(reply_text) ], groups => { defaults => [ qw(reply_text) ] }, };
sub reply_text { my ($self) = @_; join “\n”, map “>$_”, split /\n/, $self->body; }
Exporting Methods package Email::Simple::mixin:ReplyText;
use Sub::Exporter -setup => { into => ‘Email::Simple’, exports => [ qw(reply_text) ], groups => { defaults => [ qw(reply_text) ] }, };
sub reply_text { my ($self) = @_; join “\n”, map “>$_”, split /\n/, $self->body; }
Exporting Methods
use Email::Simple; use Email::Simple::mixin::ReplyText;
Exporting Methods
use Email::Simple; use Email::Simple::mixin::ReplyText;
use Email::Simple::Mock; use Email::Simple::mixin::ReplyText { into => ‘Email::Simple::Mock’ };
Emulating mixin.pm
Emulating mixin.pm
•Don’t import into my namespace...
Emulating mixin.pm
•Don’t import into my namespace...
• ...import to a new namespace...
Emulating mixin.pm
•Don’t import into my namespace...
• ...import to a new namespace...
• ...and add it to my @ISA.
Emulating mixin.pm
Emulating mixin.pm
•This makes it easy to import a chunk of methods and override just a few...
Emulating mixin.pm
•This makes it easy to import a chunk of methods and override just a few...
• ...and those few can call SUPER.
Emulating mixin.pm package Email::Simple::mixin:ReplyText;
use Sub::Exporter -setup => { into => ‘Email::Simple’, exports => [ qw(reply_text) ], groups => { defaults => [ qw(reply_text) ] }, };
sub reply_text { my ($self) = @_; join “\n”, map “>$_”, split /\n/, $self->body; }
Emulating mixin.pm package Email::Simple::mixin:ReplyText;
use Sub::Exporter -setup => { into => ‘Email::Simple’, exporter=> mixin_exporter, exports => [ qw(reply_text) ], groups => { defaults => [ qw(reply_text) ] }, };
sub reply_text { my ($self) = @_; join “\n”, map “>$_”, split /\n/, $self->body; }
Collectors
Collectors
Collectors
•Arguments that don’t export anything.
Collectors
•Arguments that don’t export anything.
•They collect data for generators to use.
Collectors
package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => \’_build_elide’, trunc => \’_build_trunc’, ], collectors => [ qw(defaults) ], };
Collectors
Collectors
use String::Truncate
Collectors
use String::Truncate defaults => { length => 10 },
Collectors
use String::Truncate defaults => { length => 10 },
qw(-all),
Collectors
use String::Truncate defaults => { length => 10 },
qw(-all), trunc => { length => 1, -as => ‘onechar’ },
Collectors
use String::Truncate defaults => { length => 10 },
qw(-all), trunc => { length => 1, -as => ‘onechar’ }, elide => { marker => ‘&c’, -as => ‘yul’ },
Collectors
use String::Truncate defaults => { length => 10 },
qw(-all), trunc => { length => 1, -as => ‘onechar’ }, elide => { marker => ‘&c’, -as => ‘yul’ }, ;
Collectors
sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest); } }
Collectors
sub _build_trunc { my ($class, $name, $arg, $col) = @_; my $_length = $arg->{length};
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest); } }
Collectors sub _build_trunc { my ($class, $name, $arg, $col) = @_; my $_length = $arg->{length};
$_length = $col->{defaults}{length} if !defined $_length;
return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length; trunc($string, $length, @rest); } }
Collectors
Collectors
•Arguments that don’t export.
•They collect data for generators to use.
Collectors
•Arguments that don’t export.
•They collect data for generators to use.
•They can validate the collected data.
Collectors
package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => \’_build_elide’, trunc => \’_build_trunc’, ], collectors => { defaults => \’_validate_defaults’, }, };
Collectors
sub _validate_defaults { my ($class, $value, $data) = @_; return (ref $value eq ‘HASH’); }
Collectors
Collectors
•Arguments that don’t export.
•They collect data for generators to use.
•They can validate the collected data.
Collectors
•Arguments that don’t export.
•They collect data for generators to use.
•They can validate the collected data.
•They can do Almost Anything Else.
Collectors
sub _validate_defaults { my ($class, $value, $data) = @_; return (ref $value eq ‘HASH’); }
Collectors
Collectors
•name - name of the collection
Collectors
•name - name of the collection
•class - invocant of import method
Collectors
•name - name of the collection
•class - invocant of import method
•config - exporter configuration
Collectors
•name - name of the collection
•class - invocant of import method
•config - exporter configuration
• into - the package that’s importing
Collectors
•name - name of the collection
•class - invocant of import method
•config - exporter configuration
• into - the package that’s importing
• import_args - args to import method
Collectors
Collectors
•name - the name of the collection
Collectors
•name - the name of the collection
•class - import’s invocant
Collectors
Collectors
•config - the Sub::Exporter config
Collectors
•config - the Sub::Exporter config
• find out what exports exist
Collectors
•config - the Sub::Exporter config
• find out what exports exist
• validate collection value based on config
use LWP::Simple “/^is_/”;
is_success($res); is_failure($res);
use LWP::Simpleton;
use Sub::Exporter -setup => { collectors => { like => Sub::Exporter::Util::like }, };
use LWP::Simple like => qr/^is_/;
is_success($res); is_failure($res);
use LWP::Simple like => [ qr/^is_/, undef, qr/^get/, { -prefix => ‘https_’, ssl => 1 } ];
is_success($res); is_failure($res);
https_get(“https://codesimply.com”)
Collectors
Collectors
• into - the target to which exports go
Collectors
• into - the target to which exports go
•alter the class directly
Collectors
• into - the target to which exports go
•alter the class directly
•particularly useful: @ISA
sub _make_base { my ($class, $value, $data) = @_;
my $target = $data->{into}; push @{“$target\::ISA”}, $class; }
sub _make_base { my ($class, $value, $data) = @_;
my $target = $data->{into}; push @{“$target\::ISA”}, $class; }
use Sub::Exporter -setup => { collectors => { base => \’_make_base’ }, };
sub _make_base { my ($class, $value, $data) = @_;
my $target = $data->{into}; push @{“$target\::ISA”}, $class; }
use Sub::Exporter -setup => { collectors => { base => \’_make_base’ }, };
use Magic::Superclass -base;
package Email::Constants;
sub _set_constants { my ($class, $value, $data) = @_;
Package::Generator->assign_symbols( $data->{into}, [ EX_TEMPFAIL => 75, FORMATS => [ qw(Maildir mbox mh) ], ], ); }
package Email::Constants;
sub _set_constants { my ($class, $value, $data) = @_;
Package::Generator->assign_symbols( $data->{into}, [ EX_TEMPFAIL => 75, FORMATS => [ qw(Maildir mbox mh) ], ], ); }
use Sub::Exporter -setup => { collectors => { constants => \’_set_constants’ }, };
use Email::Constants qw(constants);
Collectors
Collectors
• import_args - the arguments to import
Collectors
• import_args - the arguments to import
• rewrite the arguments list
Collectors
• import_args - the arguments to import
• rewrite the arguments list
• add new imports
sub _setup {
sub _setup { my ($class, $value, $data) = @_;
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) {
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} },
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ];
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) {
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} },
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => {
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, exports => $value } ];
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, exports => $value } ]; return 1;
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, exports => $value } ]; return 1; }
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, exports => $value } ]; return 1; } return;
sub _setup { my ($class, $value, $data) = @_;
if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
} elsif (ref $value eq ‘ARRAY’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, exports => $value } ]; return 1; } return; }
use Sub::Exporter -setup => {
use Sub::Exporter -setup => { collectors => { -setup => \’_setup’ },
use Sub::Exporter -setup => { collectors => { -setup => \’_setup’ }, exports => [ _import => \’_build_import’ ],
use Sub::Exporter -setup => { collectors => { -setup => \’_setup’ }, exports => [ _import => \’_build_import’ ], });
-setup => { into_level => 2, exports => [qw(foo)] }
-setup => { into_level => 2, exports => [qw(foo)] }
_import => { -as => ‘import’, into_level => 2, exports => [qw(foo)] }
-setup => [ qw(foo bar baz) ]
-setup => [ qw(foo bar baz) ]
_import => { -as => ‘import’, exports => [qw(foo bar baz)] }
use Sub::Exporter -setup => { collectors => { -setup => \’_setup’ }, exports => [ _import => \’_build_import’ ], });
use Sub::Exporter -setup => { collectors => { -setup => \’_setup’ }, exports => [ _import => sub { my ($class, $name, $arg) = @_; build_exporter($arg); }, ], });
package Sub::Exporter;
use Sub::Exporter -setup => { collectors => { -setup => \&_setup }, exports => [ _import => sub { my ($class, $name, $arg) = @_; build_exporter($arg); }, ], });
RJBS’s Advice
RJBS’s Advice
•Write the client code first.
RJBS’s Advice
•Write the client code first.
•Make as many assumptions as possible.
RJBS’s Advice
•Write the client code first.
•Make as many assumptions as possible.
• Let most of them be refuted.
Any Questions?
Random Tricks
Mixed-in Helpers
$object->complex_method($arg);
Mixed-in Helpers
sub _build_cplx_method { my ($mixin) = @_; sub { my ($self, $arg) = @_; $mixin->validate_arg($arg); $mixin->do_stuff($self, $arg); return $mixin->analyze($self); }}
sub validate_arg {...}
Mixed-in Helpers
package Mixin::Helper;
use Sub::Exporter -setup => { exports => [ complex_method => \’_build_cplx_method’, ],};
sub _build_cplx_method { ...
Mixed-in Helpers
sub _build_cplx_method { my ($mixin) = @_; sub { my ($self, $arg) = @_; $mixin->validate_arg($arg); $mixin->do_stuff($self, $arg); return $mixin->analyze($self); }}
sub validate_arg {...}
Mixed-in Helpers
package Mixin::Helper::Faster;use base qw(Mixin::Helper);
sub analyze { my ($mixin, $object) = @_; return 1;}
1;
A Coderef Generator
A Coderef Generator
use String::Truncate ();
A Coderef Generator
use String::Truncate ();
my $trunc;
A Coderef Generator
use String::Truncate ();
my $trunc; String::Truncate->import(
A Coderef Generator
use String::Truncate ();
my $trunc; String::Truncate->import(trunc =>
A Coderef Generator
use String::Truncate ();
my $trunc; String::Truncate->import(trunc => { -as => \$trunc });
package YAPC::Slideshow; use Accessors::Simple -setup => { fields => [ qw(topic presenter timeslot room) ], };
Accessors sans ISA
Accessors sans ISA
sub _make_accessor {
Accessors sans ISA
sub _make_accessor { my ($field) = @_;
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub {
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift;
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_;
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field};
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; }
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors {
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} };
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} }; my %sub = map { $_ => _make_accessor($_) } @fields;
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} }; my %sub = map { $_ => _make_accessor($_) } @fields; return \%sub;
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} }; my %sub = map { $_ => _make_accessor($_) } @fields; return \%sub; }
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} }; my %sub = map { $_ => _make_accessor($_) } @fields; return \%sub; }
use Sub::Exporter -setup =>
Accessors sans ISA
sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
sub _make_many_accessors { my @fields = @{ $arg->{fields} }; my %sub = map { $_ => _make_accessor($_) } @fields; return \%sub; }
use Sub::Exporter -setup => { groups => { setup => \&_make_many_accessors } };
Accessors sans ISA
Eat Exporter’s Brain
sub exporter_upgrade {
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_;
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’,
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg,
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ],
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => {
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”},
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ],
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], },
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class;
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class; return $new_pkg;
Eat Exporter’s Brain
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class; return $new_pkg; }
Eat Exporter’s Brain
package UNIVERSAL;
package UNIVERSAL;
sub exporter_upgrade {
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_;
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’,
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg,
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ],
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => {
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”},
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ],
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], },
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class;
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class; return $new_pkg;
package UNIVERSAL;
sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg\::SE”;
return $new_pkg if $new_pkg->isa($pkg);
Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg, exports => [ @{“$pkg\::EXPORT_OK”} ], groups => { %{“$pkg\::EXPORT_TAGS”}, default => [ @{“$pkg\::EXPORTS”} ], }, });
push @{“$new_pkg\::ISA”}, $class; return $new_pkg; }
Fixing caller
Fixing caller
sub default_exporter {
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into)
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install(
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install( _generate($class, $generator, $name, $arg, $col),
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install( _generate($class, $generator, $name, $arg, $col), $into,
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install( _generate($class, $generator, $name, $arg, $col), $into, $as,
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install( _generate($class, $generator, $name, $arg, $col), $into, $as, );
Fixing caller
sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
_install( _generate($class, $generator, $name, $arg, $col), $into, $as, ); }
sub evil_eval_exporter { # TOTALLY UNTESTED!
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into)
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do {
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g;
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”};
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install(
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install( $col->{_g}($class, $generator, $name, $arg, $col),
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install( $col->{_g}($class, $generator, $name, $arg, $col), $into,
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install( $col->{_g}($class, $generator, $name, $arg, $col), $into, $as,
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install( $col->{_g}($class, $generator, $name, $arg, $col), $into, $as, );
sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(\&_generate)->Names(‘GEN’)->Out; $g =~ s/\A\$GEN = sub/sub _generate/; $g = “package $into;\n$g”; eval $g; \&{“$into\::_generate”}; };
_install( $col->{_g}($class, $generator, $name, $arg, $col), $into, $as, ); }
Thank You!