Crafting Custom Interfaces with Sub::Exporter

6,690 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,690
On SlideShare
0
From Embeds
0
Number of Embeds
220
Actions
Shares
0
Downloads
101
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!

×