Getting big without
getting fat, in Perl
Dean Hamstead, dean@bytefoundry.com.au
Sydney PM, 18th August 2015
Trigger Warning:
Startup lingo for the
next few slides.
I’ve given you fair
warning :)
Whipupitude and
Getting Things Done!
● Whipupitude is a Perl strength
○ Great at getting from 0 to 1
● Quick perl scripts tend to become enterprise
software (possibly better written?)
● Getting Things Done > Unfinished ‘Better’
○ A business making money from in-production code
is a business with the luxury of rewriting in-
production code!
○ The quality (hence cost) of the solution needs to
match the problem being solved
○ (We are artists, so trade offs don’t always sit well)
Going from 1 to 1+
● Initially we had nothing, now we have
something
● It works well enough that now we have the
luxury of rewriting it
● “I was right” attitudes forget that we had to
get from 0 to 1 :)
● As our business/platform is now making
money, its a “problem” that demands a
better solution
Quick lol
Make the pain go away
● Structuring your code to add features easily
& quickly
● Ensure that existing features keep working
and new features work as planned
● Providing the right buttons and knobs to
make life easier for humans
The four elements of code that grows sensibly
Testing.
Won’t be covered in this talk.
Logging.
See my talk from last month.
tl;dr?
● Try Log::Log4perl
● Try Log::Dispatch
● Profit!
Plugins.
Ok, let’s dive in.
Plugins 101
● Easily add functionality
● Easily remove functionality
○ Make things ‘pluggable’!
● Simplify parallel development
● Facilitate orthogonal requirements/needs
● Loose coupling and separation of concerns
● Simplify testing!
Dog Food Philosophy
● State your API publically
● Move all functionality into plugins, use only
your stated API
● Keep only basic functionality
in the core of the application
● Ideally, the core should rarely
change
By using your own API you will quickly
discover it’s weaknesses!
Measurements of success
● Separation of concerns achieved
(I have no idea how you measure this?)
● You can change the internals without
changing the plugins!
● Others can make a plugin without
understanding all (or any!) internals
● It’s so easy (and fun!) to write plugins
that people are doing it whimsically
API Nice-to-haves:
● temporarily disable plugins
● per-plugin configuration
● per-plugin storage
API Strategies
● Hooks
● Delegation
● Roles
● Mix-ins
● Mix and match!
Hooks
● The plugin registers with the program
○ “I can read image/png files”
○ “Tell me when the user enters a URL”
○ “Call me when a new connection is opened”
● Fine grained control
● Helps provide a clear API
● Can be a pain to put in hook
calls everywhere it’s
(possibly) needed
Hooks as overrides: Net::
Server
In Net::Server you override the default hook handlers
(which do nothing). The basic flow is shown below:
$self->configure_hook; # Hook
$self->configure(@_);
$self->post_configure;
$self->post_configure_hook;
$self->pre_bind;
$self->bind;
$self->post_bind_hook;
$self->post_bind;
$self->pre_loop_hook;
$self->loop;
# lots of others inside $self->loop
$self->pre_server_close_hook;
$self->server_close;
Others (not in ->loop):
$self->can_read_hook()
$self->allow_deny_hook()
$self->request_denied_hook()
$self->post_process_request_hook()
$self->post_client_connection_hook()
$self->other_child_died_hook($pid)
$self->write_to_log_hook()
$self->fatal_hook()
$self->post_child_cleanup_hook()
$self->restart_open_hook()
$self->restart_close_hook()
$self->child_init_hook()
$self->pre_fork_hook()
$self->child_finish_hook()
E.g. Net::Server (cont.)
use strict; use warnings;
package My::Thing;
use parent qw( Net::Server::PreFork ); # pick your personality
sub _rename_me { $0 = q|Me: | . $_[0] }
sub post_bind_hook { _rename_me(q/listening/) }
sub process_request { _rename_me(q/running/); do_stuff() }
package main;
My::Thing->run(port => 12345);
The overrides way:
● Uses normal perl inheritance
● In Net::Server you can override stuff that’s
not intended as a hook for full blown perl
madness
● AUTOLOAD can be a lot of fun here
● Only allows one sub to be called on each
hook, although your sub could call SUPER::
Class::Trigger
● Allows your main program to define hooks (triggers) and
when they are called
● Provides methods so that your plugin attaches it’s subs
to these
hook points
● Triggers can optionally
have callbacks!
● Triggers are inherited!
● Triggers are called in the
same order they are attached
Delegation
The program tells the plugin/extension/module:
You go take care of this stuff
● “you take care of the database connection”
● “you parse the XML”
Not so good for small tweaks and add-ons
Inheritance is / can be a variation of this
Roles
● Are when a package (class) is required to
implement certain sub’s (methods)
● DIY with sub foo { die ‘missing
foo’ } in your base class (don’t though)
● Use Role::Tony, Moo::Role or similar
● Both complain early and loud that things are
missing
● Plus other helpful stuff
Mix-ins
● Add methods into existing base classes
● Good for adding new stuff to frameworks
● Not so good for changing how something
works
● Usually not good for application plugins
The Catalyst framework uses this method,
which is how methods magically appear on $c
Flat-file vs Modules
Flat-file
● OK for simple stuff
● You’ll have to
locate and load
them
● Namespace
dramas
● Crude entry/exit
points
Modules
● Full-access to Perl
OO goodness
● Perl can take care
of locating them
● Elegant touch
points
Flat File & .pm compared
cat Plugins/Example.pl
#!perl
use strict;
use warnings;
sub _hello {
print qq|hello!n|
}
# just one action, final statement
sub { _hello() }
cat Plugins/Example.pm
#!perl
use strict;
use warnings;
package App::Plugins::Example;
our $VERSION = 0.1;
sub _hello {
print qq|hello!n|
}
sub action1 { hello() }
sub action2 { print qq|bai!n| }
1
Loading? Don’t do this….
Use Module::Pluggable
#!perl
package MyClass;
use Module::Pluggable;
use MyClass;
my $mc = MyClass->new();
# returns the names of all plugins installed under MyClass::Plugin::*
my @plugins = $mc->plugins();
# Or if you want to look in another namespace
use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass::
Extend'];
# You can limit the plugins loaded using the except option, either as a string, array ref or regex
use Module::Pluggable except => 'MyClass::Plugin::Foo';
# Or if you want to instantiate each plugin rather than just return the name
use Module::Pluggable instantiate => 'new';
# Alternatively you can just require the module without instantiating it
use Module::Pluggable require => 1;
M::P::Object for OO
#!perl
package MyClass;
use Module::Pluggable::Object;
my $finder = Module::Pluggable::Object->new(%opts);
print "My plugins are: ".join(", ", $finder->plugins)."
n";
Other options as per Module::Pluggable. There are so so
many :)
Nice examples
● Plagger
● qpsmtpd
● HTML::FormFu
(maybe)
Config (CLI)
● GetOpt::Long
Does it need introducing? I am open to
alternatives though...
● Pod::Usage
Prints a usage message from embedded pod
documentation (do things once!)
Config (file)
“Config::Any provides a facility for Perl
applications and libraries to load configuration
data from multiple different file formats. It
supports XML, YAML, JSON, Apache-style
configuration, Windows INI files, and even Perl
code.”
Config (file) (cont.)
● Config::General deserves an extra look
● Apache style config files
● Lots of control over how the config is
interpreted and processed for you:
○ (Dis)allow multiple identical options?
○ Lower case all names?
○ Include directories, globs? over and over?
○ Merge duplicates?
○ Taint cleaning
○ Conversion to boolean (“true” and “false”)
○ Much much more!
Config (file) (cont.)
Fragmenting config files is really good idea.
We know this because Debian does it religiously: What Would Debian Do?
/etc/whatever/conf.d/*conf
Facilitates separation of concerns, works well
with plugins
Makes writing helper scripts super easy
Just email me or find me on Facebook
Dear Internet,
Please contact me and tell me
where I can do better!
I don’t (yet) know everything
Questions?
Other good stuff:
● Writing Pluggable Software
http://www.slideshare.net/miyagawa/writing-pluggable-software
● Build Easily Extensible Perl Programs
http://www.askbjoernhansen.com/archives/2005/08/Build_Easily_Extensible_Perl_Programs.pdf

Getting big without getting fat, in perl

  • 1.
    Getting big without gettingfat, in Perl Dean Hamstead, dean@bytefoundry.com.au Sydney PM, 18th August 2015
  • 2.
    Trigger Warning: Startup lingofor the next few slides. I’ve given you fair warning :)
  • 3.
    Whipupitude and Getting ThingsDone! ● Whipupitude is a Perl strength ○ Great at getting from 0 to 1 ● Quick perl scripts tend to become enterprise software (possibly better written?) ● Getting Things Done > Unfinished ‘Better’ ○ A business making money from in-production code is a business with the luxury of rewriting in- production code! ○ The quality (hence cost) of the solution needs to match the problem being solved ○ (We are artists, so trade offs don’t always sit well)
  • 4.
    Going from 1to 1+ ● Initially we had nothing, now we have something ● It works well enough that now we have the luxury of rewriting it ● “I was right” attitudes forget that we had to get from 0 to 1 :) ● As our business/platform is now making money, its a “problem” that demands a better solution
  • 5.
  • 6.
    Make the paingo away ● Structuring your code to add features easily & quickly ● Ensure that existing features keep working and new features work as planned ● Providing the right buttons and knobs to make life easier for humans
  • 7.
    The four elementsof code that grows sensibly
  • 8.
  • 9.
    Logging. See my talkfrom last month. tl;dr? ● Try Log::Log4perl ● Try Log::Dispatch ● Profit!
  • 10.
  • 11.
    Plugins 101 ● Easilyadd functionality ● Easily remove functionality ○ Make things ‘pluggable’! ● Simplify parallel development ● Facilitate orthogonal requirements/needs ● Loose coupling and separation of concerns ● Simplify testing!
  • 12.
    Dog Food Philosophy ●State your API publically ● Move all functionality into plugins, use only your stated API ● Keep only basic functionality in the core of the application ● Ideally, the core should rarely change By using your own API you will quickly discover it’s weaknesses!
  • 13.
    Measurements of success ●Separation of concerns achieved (I have no idea how you measure this?) ● You can change the internals without changing the plugins! ● Others can make a plugin without understanding all (or any!) internals ● It’s so easy (and fun!) to write plugins that people are doing it whimsically
  • 14.
    API Nice-to-haves: ● temporarilydisable plugins ● per-plugin configuration ● per-plugin storage
  • 15.
    API Strategies ● Hooks ●Delegation ● Roles ● Mix-ins ● Mix and match!
  • 16.
    Hooks ● The pluginregisters with the program ○ “I can read image/png files” ○ “Tell me when the user enters a URL” ○ “Call me when a new connection is opened” ● Fine grained control ● Helps provide a clear API ● Can be a pain to put in hook calls everywhere it’s (possibly) needed
  • 17.
    Hooks as overrides:Net:: Server In Net::Server you override the default hook handlers (which do nothing). The basic flow is shown below: $self->configure_hook; # Hook $self->configure(@_); $self->post_configure; $self->post_configure_hook; $self->pre_bind; $self->bind; $self->post_bind_hook; $self->post_bind; $self->pre_loop_hook; $self->loop; # lots of others inside $self->loop $self->pre_server_close_hook; $self->server_close; Others (not in ->loop): $self->can_read_hook() $self->allow_deny_hook() $self->request_denied_hook() $self->post_process_request_hook() $self->post_client_connection_hook() $self->other_child_died_hook($pid) $self->write_to_log_hook() $self->fatal_hook() $self->post_child_cleanup_hook() $self->restart_open_hook() $self->restart_close_hook() $self->child_init_hook() $self->pre_fork_hook() $self->child_finish_hook()
  • 18.
    E.g. Net::Server (cont.) usestrict; use warnings; package My::Thing; use parent qw( Net::Server::PreFork ); # pick your personality sub _rename_me { $0 = q|Me: | . $_[0] } sub post_bind_hook { _rename_me(q/listening/) } sub process_request { _rename_me(q/running/); do_stuff() } package main; My::Thing->run(port => 12345);
  • 19.
    The overrides way: ●Uses normal perl inheritance ● In Net::Server you can override stuff that’s not intended as a hook for full blown perl madness ● AUTOLOAD can be a lot of fun here ● Only allows one sub to be called on each hook, although your sub could call SUPER::
  • 20.
    Class::Trigger ● Allows yourmain program to define hooks (triggers) and when they are called ● Provides methods so that your plugin attaches it’s subs to these hook points ● Triggers can optionally have callbacks! ● Triggers are inherited! ● Triggers are called in the same order they are attached
  • 21.
    Delegation The program tellsthe plugin/extension/module: You go take care of this stuff ● “you take care of the database connection” ● “you parse the XML” Not so good for small tweaks and add-ons Inheritance is / can be a variation of this
  • 22.
    Roles ● Are whena package (class) is required to implement certain sub’s (methods) ● DIY with sub foo { die ‘missing foo’ } in your base class (don’t though) ● Use Role::Tony, Moo::Role or similar ● Both complain early and loud that things are missing ● Plus other helpful stuff
  • 23.
    Mix-ins ● Add methodsinto existing base classes ● Good for adding new stuff to frameworks ● Not so good for changing how something works ● Usually not good for application plugins The Catalyst framework uses this method, which is how methods magically appear on $c
  • 24.
    Flat-file vs Modules Flat-file ●OK for simple stuff ● You’ll have to locate and load them ● Namespace dramas ● Crude entry/exit points Modules ● Full-access to Perl OO goodness ● Perl can take care of locating them ● Elegant touch points
  • 25.
    Flat File &.pm compared cat Plugins/Example.pl #!perl use strict; use warnings; sub _hello { print qq|hello!n| } # just one action, final statement sub { _hello() } cat Plugins/Example.pm #!perl use strict; use warnings; package App::Plugins::Example; our $VERSION = 0.1; sub _hello { print qq|hello!n| } sub action1 { hello() } sub action2 { print qq|bai!n| } 1
  • 26.
  • 27.
    Use Module::Pluggable #!perl package MyClass; useModule::Pluggable; use MyClass; my $mc = MyClass->new(); # returns the names of all plugins installed under MyClass::Plugin::* my @plugins = $mc->plugins(); # Or if you want to look in another namespace use Module::Pluggable search_path => ['Acme::MyClass::Plugin', 'MyClass:: Extend']; # You can limit the plugins loaded using the except option, either as a string, array ref or regex use Module::Pluggable except => 'MyClass::Plugin::Foo'; # Or if you want to instantiate each plugin rather than just return the name use Module::Pluggable instantiate => 'new'; # Alternatively you can just require the module without instantiating it use Module::Pluggable require => 1;
  • 28.
    M::P::Object for OO #!perl packageMyClass; use Module::Pluggable::Object; my $finder = Module::Pluggable::Object->new(%opts); print "My plugins are: ".join(", ", $finder->plugins)." n"; Other options as per Module::Pluggable. There are so so many :)
  • 29.
    Nice examples ● Plagger ●qpsmtpd ● HTML::FormFu (maybe)
  • 30.
    Config (CLI) ● GetOpt::Long Doesit need introducing? I am open to alternatives though... ● Pod::Usage Prints a usage message from embedded pod documentation (do things once!)
  • 31.
    Config (file) “Config::Any providesa facility for Perl applications and libraries to load configuration data from multiple different file formats. It supports XML, YAML, JSON, Apache-style configuration, Windows INI files, and even Perl code.”
  • 32.
    Config (file) (cont.) ●Config::General deserves an extra look ● Apache style config files ● Lots of control over how the config is interpreted and processed for you: ○ (Dis)allow multiple identical options? ○ Lower case all names? ○ Include directories, globs? over and over? ○ Merge duplicates? ○ Taint cleaning ○ Conversion to boolean (“true” and “false”) ○ Much much more!
  • 33.
    Config (file) (cont.) Fragmentingconfig files is really good idea. We know this because Debian does it religiously: What Would Debian Do? /etc/whatever/conf.d/*conf Facilitates separation of concerns, works well with plugins Makes writing helper scripts super easy
  • 34.
    Just email meor find me on Facebook Dear Internet, Please contact me and tell me where I can do better! I don’t (yet) know everything
  • 35.
  • 36.
    Other good stuff: ●Writing Pluggable Software http://www.slideshare.net/miyagawa/writing-pluggable-software ● Build Easily Extensible Perl Programs http://www.askbjoernhansen.com/archives/2005/08/Build_Easily_Extensible_Perl_Programs.pdf