Sub::Exporter
 Crafting Custom Interfaces
rjbs
@cpan.org
Ricardo 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(define...
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 cli...
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}::...
*{quot;$::$quot;} = &{quot;$::$quot;};




• the caller gets the same code
• with the same name
*{quot;$::$quot;} = &{quot;$::$quot;};




• 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 qua...
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
  spec...
Craftsman Tools

• We want adaptable tools, customized
  for our current needs.
• We want tools hand-crafted to our
  spec...
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); #...
Basic Exports

To let your client write:

 use String::Truncate qw(elide trunc);
Basic Exports

package String::Truncate;

use Sub::Exporter -setup => {
   exports => [ qw(elide trunc) ],
};
Basic Groups

To let your client write:

 use String::Truncate qw(:all)
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 => { a...
Basic Groups

To let your client write:

 use String::Truncate qw(:basic)
Basic Groups

package String::Truncate;

use Sub::Exporter -setup => {
   exports => [ qw(elide trunc) ],
   groups => { b...
Basic Defaults

To let your client write:

 use String::Truncate; # imports “trunc”
Basic Defaults

package String::Truncate;

use Sub::Exporter -setup => {
   exports => [ qw(elide trunc) ],
   groups => {...
Using Exporter.pm
package String::Truncate;

use Exporter;
use vars
  qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);

@ISA = qw...
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
Using Exporter::Renaming
use Exporter::Renaming;
use String::Truncate
 qw(elide),
 Renaming => [ trunc =>...
Renaming Exports

To let your client write:

 use String::Truncate
  qw(trunc), trunc => { -as => ‘trunc_str’ };
Renaming Exports

package String::Truncate;

use Sub::Exporter -setup => {
   exports => [ qw(elide trunc) ],
   groups =>...
Wait a second...


use String::Truncate
 qw(trunc), trunc => { -as => ‘trunc_str’ };
Data::OptList
Quick and Dirty Data Structures
Data::OptList

@optlist = (
  qw(alfa bravo),
  charlie => [ 0, 1, 2 ],
  delta   => { a => 1 },
  ‘echo’,
  foxtrox => un...
Data::OptList

                            $as_href = {
@optlist = (
                             alfa => undef,
  qw(alfa...
Data::OptList

                            $as_aref = [
@optlist = (
                             [ alfa => undef ],
  qw(...
Data::OptList

@optlist = (
   qw(aye aye)
   love => [ qw(chex) ],
   love => [ qw(milk) ],
   aye => { sir => ‘!’ },
);
Data::OptList

                            $as_aref = [
@optlist = (
                               [ aye => undef ],
   q...
Data::OptList

@optlist = (
   qw(aye aye)
   love => [ qw(chex) ],
                            $as_href = die “...”;
   l...
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) = @_;

    # ...

   ...
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 d...
package String::Truncate::Split;
use base qw(String::Truncate);

sub trunc {
  my ($string, $length) = @_;

    # ...

   ...
package String::Truncate::Split;
use base qw(String::Truncate);

our   @EXPORT_OK = @String::Truncate::EXPORT_OK;
our   @E...
package String::Truncate::Split;
use base qw(String::Truncate);

our   @EXPORT_OK = @String::Truncate::EXPORT_OK;
our   @E...
package String::Truncate::Split;
use base qw(String::Truncate);

our   @EXPORT_OK = @String::Truncate::EXPORT_OK;
our   @E...
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, replac...
package String::Truncate::Split;
use base qw(String::Truncate);

sub trunc {
  my ($string, $length) = @_;

    # ...

   ...
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_M...
Package-Level Config

use String::Truncate qw(:all);
$String::Truncate::DEFAULT_LENGTH = 20;
$String::Truncate::DEFAULT_MA...
Package-Level Config

use String::Truncate ();
use Tools::Useful;

sub trunc {
  my ($string, $length) = @_;
  $length = 2...
Package-Level Config

use String::Truncate ();
use Tools::Useful;

sub trunc {
  local $String::Truncate::DEFAULT_LENGTH
 ...
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’, leng...
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 =>...
Exports to Order

package String::Truncate;

use Sub::Exporter -setup => {
   exports => [ qw(elide trunc) ],
   groups =>...
Exports to Order
package String::Truncate;

use Sub::Exporter -setup => {
   exports => [
      elide => undef,
      trun...
Exports to Order
package String::Truncate;

use Sub::Exporter -setup => {
   exports => [
      elide => ’_build_elide’,
 ...
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 (...
Generating Routines
sub _build_trunc {
 my ($class, $name, $arg) = @_;
 my $_length = $arg->{length};

return sub {
  my (...
Generating Routines
sub _build_trunc {
 my ($class, $name, $arg) = @_;
 my $_length = $arg->{length};

return sub {
  my (...
Generating Routines
sub _build_trunc {
 my ($class, $name, $arg) = @_;
 my $_length = $arg->{length};

return sub {
  my (...
Generating Routines
 sub _build_trunc {
  my ($class, $name, $arg) = @_;
  my $_length = $arg->{length};

    return sub {...
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->...
Routines ex nihilo
use Cypher::Trivial qw(cyphers);

my ($encyph, $decyph) = cyphers(“secret”);

$cyphertext
  = $encyph->...
Routines ex nihilo
use Cypher::Trivial qw(cyphers);

my ($encyph, $decyph) = cyphers(“secret”);

$cyphertext
  = $encyph->...
Routines ex nihilo
use Cypher::Trivial qw(cyphers);

my ($encyph, $decyph) = cyphers(“secret”);

$cyphertext
  = $encyph->...
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});

 ret...
Routines ex nihilo
sub _build_encypher {
 my ($class, $name, $arg) = @_;
 my ($enc, $dec) = cyphers($arg->{secret});

 ret...
Routines ex nihilo
package Cypher::Trivial;

use Sub::Exporter -setup => {
   exports => [
     encypher => ’_build_encyph...
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(“...
Generating Groups
package Cypher::Trivial;

use Sub::Exporter -setup => {
   exports => [
     encypher => ’_build_encyphe...
Generating Groups
sub _build_encypher {
 my ($class, $name, $arg) = @_;
 my ($enc, $dec) = cyphers($arg->{secret});

 retu...
Generating Groups
package Cypher::Trivial;

use Sub::Exporter -setup => {
   exports => [
     encypher => ’_build_encyphe...
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)}
   = cyph...
Generating Groups

sub _build_cyphers {
  my ($class, $name, $arg) = @_;

 my %sub;
 @sub{qw(encypher decypher)}
   = cyph...
Generating Groups

sub _build_cyphers {
  my ($class, $name, $arg) = @_;

    my %sub;
    @sub{qw(encypher decypher)}
   ...
Generating Groups


use Cypher::Trivial
  -cyphers => { secret => “secret” };
Generating Groups

use Cypher::Trivial
  -cyphers => { secret => “secret” },

    -cyphers => { secret => ‘Secret1234’,
  ...
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!”
 • *{“$::$”}   = &{“$::$”};
Methods & Exporter.pm
package Object::Hybrid;
use Exporter;
@Object::Exporter::ISA = qw(Exporter);

@Object::Exporter::EXP...
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::EXP...
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->retri...
Methods & Exporter.pm

use Object::Hybrid;
use Object::Hybrid::With::Much::Derivation;

my $object = Object::Hybrid->retri...
Methods & Exporter.pm

use Object::Hybrid;
use Object::Hybrid::With::Much::Derivation;

my $object = Object::Hybrid->retri...
Methods & Exporter.pm

use Object::Hybrid;
use Object::Hybrid::With::Much::Derivation;

my $object = Object::Hybrid->retri...
Methods & Exporter.pm
package Object::Hybrid;
use Exporter;
@Object::Exporter::ISA = qw(Exporter);

@Object::Exporter::EXP...
Currying Methods

use Object::Hybrid qw(object);

use Object::Hybrid::With::Much::Derivation
 object => { -as => ‘derived_...
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 (...
Currying Methods

use Sub::Exporter -setup => {
   exports => [ object => &_build_object ],
};

sub _build_object {
  my (...
Currying Methods

use Sub::Exporter -setup => {
   exports => [ object => &_build_object ],
};

sub _build_object {
  my (...
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_c...
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
  ...
Exporting Methods
package Mixin::Dumper;

use Sub::Exporter -setup => {
   exports => [ qw(dump) ],
   groups => { default...
Exporting Methods
package Email::Simple::mixin:ReplyText;

use Sub::Exporter -setup => {
   exports => [ qw(reply_text) ],...
Exporting Methods
package Email::Simple::mixin:ReplyText;

use Sub::Exporter -setup => {
   into    => ‘Email::Simple’,
  ...
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::...
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...
Emulating mixin.pm
package Email::Simple::mixin:ReplyText;

use Sub::Exporter -setup => {
   into    => ‘Email::Simple’,
 ...
Emulating mixin.pm
package Email::Simple::mixin:ReplyText;

use Sub::Exporter -setup => {
   into    => ‘Email::Simple’,
 ...
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’,
      ...
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’ ...
Collectors

use String::Truncate
  defaults => { length => 10 },

    qw(-all),
    trunc => { length => 1,    -as => ‘one...
Collectors
 sub _build_trunc {
  my ($class, $name, $arg) = @_;
  my $_length = $arg->{length};

    return sub {
      my...
Collectors
 sub _build_trunc {
  my ($class, $name, $arg, $col) = @_;
  my $_length = $arg->{length};

    return sub {
  ...
Collectors
 sub _build_trunc {
  my ($class, $name, $arg, $col) = @_;
  my $_length = $arg->{length};

    $_length = $col...
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 ...
Collectors
package String::Truncate;

use Sub::Exporter -setup => {
   exports => [
      elide => ’_build_elide’,
      t...
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 ...
Collectors

• Arguments that don’t export.
• They collect data for generators to
  use.
• They can validate the collected ...
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 -...
Collectors

• name - name of the collection
• class - invocant of import method
• config - exporter configuration
• into -...
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
    c...
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_fail...
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;
}...
sub _make_base {
  my ($class, $value, $data) = @_;

    my $target = $data->{into};
    push @{“$target::ISA”}, $class;
}...
package Email::Constants;

sub _set_constants {
  my ($class, $value, $data) = @_;

    Package::Generator->assign_symbols...
package Email::Constants;

sub _set_constants {
  my ($class, $value, $data) = @_;

    Package::Generator->assign_symbols...
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} },
      [...
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} },
      [...
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} },
      [...
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} },
      [...
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} },
      [...
sub _setup {
  my ($class, $value, $data) = @_;

    if (ref $value eq ‘HASH’) {
      push @{ $data->{import_args} },
   ...
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,
  export...
-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 ($cl...
package Sub::Exporter;

use Sub::Exporter -setup => {
  collectors => { -setup => &_setup },
  exports => [
    _import =>...
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(...
Mixed-in Helpers
package Mixin::Helper;

use Sub::Exporter -setup => {
 exports => [
    complex_method => ’_build_cplx_me...
Mixed-in Helpers
sub _build_cplx_method {
  my ($mixin) = @_;
  sub {
    my ($self, $arg) = @_;
    $mixin->validate_arg(...
Mixed-in Helpers

package Mixin::Helper::Faster;
use base qw(Mixin::Helper);

sub analyze {
  my ($mixin, $object) = @_;
 ...
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 });
Accessors sans ISA


package YAPC::Slideshow;
use Accessors::Simple -setup => {
   fields => [ qw(topic presenter timeslot...
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 ...
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 ...
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 ...
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 ...
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 ...
Accessors sans ISA
sub _make_accessor {
  my ($field) = @_;
  sub {
    my ($self) = shift;
    $self->{field} = shift if ...
Eat Exporter’s Brain
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...
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...
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...
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...
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...
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_export...
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->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

  return $new_pkg if $new_pkg->i...
package UNIVERSAL;

sub exporter_upgrade {
  my ($pkg) = @_;
  my $new_pkg = “$pkg::SE”;

    return $new_pkg if $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(
   _generat...
Fixing caller

sub default_exporter {
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 _install(
   _generat...
Fixing caller

sub default_exporter {
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 _install(
   _generat...
Fixing caller

sub default_exporter {
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 _install(
   _generat...
Fixing caller

sub default_exporter {
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

    _install(
      _g...
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} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

 $col->{_g} ||=...
sub evil_eval_exporter { # TOTALLY UNTESTED!
  my ($class, $gen, $name, $arg, $col, $as, $into)
    = @_;

    $col->{_g} ...
Thank
 You!
Crafting Custom Interfaces with Sub::Exporter
Crafting Custom Interfaces with Sub::Exporter
Crafting Custom Interfaces with Sub::Exporter
Crafting Custom Interfaces with Sub::Exporter
Crafting Custom Interfaces with Sub::Exporter
Crafting Custom Interfaces with Sub::Exporter
Upcoming SlideShare
Loading in …5
×

Crafting Custom Interfaces with Sub::Exporter

6,411 views
6,315 views

Published on

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.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
6,411
On SlideShare
0
From Embeds
0
Number of Embeds
209
Actions
Shares
0
Downloads
100
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Crafting Custom Interfaces with Sub::Exporter

  1. 1. Sub::Exporter Crafting Custom Interfaces
  2. 2. rjbs @cpan.org Ricardo SIGNES
  3. 3. What’s an Exporter? • Something that takes care of the annoying details of importing.
  4. 4. What is Importing?
  5. 5. What is Importing? • Build it over there, then bring it here.
  6. 6. What is Importing? • Build it over there, then bring it here. • For our purposes, “it” is code.
  7. 7. Why Do We Import?
  8. 8. Why Do We Import? • We want someone else to do the hard, boring work.
  9. 9. Why Do We Import? • We want someone else to do the hard, boring work. • And we want it done cheap.
  10. 10. 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); }
  11. 11. Date::Format::strftime
  12. 12. strftime
  13. 13. How Importing Works
  14. 14. How Importing Works • the client use-s a module
  15. 15. How Importing Works • the client use-s a module • the module’s import method is called
  16. 16. How Importing Works • the client use-s a module • the module’s import method is called • something ugly happens
  17. 17. 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
  18. 18. How Importing Works • usually that ugliness is Exporter.pm
  19. 19. How Importing Works • usually that ugliness is Exporter.pm # the dark and twisted heart of Exporter.pm *{“${callpkg}::$sym”} = &{“${pkg}::$sym”};
  20. 20. *{quot;$::$quot;} = &{quot;$::$quot;}; • the caller gets the same code • with the same name
  21. 21. *{quot;$::$quot;} = &{quot;$::$quot;}; • Exporter.pm churns out identically named and constructed products.
  22. 22. The Factory Model
  23. 23. The Factory Model • One size fits all
  24. 24. The Factory Model • One size fits all • If it doesn’t fit your code, adjust your code.
  25. 25. The Factory Model • One size fits all • If it doesn’t fit your code, adjust your code. • Or abuse the Exporter
  26. 26. The Factory Model • There’s Only One Way To Do It
  27. 27. The Tool Metaphor
  28. 28. The Tool Metaphor • “You can’t write good code without good tools.”
  29. 29. The Tool Metaphor • “You can’t write good code without good tools.” • Exporters are tools for making tools.
  30. 30. 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.
  31. 31. Craftsman Tools
  32. 32. Craftsman Tools • We want adaptable tools, customized for our current needs.
  33. 33. Craftsman Tools • We want adaptable tools, customized for our current needs. • We want tools hand-crafted to our specifications.
  34. 34. 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.
  35. 35. Sub::Exporter!
  36. 36. Basic Exporting
  37. 37. The Basics • String::Truncate • trunc: truncate a string to length • elide: truncate, ending in “...”
  38. 38. String::Truncate
  39. 39. String::Truncate $string = “This string is 34 characters long.”;
  40. 40. String::Truncate $string = “This string is 34 characters long.”; trunc($string, 10); # This strin
  41. 41. String::Truncate $string = “This string is 34 characters long.”; trunc($string, 10); # This strin elide($string, 10); # This st...
  42. 42. Basic Exports To let your client write: use String::Truncate qw(elide trunc);
  43. 43. Basic Exports package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], };
  44. 44. Basic Groups To let your client write: use String::Truncate qw(:all)
  45. 45. Basic Groups package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], };
  46. 46. Basic Groups package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { all => [qw(elide trunc)] }, };
  47. 47. Basic Groups To let your client write: use String::Truncate qw(:basic)
  48. 48. Basic Groups package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [qw(elide trunc)] } };
  49. 49. Basic Defaults To let your client write: use String::Truncate; # imports “trunc”
  50. 50. Basic Defaults package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
  51. 51. Using Exporter.pm 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) ], );
  52. 52. Renaming Exports
  53. 53. Renaming Exports • avoid namespace collisions
  54. 54. Renaming Exports use CGI qw(:standard); use LWP::Simple; my $head = head(...); # what happens?
  55. 55. Renaming Exports • avoid unclear or ambiguous names
  56. 56. Renaming Exports use File::Basename; use XML::Parser; my $file = fileparse($ARGV[0]); $parser->parsefile($file);
  57. 57. Renaming Exports • “privatize” subs imported to classes
  58. 58. Renaming Exports package XML::Parser::Hypothetical; use File::Basename;
  59. 59. Renaming Exports Using Exporter::Renaming use Exporter::Renaming; use String::Truncate qw(elide), Renaming => [ trunc => ‘trunc_str’ ]; no Exporter::Renaming;
  60. 60. Renaming Exports To let your client write: use String::Truncate qw(trunc), trunc => { -as => ‘trunc_str’ };
  61. 61. Renaming Exports package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
  62. 62. Wait a second... use String::Truncate qw(trunc), trunc => { -as => ‘trunc_str’ };
  63. 63. Data::OptList Quick and Dirty Data Structures
  64. 64. Data::OptList @optlist = ( qw(alfa bravo), charlie => [ 0, 1, 2 ], delta => { a => 1 }, ‘echo’, foxtrox => undef, ‘gulf’, );
  65. 65. Data::OptList $as_href = { @optlist = ( alfa => undef, qw(alfa bravo), bravo => undef, charlie => [ 0, 1, 2 ], charlie => [ 0, 1, 2], delta => { a => 1 }, delta => { a => 1 }, ‘echo’, echo => undef, foxtrox => undef, foxtrot => undef, ‘gulf’, gulf => undef, ); ];
  66. 66. Data::OptList $as_aref = [ @optlist = ( [ alfa => undef ], qw(alfa bravo), [ bravo => undef ], charlie => [ 0, 1, 2 ], [ charlie => [0,1,2] ], delta => { a => 1 }, [ delta => {a => 1}], ‘echo’, [ echo => undef ], foxtrox => undef, [ foxtrot => undef ], ‘gulf’, [ gulf => undef ], ); ];
  67. 67. Data::OptList @optlist = ( qw(aye aye) love => [ qw(chex) ], love => [ qw(milk) ], aye => { sir => ‘!’ }, );
  68. 68. Data::OptList $as_aref = [ @optlist = ( [ aye => undef ], qw(aye aye) [ aye => undef ], love => [ qw(chex) ], [ love => [qw(chex)] ], love => [ qw(milk) ], [ love => [qw(milk)] ], aye => { sir => ‘!’ }, [ aye => {sir => ‘!’}] ); ];
  69. 69. Data::OptList @optlist = ( qw(aye aye) love => [ qw(chex) ], $as_href = die “...”; love => [ qw(milk) ], aye => { sir => ‘!’ }, );
  70. 70. Customizing Exports
  71. 71. Subclassed Exporters
  72. 72. Subclassed Exporters • import is a method
  73. 73. Subclassed Exporters • import is a method • that implies that exporters are classes
  74. 74. Subclassed Exporters • import is a method • that implies that exporters are classes • and that we can subclass them
  75. 75. package String::Truncate::Split; use base qw(String::Truncate); sub trunc { my ($string, $length) = @_; # ... return ($head, $tail); }
  76. 76. Subclassed Exporters
  77. 77. Subclassed Exporters • *{“$::$”} = &{“$::$”};
  78. 78. Subclassed Exporters • *{“$::$”} = &{“$::$”}; • @EXPORT has to be defined in the derived class
  79. 79. Subclassed Exporters • *{“$::$”} = &{“$::$”}; • @EXPORT has to be defined in the derived class • the export has to be defined in the exporting package
  80. 80. package String::Truncate::Split; use base qw(String::Truncate); sub trunc { my ($string, $length) = @_; # ... return ($head, $tail); } 1;
  81. 81. 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); }
  82. 82. 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;
  83. 83. 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; }
  84. 84. Subclassed Exporters
  85. 85. Subclassed Exporters • Sub::Exporter finds exports with “can”
  86. 86. Subclassed Exporters • Sub::Exporter finds exports with “can” • this means you can subclass exporting toolkits, replacing just pieces
  87. 87. package String::Truncate::Split; use base qw(String::Truncate); sub trunc { my ($string, $length) = @_; # ... return ($head, $tail); }
  88. 88. Customizing Exports
  89. 89. Customizing Exports • What if you want trunc to work differently when you use it?
  90. 90. Customizing Exports • Some modules do this with package variables.
  91. 91. Package-Level Config use String::Truncate qw(:all); $String::Truncate::DEFAULT_LENGTH = 20; $String::Truncate::DEFAULT_MARKER = “--”;
  92. 92. Package-Level Config use String::Truncate qw(:all); $String::Truncate::DEFAULT_LENGTH = 20; $String::Truncate::DEFAULT_MARKER = “--”; use Tools::Useful;
  93. 93. 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) }
  94. 94. Package-Level Config use String::Truncate (); use Tools::Useful; sub trunc { local $String::Truncate::DEFAULT_LENGTH = 20; String::Truncate::trunc(@_); }
  95. 95. Custom Imports use String::Truncate qw(trunc), elide => { -as => ‘trail_off’, marker => ‘etc’, };
  96. 96. Custom Imports use String::Truncate qw(trunc elide), elide => { -as => ‘trail_off’, marker => ‘etc’, };
  97. 97. Custom Imports use String::Truncate trunc => { -as => ‘trunc_str’, length => 10 }, elide => { -as => ‘elide_str’, length => 10 };
  98. 98. Custom Imports use String::Truncate -all => { -suffix => ‘_str’, length => 10 };
  99. 99. Exports to Order package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
  100. 100. Exports to Order package String::Truncate; use Sub::Exporter -setup => { exports => [ qw(elide trunc) ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
  101. 101. Exports to Order package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => undef, trunc => undef, ], groups => { basic => [ qw(elide trunc) ], default => [ qw(trunc) ] }, };
  102. 102. 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) ] }, };
  103. 103. Generating Routines
  104. 104. Generating Routines sub _build_trunc {
  105. 105. Generating Routines sub _build_trunc { my ($class, $name, $arg) = @_;
  106. 106. Generating Routines sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length};
  107. 107. Generating Routines sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length}; return sub {
  108. 108. Generating Routines sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length}; return sub { my ($string, $length, @rest) = @_;
  109. 109. Generating Routines sub _build_trunc { my ($class, $name, $arg) = @_; my $_length = $arg->{length}; return sub { my ($string, $length, @rest) = @_; $length = $_length if !defined $length;
  110. 110. 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);
  111. 111. 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); }
  112. 112. 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); } }
  113. 113. Routines ex nihilo
  114. 114. Routines ex nihilo use Cypher::Trivial qw(cyphers);
  115. 115. Routines ex nihilo use Cypher::Trivial qw(cyphers); my ($encyph, $decyph) = cyphers(“secret”);
  116. 116. Routines ex nihilo use Cypher::Trivial qw(cyphers); my ($encyph, $decyph) = cyphers(“secret”); $cyphertext
  117. 117. Routines ex nihilo use Cypher::Trivial qw(cyphers); my ($encyph, $decyph) = cyphers(“secret”); $cyphertext = $encyph->(“Top secret message.”);
  118. 118. Routines ex nihilo use Cypher::Trivial qw(cyphers); my ($encyph, $decyph) = cyphers(“secret”); $cyphertext = $encyph->(“Top secret message.”); sub encypher {
  119. 119. 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);
  120. 120. 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); }
  121. 121. Routines ex nihilo
  122. 122. Routines ex nihilo use Cypher::Trivial
  123. 123. Routines ex nihilo use Cypher::Trivial encypher => { secret => “secret” };
  124. 124. Routines ex nihilo use Cypher::Trivial encypher => { secret => “secret” }; encypher(“Top secret message”);
  125. 125. 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; }
  126. 126. 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; }
  127. 127. Routines ex nihilo package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => ’_build_encypher’, decypher => ’_build_decypher’, cyphers => undef, ], groups => { cyphers => [ qw(encypher decypher) ], } };
  128. 128. Routines ex nihilo use Cypher::Trivial encypher => { secret => “secret” }; encypher(“Top secret message”);
  129. 129. Routines ex nihilo use Cypher::Trivial -cyphers => { secret => “secret” }; encypher(“Top secret message”); decypher(“Gbc frperg zrffntr”);
  130. 130. Generating Groups package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => ’_build_encypher’, decypher => ’_build_decypher’, cyphers => undef, ], groups => { cyphers => [ qw(encypher decypher) ], } };
  131. 131. 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; }
  132. 132. Generating Groups package Cypher::Trivial; use Sub::Exporter -setup => { exports => [ encypher => ’_build_encypher’, decypher => ’_build_decypher’, cyphers => undef, ], groups => { cyphers => ’_build_cyphers’, } };
  133. 133. Generating Groups
  134. 134. Generating Groups sub _build_cyphers {
  135. 135. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_;
  136. 136. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_; my %sub;
  137. 137. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_; my %sub; @sub{qw(encypher decypher)}
  138. 138. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_; my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret});
  139. 139. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_; my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret}); return %sub;
  140. 140. Generating Groups sub _build_cyphers { my ($class, $name, $arg) = @_; my %sub; @sub{qw(encypher decypher)} = cyphers($arg->{secret}); return %sub; }
  141. 141. Generating Groups use Cypher::Trivial -cyphers => { secret => “secret” };
  142. 142. Generating Groups use Cypher::Trivial -cyphers => { secret => “secret” }, -cyphers => { secret => ‘Secret1234’, -suffix => ‘_strong’ } ;
  143. 143. Exporting Methods
  144. 144. ZOMG O NO!
  145. 145. Methods & Exporter.pm
  146. 146. Methods & Exporter.pm • Exporter.pm: “Do not export method names!”
  147. 147. Methods & Exporter.pm • Exporter.pm: “Do not export method names!” • *{“$::$”} = &{“$::$”};
  148. 148. Methods & Exporter.pm 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; }
  149. 149. Methods & Exporter.pm use Object::Hybrid qw(retrieve); my $object = retrieve(42); my $object = retrieve(49);
  150. 150. 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(@_) }
  151. 151. Methods & Exporter.pm use Object::Hybrid qw(object); my $object = object(42); my $object = object(49);
  152. 152. Methods & Exporter.pm use Object::Hybrid; my $object = Object::Hybrid->object(42);
  153. 153. Methods & Exporter.pm
  154. 154. Methods & Exporter.pm use Object::Hybrid;
  155. 155. Methods & Exporter.pm use Object::Hybrid; use Object::Hybrid::With::Much::Derivation;
  156. 156. Methods & Exporter.pm use Object::Hybrid; use Object::Hybrid::With::Much::Derivation; my $object = Object::Hybrid->retrieve(42);
  157. 157. Methods & Exporter.pm use Object::Hybrid; use Object::Hybrid::With::Much::Derivation; my $object = Object::Hybrid->retrieve(42); my $thing =
  158. 158. 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
  159. 159. 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);
  160. 160. 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(@_) }
  161. 161. 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);
  162. 162. Currying Methods use Object::Hybrid qw(object); my $object = Object::Hybrid->object(49);
  163. 163. Currying Methods
  164. 164. Currying Methods
  165. 165. Currying Methods use Sub::Exporter -setup => {
  166. 166. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ],
  167. 167. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ], };
  168. 168. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ], }; sub _build_object {
  169. 169. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ], }; sub _build_object { my ($class, $name, $arg) = @_;
  170. 170. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ], }; sub _build_object { my ($class, $name, $arg) = @_; return sub { $class->new(@_); }
  171. 171. Currying Methods use Sub::Exporter -setup => { exports => [ object => &_build_object ], }; sub _build_object { my ($class, $name, $arg) = @_; return sub { $class->new(@_); } }
  172. 172. Currying Methods use Sub::Exporter -setup => { exports => [ object => curry_class(‘new’) ], }
  173. 173. Currying Methods use Sub::Exporter::Util qw(curry_class); use Sub::Exporter -setup => { exports => [ object => curry_class(‘new’) ], }
  174. 174. Exporting Methods
  175. 175. Exporting Methods • Sometimes you want to export methods without currying the class.
  176. 176. Exporting Methods • Sometimes you want to export methods without currying the class. • Exporters can serve as method crafters.
  177. 177. 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); }
  178. 178. 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; }
  179. 179. 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; }
  180. 180. Exporting Methods use Email::Simple; use Email::Simple::mixin::ReplyText;
  181. 181. Exporting Methods use Email::Simple; use Email::Simple::mixin::ReplyText; use Email::Simple::Mock; use Email::Simple::mixin::ReplyText { into => ‘Email::Simple::Mock’ };
  182. 182. Emulating mixin.pm
  183. 183. Emulating mixin.pm • Don’t import into my namespace...
  184. 184. Emulating mixin.pm • Don’t import into my namespace... • ...import to a new namespace...
  185. 185. Emulating mixin.pm • Don’t import into my namespace... • ...import to a new namespace... • ...and add it to my @ISA.
  186. 186. Emulating mixin.pm
  187. 187. Emulating mixin.pm • This makes it easy to import a chunk of methods and override just a few...
  188. 188. Emulating mixin.pm • This makes it easy to import a chunk of methods and override just a few... • ...and those few can call SUPER.
  189. 189. 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; }
  190. 190. 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; }
  191. 191. Collectors
  192. 192. Collectors
  193. 193. Collectors • Arguments that don’t export anything.
  194. 194. Collectors • Arguments that don’t export anything. • They collect data for generators to use.
  195. 195. Collectors package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => ’_build_elide’, trunc => ’_build_trunc’, ], collectors => [ qw(defaults) ], };
  196. 196. Collectors
  197. 197. Collectors use String::Truncate
  198. 198. Collectors use String::Truncate defaults => { length => 10 },
  199. 199. Collectors use String::Truncate defaults => { length => 10 }, qw(-all),
  200. 200. Collectors use String::Truncate defaults => { length => 10 }, qw(-all), trunc => { length => 1, -as => ‘onechar’ },
  201. 201. Collectors use String::Truncate defaults => { length => 10 }, qw(-all), trunc => { length => 1, -as => ‘onechar’ }, elide => { marker => ‘&c’, -as => ‘yul’ },
  202. 202. Collectors use String::Truncate defaults => { length => 10 }, qw(-all), trunc => { length => 1, -as => ‘onechar’ }, elide => { marker => ‘&c’, -as => ‘yul’ }, ;
  203. 203. 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); } }
  204. 204. 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); } }
  205. 205. 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); } }
  206. 206. Collectors
  207. 207. Collectors • Arguments that don’t export. • They collect data for generators to use.
  208. 208. Collectors • Arguments that don’t export. • They collect data for generators to use. • They can validate the collected data.
  209. 209. Collectors package String::Truncate; use Sub::Exporter -setup => { exports => [ elide => ’_build_elide’, trunc => ’_build_trunc’, ], collectors => { defaults => ’_validate_defaults’, }, };
  210. 210. Collectors sub _validate_defaults { my ($class, $value, $data) = @_; return (ref $value eq ‘HASH’); }
  211. 211. Collectors
  212. 212. Collectors • Arguments that don’t export. • They collect data for generators to use. • They can validate the collected data.
  213. 213. 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.
  214. 214. Collectors sub _validate_defaults { my ($class, $value, $data) = @_; return (ref $value eq ‘HASH’); }
  215. 215. Collectors
  216. 216. Collectors • name - name of the collection
  217. 217. Collectors • name - name of the collection • class - invocant of import method
  218. 218. Collectors • name - name of the collection • class - invocant of import method • config - exporter configuration
  219. 219. Collectors • name - name of the collection • class - invocant of import method • config - exporter configuration • into - the package that’s importing
  220. 220. 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
  221. 221. Collectors
  222. 222. Collectors • name - the name of the collection
  223. 223. Collectors • name - the name of the collection • class - import’s invocant
  224. 224. Collectors
  225. 225. Collectors • config - the Sub::Exporter config
  226. 226. Collectors • config - the Sub::Exporter config • find out what exports exist
  227. 227. Collectors • config - the Sub::Exporter config • find out what exports exist • validate collection value based on config
  228. 228. use LWP::Simple “/^is_/”; is_success($res); is_failure($res);
  229. 229. use LWP::Simpleton; use Sub::Exporter -setup => { collectors => { like => Sub::Exporter::Util::like }, };
  230. 230. use LWP::Simple like => qr/^is_/; is_success($res); is_failure($res);
  231. 231. use LWP::Simple like => [ qr/^is_/, undef, qr/^get/, { -prefix => ‘https_’, ssl => 1 } ]; is_success($res); is_failure($res); https_get(“https://codesimply.com”)
  232. 232. Collectors
  233. 233. Collectors • into - the target to which exports go
  234. 234. Collectors • into - the target to which exports go • alter the class directly
  235. 235. Collectors • into - the target to which exports go • alter the class directly • particularly useful: @ISA
  236. 236. sub _make_base { my ($class, $value, $data) = @_; my $target = $data->{into}; push @{“$target::ISA”}, $class; }
  237. 237. sub _make_base { my ($class, $value, $data) = @_; my $target = $data->{into}; push @{“$target::ISA”}, $class; } use Sub::Exporter -setup => { collectors => { base => ’_make_base’ }, };
  238. 238. 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;
  239. 239. package Email::Constants; sub _set_constants { my ($class, $value, $data) = @_; Package::Generator->assign_symbols( $data->{into}, [ EX_TEMPFAIL => 75, FORMATS => [ qw(Maildir mbox mh) ], ], ); }
  240. 240. 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’ }, };
  241. 241. use Email::Constants qw(constants);
  242. 242. Collectors
  243. 243. Collectors • import_args - the arguments to import
  244. 244. Collectors • import_args - the arguments to import • rewrite the arguments list
  245. 245. Collectors • import_args - the arguments to import • rewrite the arguments list • add new imports
  246. 246. sub _setup {
  247. 247. sub _setup { my ($class, $value, $data) = @_;
  248. 248. sub _setup { my ($class, $value, $data) = @_; if (ref $value eq ‘HASH’) {
  249. 249. sub _setup { my ($class, $value, $data) = @_; if (ref $value eq ‘HASH’) { push @{ $data->{import_args} },
  250. 250. sub _setup { my ($class, $value, $data) = @_; if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ];
  251. 251. sub _setup { my ($class, $value, $data) = @_; if (ref $value eq ‘HASH’) { push @{ $data->{import_args} }, [ _import => { -as => ‘import’, %$value } ]; return 1;
  252. 252. 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’) {
  253. 253. 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} },
  254. 254. 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 => {
  255. 255. 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 } ];
  256. 256. 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;
  257. 257. 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; }
  258. 258. 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;
  259. 259. 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; }
  260. 260. use Sub::Exporter -setup => {
  261. 261. use Sub::Exporter -setup => { collectors => { -setup => ’_setup’ },
  262. 262. use Sub::Exporter -setup => { collectors => { -setup => ’_setup’ }, exports => [ _import => ’_build_import’ ],
  263. 263. use Sub::Exporter -setup => { collectors => { -setup => ’_setup’ }, exports => [ _import => ’_build_import’ ], });
  264. 264. -setup => { into_level => 2, exports => [qw(foo)] }
  265. 265. -setup => { into_level => 2, exports => [qw(foo)] } _import => { -as => ‘import’, into_level => 2, exports => [qw(foo)] }
  266. 266. -setup => [ qw(foo bar baz) ]
  267. 267. -setup => [ qw(foo bar baz) ] _import => { -as => ‘import’, exports => [qw(foo bar baz)] }
  268. 268. use Sub::Exporter -setup => { collectors => { -setup => ’_setup’ }, exports => [ _import => ’_build_import’ ], });
  269. 269. use Sub::Exporter -setup => { collectors => { -setup => ’_setup’ }, exports => [ _import => sub { my ($class, $name, $arg) = @_; build_exporter($arg); }, ], });
  270. 270. package Sub::Exporter; use Sub::Exporter -setup => { collectors => { -setup => &_setup }, exports => [ _import => sub { my ($class, $name, $arg) = @_; build_exporter($arg); }, ], });
  271. 271. RJBS’s Advice
  272. 272. RJBS’s Advice • Write the client code first.
  273. 273. RJBS’s Advice • Write the client code first. • Make as many assumptions as possible.
  274. 274. RJBS’s Advice • Write the client code first. • Make as many assumptions as possible. • Let most of them be refuted.
  275. 275. Any Questions?
  276. 276. Random Tricks
  277. 277. Mixed-in Helpers $object->complex_method($arg);
  278. 278. 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 {...}
  279. 279. Mixed-in Helpers package Mixin::Helper; use Sub::Exporter -setup => { exports => [ complex_method => ’_build_cplx_method’, ], }; sub _build_cplx_method { ...
  280. 280. 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 {...}
  281. 281. Mixed-in Helpers package Mixin::Helper::Faster; use base qw(Mixin::Helper); sub analyze { my ($mixin, $object) = @_; return 1; } 1;
  282. 282. A Coderef Generator
  283. 283. A Coderef Generator use String::Truncate ();
  284. 284. A Coderef Generator use String::Truncate (); my $trunc;
  285. 285. A Coderef Generator use String::Truncate (); my $trunc; String::Truncate->import(
  286. 286. A Coderef Generator use String::Truncate (); my $trunc; String::Truncate->import(trunc =>
  287. 287. A Coderef Generator use String::Truncate (); my $trunc; String::Truncate->import(trunc => { -as => $trunc });
  288. 288. Accessors sans ISA package YAPC::Slideshow; use Accessors::Simple -setup => { fields => [ qw(topic presenter timeslot room) ], };
  289. 289. Accessors sans ISA
  290. 290. Accessors sans ISA sub _make_accessor {
  291. 291. Accessors sans ISA sub _make_accessor { my ($field) = @_;
  292. 292. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub {
  293. 293. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift;
  294. 294. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_;
  295. 295. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field};
  296. 296. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; }
  297. 297. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } }
  298. 298. Accessors sans ISA sub _make_accessor { my ($field) = @_; sub { my ($self) = shift; $self->{field} = shift if @_; return $self->{$field}; } } sub _make_many_accessors {
  299. 299. 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} };
  300. 300. 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;
  301. 301. 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;
  302. 302. 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; }
  303. 303. 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 =>
  304. 304. 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 } };
  305. 305. Eat Exporter’s Brain
  306. 306. Eat Exporter’s Brain sub exporter_upgrade {
  307. 307. Eat Exporter’s Brain sub exporter_upgrade { my ($pkg) = @_;
  308. 308. Eat Exporter’s Brain sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”;
  309. 309. Eat Exporter’s Brain sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”; Sub::Exporter::setup_exporter({
  310. 310. Eat Exporter’s Brain sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”; Sub::Exporter::setup_exporter({ as => ‘import’,
  311. 311. Eat Exporter’s Brain sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”; Sub::Exporter::setup_exporter({ as => ‘import’, into => $new_pkg,
  312. 312. 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”} ],
  313. 313. 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 => {
  314. 314. 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”},
  315. 315. 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”} ],
  316. 316. 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”} ], },
  317. 317. 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”} ], }, });
  318. 318. 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;
  319. 319. 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;
  320. 320. 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; }
  321. 321. package UNIVERSAL;
  322. 322. package UNIVERSAL; sub exporter_upgrade {
  323. 323. package UNIVERSAL; sub exporter_upgrade { my ($pkg) = @_;
  324. 324. package UNIVERSAL; sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”;
  325. 325. package UNIVERSAL; sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”; return $new_pkg if $new_pkg->isa($pkg);
  326. 326. package UNIVERSAL; sub exporter_upgrade { my ($pkg) = @_; my $new_pkg = “$pkg::SE”; return $new_pkg if $new_pkg->isa($pkg); Sub::Exporter::setup_exporter({
  327. 327. 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’,
  328. 328. 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,
  329. 329. 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”} ],
  330. 330. 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 => {
  331. 331. 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”},
  332. 332. 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”} ],
  333. 333. 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”} ], },
  334. 334. 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”} ], }, });
  335. 335. 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;
  336. 336. 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;
  337. 337. 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; }
  338. 338. Fixing caller
  339. 339. Fixing caller sub default_exporter {
  340. 340. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into)
  341. 341. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
  342. 342. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install(
  343. 343. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install( _generate($class, $generator, $name, $arg, $col),
  344. 344. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install( _generate($class, $generator, $name, $arg, $col), $into,
  345. 345. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install( _generate($class, $generator, $name, $arg, $col), $into, $as,
  346. 346. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install( _generate($class, $generator, $name, $arg, $col), $into, $as, );
  347. 347. Fixing caller sub default_exporter { my ($class, $gen, $name, $arg, $col, $as, $into) = @_; _install( _generate($class, $generator, $name, $arg, $col), $into, $as, ); }
  348. 348. sub evil_eval_exporter { # TOTALLY UNTESTED!
  349. 349. sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into)
  350. 350. sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
  351. 351. sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_;
  352. 352. sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do {
  353. 353. sub evil_eval_exporter { # TOTALLY UNTESTED! my ($class, $gen, $name, $arg, $col, $as, $into) = @_; $col->{_g} ||= do { my $g = Dump(&_generate)->Names(‘GEN’)->Out;
  354. 354. 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/;
  355. 355. 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”;
  356. 356. 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;
  357. 357. 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”};
  358. 358. 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”}; };
  359. 359. 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(
  360. 360. 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),
  361. 361. 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,
  362. 362. 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,
  363. 363. 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, );
  364. 364. 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, ); }
  365. 365. Thank You!

×