Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Exporter Proxy

673 views

Published on

This helps break up bloated modules -- or keeping new ones manageable -- by simplifying variable and method export and adding package-space dispatchers to modules. The talk uses the example of an overgrown Query module.

Published in: Technology, Business
  • Be the first to comment

  • Be the first to like this

Exporter Proxy

  1. 1. Exporter::Proxy Splitting up modules to help your sanity. Steven Lembark Workhorse Computing
  2. 2. Stuck in Lodi Again ● We've all been there: the 4000­line module. ● It began small enough... then it grew. ● Canned­query modules are classics for this: ● In the beginning was “userid”, and it was good, enough. ● Then it was split into get_userid/set_userid. ● Then it branched into get_dept_userid,  get_general_userid and set_*.  ● That was then, your cleanup is now.
  3. 3. Welcome to the Machine ● You have to clean up the module. ● You need to split the thing into usable chunks. ● Separating out namespaces. ● Keeping shared variables in a reasonable place. ● Keeping utility subs out of the way. ● By tomorrow. In parallel universes, in constant time.
  4. 4. This is Perl, however. ● So you will practice True Lazyness (c).  ● This means doing it once. ● Doing it right. ● And never having to do it again.
  5. 5. Lazyness is a virtue ● Exporting in Perl should be easy, but Exporter is  complicated (and error prone). ● Breaking up large modules into manageable chunks  often takes too much work. ● Both of these should be easy.
  6. 6. Un­lazy Shared Variables. ● Exporter allows sharing variables. ● If you inherit from it. ● If you keep proper track of EXPORT_OK,  EXPORT, EXPORT_TAGS. ● If, hopefully, @foo never changes to %foo. ● There has to be a better way...
  7. 7. False Lazyness: ● Break the module up by function,  the names don't  immediately collide: Lookup::userid,  Modify::userid ● But, you can't inherit both classes without collisions. ● You could try Lookup::Dept::userid and  Modify::Dept::userid. ● But do you really want to see: $qryobj->Lookup::Dept::userid( ... ); Let alone type it?
  8. 8. Un­Lazy Top­Half Modules ● Another way to split up the modules is a single God  Object dispatcher that “figures out” how to dispatch  into the various pieces of the original module.  ● This ends up being either a un­maintainable if­ block, an un­readable n­ary ternary op, or the  Switch From Hell (tm). ● The problem is that the caller knows what they  want: expose them to the decisions and let them  make the proper one.
  9. 9. Exporter::Proxy ● This is Perl, there is a lazy way. ● E::P simplifies you work by: ● Exporting symbols. ● Installing an import sub to export the symbols. ● Optionally installing a dispatch sub. ● This allows you to split your modules into layers: ● A top­half dispatcher that combines the moduels. ● The bottom­half modules that actually do the work. ● Common shared variables, all stored in one place.
  10. 10. “The simplest interface is none at all” ● Instead of having to define a set of variables for your  set of variables, just: use Exporter::Proxy qw( verbose debug foobar ); ● Whatever names you provide are exported as  symbols via Symbol: my $source = qualify_to_ref $_, $source; my $install = qualify_to_ref $_, $caller; *$install = *$source; ● $verbose, &verbose, @verbose, %verbose, all you  have to export is “verbose”.
  11. 11. Lazy Dispatcher ● These look pretty much the same: decide where the  call goes, send it there, and get out of the way. ● This is done in Exporter::Proxy with the “dispatch=”  argument. ● The dispatcher is exported by default. ● Each one dispatches calls within its own package: my $name = splice @_, 1, 1; my $dest = $package->can( $name ) or croak “$package cannot '$name'”; goto &$dest;
  12. 12. Splitting Up The Interface ● You've probably witnessed *NIX device drivers . ● The top half validates and dispatches the call, the  bottom half just does things. ● The top half doesn't care what does on, it just sends  things where they belong. ● The bottom half doesn't care why it does things, it  just does them.
  13. 13. Breaking Up A Module ● You can usually break the combined interface up  into functional groups: ● “modify” vs. “lookup” ● “department” vs. “office” vs. “location”. ● “Taxid”, “GenBank”, “Medline”, “MeSH”. ● You can also combine any shared variables into a  single module that exports them as needed.
  14. 14. Looking At A Query Module ● “modify” and “lookup” are probably good divisions. ● The SQL for canned queries is probably sharable.  ● The query methods don't care who their caller is. ● The dispatcher doesn't care why the methods were  called, it just has to hand back the data. ● Code something like: $query->lookup( department => @argz ); $query->modify( department => @argz ); ● Is reasonably mnemonic and easily extended.
  15. 15. Bottom Half Does the Work. ● The bottom half implements methods for specific  tasks and exports a single dispatcher for them. ● Break the module up into Query::Lookup,  Query::Modify, Query::Shared. ● Lookup & Modify install a dispatcher: use Exporter::Proxy qw( dispatch=lookup ); use Exporter::Proxy qw( dispatch=query ); ● They pull in shared content by Using Query::Shared: package Query::Modify; use Query::Shared qw( modify_dml ); # SQL hash
  16. 16. The Public Interface is Truly Lazy ● Bottom halves export their dispatchers: just collect  them together into a single API. package Query; use Query::Lookup; use Query::Modify; use Query::Shared qw( verbose ); sub verbose { @_ ? $verbose = shift : $verbose } 42 __END__ ● The Top Half is DUMB: there are no decisions here,  no special cases, no edge cases.
  17. 17. What the Caller Sees ● Anyone using “Query” just knows that its API  includes “lookup”, “modify”, and “verbose”. ● The methods take a first argument of what to lookup  or modify, and whatever arguments it needs. ● They don't have to know about the bottom­half,  shared variables, or utility sub's that aren't exported  by the bottom­half modules into the API. ● This is what makes the Top Half dumb: the caller  makes their own decisions on what to call.
  18. 18. Lazy Growth ● Re­factoring the interface into subject areas is also  straightforward: ● Add new modules “department”, “office”, “frobnicate”. ● Each has a mnemonic dispatcher, say, “dept”, “office”,  and “frob”. ● They implement whatever methods describe the action. ● They can share the existing SQL or define their own. ● The caller uses, say, $query->dept( id => $dept_name );
  19. 19. Summary ● True Lazyness is a virtue. ● Dispatching interfaces offer a simple way to  segregate the classes. ● Top­half classes can implement mnemonic API's. ● Bottom duty can be bearable with Exporter::Proxy.

×