crafting custom interfaces with sub::exporter

Post on 11-Jun-2015

6.046 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Everybody knows about Exporter.pm: you use it, and if someone uses your module, they don't have to type quite as much. We'll look at how the Exporter works, and how it fails to take advantage of the powerful concepts on which it's built. We'll see how you can provide flexible import routines that allow your module's user to type even less and get code that behaves much more like part of his own program. You can avoid repeating unnecessary parameters to every overly-generic routine and can avoid collision-prone global configuration. All of this is made possible -- and easy -- by Sub::Exporter.Generators -- routines that build routines -- can produce customized code, built to each importer's specifications. Sub::Exporter lets you build and provide customized routines easily. You'll learn how to write generators, and how to use them with Sub::Exporter . In its simplest form, it's as easy to use as Exporter.pm. With just a bit more configuration, it can build, group, rename, and julienne routines easily. With this tool, you'll be able to provide interfaces that are both simpler and more powerful than those provided by the stock Exporter.

TRANSCRIPT

Sub::ExporterCrafting Custom Interfaces

rjbs@cpan.orgRicardo 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!

top related