SlideShare a Scribd company logo
Writing Pluggable Software Tatsuhiko Miyagawa   [email_address] Six Apart, Ltd. / Shibuya Perl Mongers YAPC::Asia 2007 Tokyo
For non-JP attendees … If you find \ in the code, Replace that with backslash. (This is MS' fault)
Plaggable Software
Plaggable Software
Pl u ggable Software
Agenda
#1 How to make your app pluggable
#2 TMTOWTDP There's More Than One Way To Deploy Plugins Pros/Cons by examples
First-of-all: Why pluggable?
Benefits
#1 Keep the app design and code simple
#2 Let the app users customize the behavior (without hacking the internals)
#3 It's  fun  to write plugins for most hackers (see: Plagger and Kwiki)
"Can your app do XXX?" "Yes, by plugins."
"Your app has a bug in YYY" "No, it's the bug in plugin YYY, Not my fault." (Chain Of Responsibilities)
Good Enough Reasons, huh?
#1 Make your app pluggable
Example
ack (App::Ack)
grep –r for programmers
Ack is a "full-stack" software now.
By "full-stack" I mean: Easy install No configuration No way to extend
Specifically: These are hardcoded Ignored directories Filenames and types
Ignored Directories @ignore_dirs = qw( blib CVS RCS SCCS .svn _darcs .git );
Filenames and languages mapping %mappings = ( asm  => [qw( s S )], binary  => …, cc  => [qw( c h xs )], cpp  => [qw( cpp m h C H )], csharp  => [qw( cs )], … perl  => [qw( pl pm pod tt ttml t )], … );
What if making these pluggable?
DISCLAIMER
Don't get me wrong Andy, I love ack the way it is… Just thought it can be a very good example for the tutorial.
Quickstart: Class::Trigger Module::Pluggable © Six Apart Ltd. Employees
Class::Trigger SYNOPSIS package Foo; use Class::Trigger; sub foo { my $self = shift; $self->call_trigger('before_foo'); # some code ... $self->call_trigger('after_foo'); } package main; Foo->add_trigger(before_foo => \&sub1); Foo->add_trigger(after_foo => \&sub2);
Class::Trigger Helps you to implement Observer Pattern. (Rails calls this Observer)
Module::Pluggable SYNOPSIS 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(); package MyClass::Plugin::Foo; sub new { … } 1;
Setup plugins in App::Ack package App::Ack; use Class::Trigger; use Module::Pluggable require => 1; __PACKAGE__->plugins;
Setup plugins in App::Ack package App::Ack; use Class::Trigger; use Module::Pluggable  require => 1 ; __PACKAGE__->plugins; # "requires" modules
Ignored Directories (Before) @ignore_dirs = qw( blib CVS RCS SCCS .svn _darcs .git );
Ignored Directories (After) # lib/App/Ack.pm __PACKAGE__->call_trigger(' ignore_dirs.add ', \@ignore_dirs);
Ignored Directories (plugins) # lib/App/Ack/Plugin/IgnorePerlBuildDir.pm package App::Ack::Plugin::IgnorePerlBuildDir; App::Ack->add_trigger( " ignore_dirs.add " => sub { my($class, $ignore_dirs) = @_; push @$ignore_dirs, qw( blib ); }, ); 1;
Ignored Directories (plugins) # lib/App/Ack/Plugin/IgnoreSourceControlDir.pm package App::Ack::Plugin::IgnoreSourcdeControlDir; App::Ack->add_trigger( " ignore_dirs.add " => sub { my($class, $ignore_dirs) = @_; push @$ignore_dirs, qw( CVS RCS .svn _darcs .git ); }, ); 1;
Filenames and languages (before) %mappings = ( asm  => [qw( s S )], binary  => …, cc  => [qw( c h xs )], cpp  => [qw( cpp m h C H )], csharp  => [qw( cs )], … perl  => [qw( pl pm pod tt ttml t )], … );
Filenames and languages (after) # lib/App/Ack.pm __PACKAGE__->call_trigger('mappings.add', \%mappings);
Filenames and languages (plugins) package App::Ack::Plugin::MappingCFamily; use strict; App::Ack->add_trigger( "mappings.add" => sub { my($class, $mappings) = @_; $mappings->{asm} = [qw( s S )]; $mappings->{cc}  = [qw( c h xs )]; $mappings->{cpp} = [qw( cpp m h C H )]; $mappings->{csharp} = [qw( cs )]; $mappings->{css} = [qw( css )]; }, ); 1;
Works great  with few lines of code!
Now it's time to add  Some useful stuff.
Example Plugin: Content Filter
sub _search { my $fh = shift; my $is_binary = shift; my $filename = shift; my $regex = shift; my %opt = @_; if ($is_binary) { my $new_fh; App::Ack->call_trigger('filter.binary', $filename, \$new_fh); if ($new_fh) { return _search($new_fh, 0, $filename, $regex, @_); } }
Example: Search PDF content with ack
PDF filter plugin package App::Ack::Plugin::ExtractContentPDF; use strict; use CAM::PDF; use File::Temp; App::Ack->add_trigger( 'mappings.add' => sub { my($class, $mappings) = @_; $mappings->{pdf} = [qw(pdf)]; }, );
PDF filter plugin (cont.) App::Ack->add_trigger( 'filter.binary' => sub { my($class, $filename, $fh_ref) = @_; if ($filename =~ /\.pdf$/) { my $fh = File::Temp::tempfile; my $doc = CAM::PDF->new($file); my $text; for my $page (1..$doc->numPages){ $text .= $doc->getPageText($page); } print $fh $text; seek $$fh, 0, 0; $$fh_ref = $fh; } }, );
PDF search > ack --type=pdf Audrey yapcasia2007-pugs.pdf:3:Audrey Tang
Homework Use File::Extract To handle arbitrary media files
Homework 2: Search non UTF-8 files (hint: use Encode::Guess) You'll need another hook.
Summary Class::Trigger + Module::Pluggable = Pluggable app easy
#2 TMTOWTDP There's More Than One Way To Deploy Plugins
Module::Pluggable + Class::Trigger = Simple and Nice but has limitations
In Reality,  we need more control over how plugins behave
1) The order of  plugin executions
2) Per user configurations for plugins
3) Temporarily Disable plugins Should be easy
4) How to install & upgrade plugins
5) Let plugins  have storage area
Etc, etc.
Examples: Kwiki Plagger qpsmtpd Movable Type
I won't talk about Catalyst plugins (and other framework thingy)
Because they're  NOT "plug-ins"
Install plugins  And now you write  MORE CODE
95% of Catalyst plugins Are NOT "plugins" But "components" 95% of these statistics is made up by the speakers.
Kwiki 1.0
Kwiki Plugin code package Kwiki::URLBL; use  Kwiki::Plugin -Base ; use Kwiki::Installer -base; const class_id  => 'urlbl'; const class_title => 'URL Blacklist DNS'; const  config_file => 'urlbl.yaml'; sub register { require URI::Find; my $registry = shift; $registry->add(hook => 'edit:save', pre => 'urlbl_hook'); $registry->add(action => 'blacklisted_url'); }
Kwiki Plugin (cont.) sub urlbl_hook { my $hook = pop; my $old_page = $self->hub->pages->new_page($self->pages->current->id); my $this  = $self->hub->urlbl; my @old_urls = $this->get_urls($old_page->content); my @urls  = $this->get_urls($self->cgi->page_content); my @new_urls = $this->get_new_urls(\@old_urls, \@urls); if (@new_urls && $this->is_blocked(\@new_urls)) { $hook->cancel(); return $self->redirect("action=blacklisted_url"); } }
Magic implemented in Spoon(::Hooks)
"Install" Kwiki Plugins # order doesn't matter here (according to Ingy) Kwiki::Display Kwiki::Edit Kwiki::Theme::Basic Kwiki::Toolbar Kwiki::Status Kwiki::Widgets # Comment out (or entirely remove) to disable # Kwiki::UnnecessaryStuff
Kwiki plugin config # in Kwiki::URLBL plugin __config/urlbl.yaml__ urlbl_dns: sc.surbl.org, bsb.spamlookup.net, rbl.bulkfeeds.jp # config.yaml urlbl_dns: myowndns.example.org
Kwiki plugins are CPAN modules
Install and Upgrade plugins cpan> install Kwiki::SomeStuff
Using CPAN as a repository Pros #1: reuse most of current CPAN infrastructure.
Using CPAN as a repository Pros #2: Increasing # of modules = good motivation  for Perl hackers
Cons #1: Installing CPAN deps could be a mess (especially for Win32)
Cons #2: Whenever Ingy releases new Kwiki, lots of plugins just break.
Kwiki plugin storage return if  grep {$page->id} @{$self-> config->cached_display_ignore }; my $html = io->catfile( $self-> plugin_directory ,$page->id )->utf8;
Kwiki 2.0
Same as Kwiki 1.0
Except: plugins are now in SVN repository
 
Plagger plugin package Plagger::Plugin::Publish::iCal; use strict; use base qw(  Plagger::Plugin  ); use Data::ICal; use Data::ICal::Entry::Event; use DateTime::Duration; use DateTime::Format::ICal; sub register { my($self, $context) = @_; $context-> register_hook ( $self, ' publish.feed ' => \&publish_feed, ' plugin.init  ' => \&plugin_init, ); }
Plagger plugin (cont) sub plugin_init { my($self, $context) = @_; my $dir =  $self->conf->{dir}; unless (-e $dir && -d _) { mkdir $dir, 0755 or $context->error("Failed to mkdir $dir: $!"); } }
Plagger plugin storage $self->conf->{invindex} ||= $self-> cache->path_to ('invindex');
Plagger plugin config # The order matters in config.yaml # if they're in the same hooks plugins: - module: Subscription::Config config: feed: - http://www.example.com/ - module: Filter::DegradeYouTube config: dev_id: XYZXYZ - module: Publish::Gmail disable: 1
Plugins Install & Upgrade cpan> notest install Plagger # or … > svn co http://…/plagger/trunk plagger > svn update
Plagger impl. ripped off by many apps now
qpsmtpd
mod_perl for SMTP Runs with tcpserver, forkserver  or Danga::Socket standalone
Plugins: Flat files rock:/home/miyagawa/svn/qpsmtpd> ls -F plugins async/  greylisting auth/  hosts_allow check_badmailfrom  http_config check_badmailfromto  ident/ check_badrcptto  logging/ check_badrcptto_patterns  milter check_basicheaders  parse_addr_withhelo check_earlytalker  queue/ check_loop  quit_fortune check_norelay  rcpt_ok check_relay  relay_only check_spamhelo  require_resolvable_fromhost content_log  rhsbl count_unrecognized_commands  sender_permitted_from dns_whitelist_soft  spamassassin dnsbl  tls domainkeys  tls_cert* dont_require_anglebrackets  virus/
qpsmtpd plugin sub  hook_mail  { my ($self, $transaction, $sender, %param) = @_; my @badmailfrom =  $self->qp->config ("badmailfrom") or return (DECLINED); for my $bad (@badmailfrom) { my $reason = $bad; $bad =~ s/^\s*(\S+).*/$1/; next unless $bad; $transaction->notes('badmailfrom', $reason) … } return (DECLINED); }
Actually qpsmtpd Plugins are "compiled" to modules
my $eval = join("\n", "package $package;", 'use Qpsmtpd::Constants;', "require Qpsmtpd::Plugin;", 'use vars qw(@ISA);', 'use strict;', '@ISA = qw(Qpsmtpd::Plugin);', ($test_mode ? 'use Test::More;' : ''), "sub plugin_name { qq[$plugin] }", $line, $sub, "\n", # last line comment without newline? ); $eval =~ m/(.*)/s; $eval = $1; eval $eval; die "eval $@" if $@;
qpsmtpd plugin config rock:/home/miyagawa/svn/qpsmtpd> ls config.sample/ config.sample: IP  logging  require_resolvable_fromhost badhelo  loglevel  rhsbl_zones badrcptto_patterns  plugins   size_threshold dnsbl_zones  rcpthosts  tls_before_auth invalid_resolvable_fromhost  relayclients  tls_ciphers
config/plugins # content filters virus/klez_filter # rejects mails with a SA score higher than 2 spamassassin reject_threshold 20
config/badhelo # these domains never uses their domain when greeting us, so reject transactions aol.com yahoo.com
Install & Upgrade plugins Just use subversion
 
MT plugins are  flat-files (or scripts that call modules)
MT plugin code package MT::Plugin::BanASCII;  our $Method = "deny"; use MT;  use MT::Plugin;  my $plugin = MT::Plugin->new({ name => "BanASCII v$VERSION", description => "Deny or moderate ASCII or Latin-1 comment", });  MT->add_plugin($plugin); MT->add_callback('CommentFilter', 2, $plugin, \&handler);
MT plugin code (cont) sub init_app {  my $plugin = shift; $plugin->SUPER::init_app(@_); my($app) = @_; return unless $app->isa('MT::App::CMS'); $app-> add_itemset_action ({  type => 'comment', key => 'spam_submission_comment', label => 'Report SPAM Comment(s)', code => sub {  $plugin->submit_spams_action('MT::Comment', @_) },  } );
 
 
MT plugin storage require MT::PluginData; my $data = MT::PluginData->load({ plugin => 'sidebar-manager', key  => $blog_id }, ); unless ($data) { $data = MT::PluginData->new; $data->plugin('sidebar-manager'); $data->key($blog_id); } $data->data( \$modulesets ); $data->save or die $data->errstr;
Order control MT->add_callback('CMSPostEntrySave',  9 , $rightfields, \&CMSPostEntrySave); MT->add_callback('CMSPreSave_entry',  9 , $rightfields, \&CMSPreSave_entry); MT::Entry->add_callback('pre_remove',  9 , $rightfields, \&entry_pre_remove); Defined in plugins.  No Control on users end
Conclusion Flat-files vs. Modules
Flat-files: ☺  Easy to install (Just grab it) ☻  Hard to upgrade OK for simple plugins
Modules: ☺  Full-access to Perl OO goodness ☺  Avoid duplicate efforts of CPAN  ☻  Might be hard to resolve deps. Subversion to the rescue (could be a barrier for newbies)
Nice-to-haves: Order control Temporarily disable plugins Per plugin config Per plugin storage
Resources Class::Trigger http:// search.cpan.org /dist/Class-Trigger/ Module::Pluggable http:// search.cpan.org /dist/Module-Pluggable/ Ask Bjorn Hansen: Build Easily Extensible Perl Programs http://conferences.oreillynet.com/cs/os2005/view/e_sess/6806 qpsmtpd http:// smtpd.develooper.com / MT plugins http:// www.sixapart.com/pronet/plugins / Kwiki http:// www.kwiki.org / Plagger http:// plagger.org /
Ad

More Related Content

What's hot (20)

Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!
Eric Palakovich Carr
 
Modern Web Development with Perl
Modern Web Development with PerlModern Web Development with Perl
Modern Web Development with Perl
Dave Cross
 
Intro to PSGI and Plack
Intro to PSGI and PlackIntro to PSGI and Plack
Intro to PSGI and Plack
Tatsuhiko Miyagawa
 
A reviravolta do desenvolvimento web
A reviravolta do desenvolvimento webA reviravolta do desenvolvimento web
A reviravolta do desenvolvimento web
Wallace Reis
 
Jumping Into WordPress Plugin Programming
Jumping Into WordPress Plugin ProgrammingJumping Into WordPress Plugin Programming
Jumping Into WordPress Plugin Programming
Dougal Campbell
 
WordPress and Ajax
WordPress and AjaxWordPress and Ajax
WordPress and Ajax
Ronald Huereca
 
Django a whirlwind tour
Django   a whirlwind tourDjango   a whirlwind tour
Django a whirlwind tour
Brad Montgomery
 
Migraine Drupal - syncing your staging and live sites
Migraine Drupal - syncing your staging and live sitesMigraine Drupal - syncing your staging and live sites
Migraine Drupal - syncing your staging and live sites
drupalindia
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principles
Perl Careers
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
Jacob Kaplan-Moss
 
Writing your Third Plugin
Writing your Third PluginWriting your Third Plugin
Writing your Third Plugin
Justin Ryan
 
Ant
Ant Ant
Ant
sundar22in
 
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Matthias Noback
 
Plack at YAPC::NA 2010
Plack at YAPC::NA 2010Plack at YAPC::NA 2010
Plack at YAPC::NA 2010
Tatsuhiko Miyagawa
 
Web develop in flask
Web develop in flaskWeb develop in flask
Web develop in flask
Jim Yeh
 
Implementing Comet using PHP
Implementing Comet using PHPImplementing Comet using PHP
Implementing Comet using PHP
King Foo
 
Php on the Web and Desktop
Php on the Web and DesktopPhp on the Web and Desktop
Php on the Web and Desktop
Elizabeth Smith
 
Create responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJSCreate responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJS
Hannes Hapke
 
Plack perl superglue for web frameworks and servers
Plack perl superglue for web frameworks and serversPlack perl superglue for web frameworks and servers
Plack perl superglue for web frameworks and servers
Tatsuhiko Miyagawa
 
Getting modern with logging via log4perl
Getting modern with logging via log4perlGetting modern with logging via log4perl
Getting modern with logging via log4perl
Dean Hamstead
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!
Eric Palakovich Carr
 
Modern Web Development with Perl
Modern Web Development with PerlModern Web Development with Perl
Modern Web Development with Perl
Dave Cross
 
A reviravolta do desenvolvimento web
A reviravolta do desenvolvimento webA reviravolta do desenvolvimento web
A reviravolta do desenvolvimento web
Wallace Reis
 
Jumping Into WordPress Plugin Programming
Jumping Into WordPress Plugin ProgrammingJumping Into WordPress Plugin Programming
Jumping Into WordPress Plugin Programming
Dougal Campbell
 
Migraine Drupal - syncing your staging and live sites
Migraine Drupal - syncing your staging and live sitesMigraine Drupal - syncing your staging and live sites
Migraine Drupal - syncing your staging and live sites
drupalindia
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principles
Perl Careers
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
Jacob Kaplan-Moss
 
Writing your Third Plugin
Writing your Third PluginWriting your Third Plugin
Writing your Third Plugin
Justin Ryan
 
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Matthias Noback
 
Web develop in flask
Web develop in flaskWeb develop in flask
Web develop in flask
Jim Yeh
 
Implementing Comet using PHP
Implementing Comet using PHPImplementing Comet using PHP
Implementing Comet using PHP
King Foo
 
Php on the Web and Desktop
Php on the Web and DesktopPhp on the Web and Desktop
Php on the Web and Desktop
Elizabeth Smith
 
Create responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJSCreate responsive websites with Django, REST and AngularJS
Create responsive websites with Django, REST and AngularJS
Hannes Hapke
 
Plack perl superglue for web frameworks and servers
Plack perl superglue for web frameworks and serversPlack perl superglue for web frameworks and servers
Plack perl superglue for web frameworks and servers
Tatsuhiko Miyagawa
 
Getting modern with logging via log4perl
Getting modern with logging via log4perlGetting modern with logging via log4perl
Getting modern with logging via log4perl
Dean Hamstead
 

Similar to Writing Pluggable Software (20)

Create a web-app with Cgi Appplication
Create a web-app with Cgi AppplicationCreate a web-app with Cgi Appplication
Create a web-app with Cgi Appplication
olegmmiller
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
Yasuo Harada
 
Mojolicious on Steroids
Mojolicious on SteroidsMojolicious on Steroids
Mojolicious on Steroids
Tudor Constantin
 
Perl Dancer, FPW 2010
Perl Dancer, FPW 2010Perl Dancer, FPW 2010
Perl Dancer, FPW 2010
Alexis Sukrieh
 
What's New in ZF 1.10
What's New in ZF 1.10What's New in ZF 1.10
What's New in ZF 1.10
Ralph Schindler
 
Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
Kar Juan
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
diego_k
 
Grails and Dojo
Grails and DojoGrails and Dojo
Grails and Dojo
Sven Haiges
 
Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web Apps
James Williams
 
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Jon Peck
 
Writing webapps with Perl Dancer
Writing webapps with Perl DancerWriting webapps with Perl Dancer
Writing webapps with Perl Dancer
Alexis Sukrieh
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
brynary
 
Smarter Interfaces with jQuery (and Drupal)
Smarter Interfaces with jQuery (and Drupal)Smarter Interfaces with jQuery (and Drupal)
Smarter Interfaces with jQuery (and Drupal)
aasarava
 
Advanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIsAdvanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIs
andrewnacin
 
非同期処理の通知処理 with Tatsumaki
非同期処理の通知処理 with Tatsumaki非同期処理の通知処理 with Tatsumaki
非同期処理の通知処理 with Tatsumaki
keroyonn
 
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Atlassian
 
Summit2011 satellites-robinf-20110605
Summit2011 satellites-robinf-20110605Summit2011 satellites-robinf-20110605
Summit2011 satellites-robinf-20110605
Robin Fernandes
 
Introduction To Lamp
Introduction To LampIntroduction To Lamp
Introduction To Lamp
Amzad Hossain
 
Intro To Mvc Development In Php
Intro To Mvc Development In PhpIntro To Mvc Development In Php
Intro To Mvc Development In Php
funkatron
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
Gordon Forsythe
 
Create a web-app with Cgi Appplication
Create a web-app with Cgi AppplicationCreate a web-app with Cgi Appplication
Create a web-app with Cgi Appplication
olegmmiller
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
Yasuo Harada
 
Curscatalyst
CurscatalystCurscatalyst
Curscatalyst
Kar Juan
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
diego_k
 
Ratpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web AppsRatpack - Classy and Compact Groovy Web Apps
Ratpack - Classy and Compact Groovy Web Apps
James Williams
 
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Optimize Site Deployments with Drush (DrupalCamp WNY 2011)
Jon Peck
 
Writing webapps with Perl Dancer
Writing webapps with Perl DancerWriting webapps with Perl Dancer
Writing webapps with Perl Dancer
Alexis Sukrieh
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
brynary
 
Smarter Interfaces with jQuery (and Drupal)
Smarter Interfaces with jQuery (and Drupal)Smarter Interfaces with jQuery (and Drupal)
Smarter Interfaces with jQuery (and Drupal)
aasarava
 
Advanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIsAdvanced and Hidden WordPress APIs
Advanced and Hidden WordPress APIs
andrewnacin
 
非同期処理の通知処理 with Tatsumaki
非同期処理の通知処理 with Tatsumaki非同期処理の通知処理 with Tatsumaki
非同期処理の通知処理 with Tatsumaki
keroyonn
 
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Atlassian
 
Summit2011 satellites-robinf-20110605
Summit2011 satellites-robinf-20110605Summit2011 satellites-robinf-20110605
Summit2011 satellites-robinf-20110605
Robin Fernandes
 
Introduction To Lamp
Introduction To LampIntroduction To Lamp
Introduction To Lamp
Amzad Hossain
 
Intro To Mvc Development In Php
Intro To Mvc Development In PhpIntro To Mvc Development In Php
Intro To Mvc Development In Php
funkatron
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
Gordon Forsythe
 
Ad

More from Tatsuhiko Miyagawa (20)

Carton CPAN dependency manager
Carton CPAN dependency managerCarton CPAN dependency manager
Carton CPAN dependency manager
Tatsuhiko Miyagawa
 
Deploying Plack Web Applications: OSCON 2011
Deploying Plack Web Applications: OSCON 2011Deploying Plack Web Applications: OSCON 2011
Deploying Plack Web Applications: OSCON 2011
Tatsuhiko Miyagawa
 
Plack at OSCON 2010
Plack at OSCON 2010Plack at OSCON 2010
Plack at OSCON 2010
Tatsuhiko Miyagawa
 
cpanminus at YAPC::NA 2010
cpanminus at YAPC::NA 2010cpanminus at YAPC::NA 2010
cpanminus at YAPC::NA 2010
Tatsuhiko Miyagawa
 
PSGI/Plack OSDC.TW
PSGI/Plack OSDC.TWPSGI/Plack OSDC.TW
PSGI/Plack OSDC.TW
Tatsuhiko Miyagawa
 
CPAN Realtime feed
CPAN Realtime feedCPAN Realtime feed
CPAN Realtime feed
Tatsuhiko Miyagawa
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Asynchronous programming with AnyEvent
Asynchronous programming with AnyEventAsynchronous programming with AnyEvent
Asynchronous programming with AnyEvent
Tatsuhiko Miyagawa
 
Building a desktop app with HTTP::Engine, SQLite and jQuery
Building a desktop app with HTTP::Engine, SQLite and jQueryBuilding a desktop app with HTTP::Engine, SQLite and jQuery
Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Remedie OSDC.TW
Remedie OSDC.TWRemedie OSDC.TW
Remedie OSDC.TW
Tatsuhiko Miyagawa
 
Why Open Matters It Pro Challenge 2008
Why Open Matters It Pro Challenge 2008Why Open Matters It Pro Challenge 2008
Why Open Matters It Pro Challenge 2008
Tatsuhiko Miyagawa
 
20 modules i haven't yet talked about
20 modules i haven't yet talked about20 modules i haven't yet talked about
20 modules i haven't yet talked about
Tatsuhiko Miyagawa
 
Web::Scraper for SF.pm LT
Web::Scraper for SF.pm LTWeb::Scraper for SF.pm LT
Web::Scraper for SF.pm LT
Tatsuhiko Miyagawa
 
Web Scraper Shibuya.pm tech talk #8
Web Scraper Shibuya.pm tech talk #8Web Scraper Shibuya.pm tech talk #8
Web Scraper Shibuya.pm tech talk #8
Tatsuhiko Miyagawa
 
Web::Scraper
Web::ScraperWeb::Scraper
Web::Scraper
Tatsuhiko Miyagawa
 
XML::Liberal
XML::LiberalXML::Liberal
XML::Liberal
Tatsuhiko Miyagawa
 
Test::Base
Test::BaseTest::Base
Test::Base
Tatsuhiko Miyagawa
 
Hacking Vox and Plagger
Hacking Vox and PlaggerHacking Vox and Plagger
Hacking Vox and Plagger
Tatsuhiko Miyagawa
 
Plagger the duct tape of internet
Plagger the duct tape of internetPlagger the duct tape of internet
Plagger the duct tape of internet
Tatsuhiko Miyagawa
 
Tilting Google Maps and MissileLauncher
Tilting Google Maps and MissileLauncherTilting Google Maps and MissileLauncher
Tilting Google Maps and MissileLauncher
Tatsuhiko Miyagawa
 
Carton CPAN dependency manager
Carton CPAN dependency managerCarton CPAN dependency manager
Carton CPAN dependency manager
Tatsuhiko Miyagawa
 
Deploying Plack Web Applications: OSCON 2011
Deploying Plack Web Applications: OSCON 2011Deploying Plack Web Applications: OSCON 2011
Deploying Plack Web Applications: OSCON 2011
Tatsuhiko Miyagawa
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Asynchronous programming with AnyEvent
Asynchronous programming with AnyEventAsynchronous programming with AnyEvent
Asynchronous programming with AnyEvent
Tatsuhiko Miyagawa
 
Building a desktop app with HTTP::Engine, SQLite and jQuery
Building a desktop app with HTTP::Engine, SQLite and jQueryBuilding a desktop app with HTTP::Engine, SQLite and jQuery
Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Why Open Matters It Pro Challenge 2008
Why Open Matters It Pro Challenge 2008Why Open Matters It Pro Challenge 2008
Why Open Matters It Pro Challenge 2008
Tatsuhiko Miyagawa
 
20 modules i haven't yet talked about
20 modules i haven't yet talked about20 modules i haven't yet talked about
20 modules i haven't yet talked about
Tatsuhiko Miyagawa
 
Web Scraper Shibuya.pm tech talk #8
Web Scraper Shibuya.pm tech talk #8Web Scraper Shibuya.pm tech talk #8
Web Scraper Shibuya.pm tech talk #8
Tatsuhiko Miyagawa
 
Plagger the duct tape of internet
Plagger the duct tape of internetPlagger the duct tape of internet
Plagger the duct tape of internet
Tatsuhiko Miyagawa
 
Tilting Google Maps and MissileLauncher
Tilting Google Maps and MissileLauncherTilting Google Maps and MissileLauncher
Tilting Google Maps and MissileLauncher
Tatsuhiko Miyagawa
 
Ad

Recently uploaded (20)

Technology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data AnalyticsTechnology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data Analytics
InData Labs
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
SOFTTECHHUB
 
How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?
Daniel Lehner
 
TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven InsightsAndrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell
 
Drupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy ConsumptionDrupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy Consumption
Exove
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...
Vishnu Singh Chundawat
 
Quantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur MorganQuantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur Morgan
Arthur Morgan
 
HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 
Technology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data AnalyticsTechnology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data Analytics
InData Labs
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
SOFTTECHHUB
 
How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?How Can I use the AI Hype in my Business Context?
How Can I use the AI Hype in my Business Context?
Daniel Lehner
 
TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
How analogue intelligence complements AI
How analogue intelligence complements AIHow analogue intelligence complements AI
How analogue intelligence complements AI
Paul Rowe
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Generative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in BusinessGenerative Artificial Intelligence (GenAI) in Business
Generative Artificial Intelligence (GenAI) in Business
Dr. Tathagat Varma
 
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven InsightsAndrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell
 
Drupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy ConsumptionDrupalcamp Finland – Measuring Front-end Energy Consumption
Drupalcamp Finland – Measuring Front-end Energy Consumption
Exove
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...What is Model Context Protocol(MCP) - The new technology for communication bw...
What is Model Context Protocol(MCP) - The new technology for communication bw...
Vishnu Singh Chundawat
 
Quantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur MorganQuantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur Morgan
Arthur Morgan
 
HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 

Writing Pluggable Software

  • 1. Writing Pluggable Software Tatsuhiko Miyagawa [email_address] Six Apart, Ltd. / Shibuya Perl Mongers YAPC::Asia 2007 Tokyo
  • 2. For non-JP attendees … If you find \ in the code, Replace that with backslash. (This is MS' fault)
  • 5. Pl u ggable Software
  • 7. #1 How to make your app pluggable
  • 8. #2 TMTOWTDP There's More Than One Way To Deploy Plugins Pros/Cons by examples
  • 11. #1 Keep the app design and code simple
  • 12. #2 Let the app users customize the behavior (without hacking the internals)
  • 13. #3 It's fun to write plugins for most hackers (see: Plagger and Kwiki)
  • 14. "Can your app do XXX?" "Yes, by plugins."
  • 15. "Your app has a bug in YYY" "No, it's the bug in plugin YYY, Not my fault." (Chain Of Responsibilities)
  • 17. #1 Make your app pluggable
  • 20. grep –r for programmers
  • 21. Ack is a "full-stack" software now.
  • 22. By "full-stack" I mean: Easy install No configuration No way to extend
  • 23. Specifically: These are hardcoded Ignored directories Filenames and types
  • 24. Ignored Directories @ignore_dirs = qw( blib CVS RCS SCCS .svn _darcs .git );
  • 25. Filenames and languages mapping %mappings = ( asm => [qw( s S )], binary => …, cc => [qw( c h xs )], cpp => [qw( cpp m h C H )], csharp => [qw( cs )], … perl => [qw( pl pm pod tt ttml t )], … );
  • 26. What if making these pluggable?
  • 28. Don't get me wrong Andy, I love ack the way it is… Just thought it can be a very good example for the tutorial.
  • 29. Quickstart: Class::Trigger Module::Pluggable © Six Apart Ltd. Employees
  • 30. Class::Trigger SYNOPSIS package Foo; use Class::Trigger; sub foo { my $self = shift; $self->call_trigger('before_foo'); # some code ... $self->call_trigger('after_foo'); } package main; Foo->add_trigger(before_foo => \&sub1); Foo->add_trigger(after_foo => \&sub2);
  • 31. Class::Trigger Helps you to implement Observer Pattern. (Rails calls this Observer)
  • 32. Module::Pluggable SYNOPSIS 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(); package MyClass::Plugin::Foo; sub new { … } 1;
  • 33. Setup plugins in App::Ack package App::Ack; use Class::Trigger; use Module::Pluggable require => 1; __PACKAGE__->plugins;
  • 34. Setup plugins in App::Ack package App::Ack; use Class::Trigger; use Module::Pluggable require => 1 ; __PACKAGE__->plugins; # "requires" modules
  • 35. Ignored Directories (Before) @ignore_dirs = qw( blib CVS RCS SCCS .svn _darcs .git );
  • 36. Ignored Directories (After) # lib/App/Ack.pm __PACKAGE__->call_trigger(' ignore_dirs.add ', \@ignore_dirs);
  • 37. Ignored Directories (plugins) # lib/App/Ack/Plugin/IgnorePerlBuildDir.pm package App::Ack::Plugin::IgnorePerlBuildDir; App::Ack->add_trigger( " ignore_dirs.add " => sub { my($class, $ignore_dirs) = @_; push @$ignore_dirs, qw( blib ); }, ); 1;
  • 38. Ignored Directories (plugins) # lib/App/Ack/Plugin/IgnoreSourceControlDir.pm package App::Ack::Plugin::IgnoreSourcdeControlDir; App::Ack->add_trigger( " ignore_dirs.add " => sub { my($class, $ignore_dirs) = @_; push @$ignore_dirs, qw( CVS RCS .svn _darcs .git ); }, ); 1;
  • 39. Filenames and languages (before) %mappings = ( asm => [qw( s S )], binary => …, cc => [qw( c h xs )], cpp => [qw( cpp m h C H )], csharp => [qw( cs )], … perl => [qw( pl pm pod tt ttml t )], … );
  • 40. Filenames and languages (after) # lib/App/Ack.pm __PACKAGE__->call_trigger('mappings.add', \%mappings);
  • 41. Filenames and languages (plugins) package App::Ack::Plugin::MappingCFamily; use strict; App::Ack->add_trigger( "mappings.add" => sub { my($class, $mappings) = @_; $mappings->{asm} = [qw( s S )]; $mappings->{cc} = [qw( c h xs )]; $mappings->{cpp} = [qw( cpp m h C H )]; $mappings->{csharp} = [qw( cs )]; $mappings->{css} = [qw( css )]; }, ); 1;
  • 42. Works great with few lines of code!
  • 43. Now it's time to add Some useful stuff.
  • 45. sub _search { my $fh = shift; my $is_binary = shift; my $filename = shift; my $regex = shift; my %opt = @_; if ($is_binary) { my $new_fh; App::Ack->call_trigger('filter.binary', $filename, \$new_fh); if ($new_fh) { return _search($new_fh, 0, $filename, $regex, @_); } }
  • 46. Example: Search PDF content with ack
  • 47. PDF filter plugin package App::Ack::Plugin::ExtractContentPDF; use strict; use CAM::PDF; use File::Temp; App::Ack->add_trigger( 'mappings.add' => sub { my($class, $mappings) = @_; $mappings->{pdf} = [qw(pdf)]; }, );
  • 48. PDF filter plugin (cont.) App::Ack->add_trigger( 'filter.binary' => sub { my($class, $filename, $fh_ref) = @_; if ($filename =~ /\.pdf$/) { my $fh = File::Temp::tempfile; my $doc = CAM::PDF->new($file); my $text; for my $page (1..$doc->numPages){ $text .= $doc->getPageText($page); } print $fh $text; seek $$fh, 0, 0; $$fh_ref = $fh; } }, );
  • 49. PDF search > ack --type=pdf Audrey yapcasia2007-pugs.pdf:3:Audrey Tang
  • 50. Homework Use File::Extract To handle arbitrary media files
  • 51. Homework 2: Search non UTF-8 files (hint: use Encode::Guess) You'll need another hook.
  • 52. Summary Class::Trigger + Module::Pluggable = Pluggable app easy
  • 53. #2 TMTOWTDP There's More Than One Way To Deploy Plugins
  • 54. Module::Pluggable + Class::Trigger = Simple and Nice but has limitations
  • 55. In Reality, we need more control over how plugins behave
  • 56. 1) The order of plugin executions
  • 57. 2) Per user configurations for plugins
  • 58. 3) Temporarily Disable plugins Should be easy
  • 59. 4) How to install & upgrade plugins
  • 60. 5) Let plugins have storage area
  • 62. Examples: Kwiki Plagger qpsmtpd Movable Type
  • 63. I won't talk about Catalyst plugins (and other framework thingy)
  • 64. Because they're NOT "plug-ins"
  • 65. Install plugins And now you write MORE CODE
  • 66. 95% of Catalyst plugins Are NOT "plugins" But "components" 95% of these statistics is made up by the speakers.
  • 68. Kwiki Plugin code package Kwiki::URLBL; use Kwiki::Plugin -Base ; use Kwiki::Installer -base; const class_id => 'urlbl'; const class_title => 'URL Blacklist DNS'; const config_file => 'urlbl.yaml'; sub register { require URI::Find; my $registry = shift; $registry->add(hook => 'edit:save', pre => 'urlbl_hook'); $registry->add(action => 'blacklisted_url'); }
  • 69. Kwiki Plugin (cont.) sub urlbl_hook { my $hook = pop; my $old_page = $self->hub->pages->new_page($self->pages->current->id); my $this = $self->hub->urlbl; my @old_urls = $this->get_urls($old_page->content); my @urls = $this->get_urls($self->cgi->page_content); my @new_urls = $this->get_new_urls(\@old_urls, \@urls); if (@new_urls && $this->is_blocked(\@new_urls)) { $hook->cancel(); return $self->redirect("action=blacklisted_url"); } }
  • 70. Magic implemented in Spoon(::Hooks)
  • 71. "Install" Kwiki Plugins # order doesn't matter here (according to Ingy) Kwiki::Display Kwiki::Edit Kwiki::Theme::Basic Kwiki::Toolbar Kwiki::Status Kwiki::Widgets # Comment out (or entirely remove) to disable # Kwiki::UnnecessaryStuff
  • 72. Kwiki plugin config # in Kwiki::URLBL plugin __config/urlbl.yaml__ urlbl_dns: sc.surbl.org, bsb.spamlookup.net, rbl.bulkfeeds.jp # config.yaml urlbl_dns: myowndns.example.org
  • 73. Kwiki plugins are CPAN modules
  • 74. Install and Upgrade plugins cpan> install Kwiki::SomeStuff
  • 75. Using CPAN as a repository Pros #1: reuse most of current CPAN infrastructure.
  • 76. Using CPAN as a repository Pros #2: Increasing # of modules = good motivation for Perl hackers
  • 77. Cons #1: Installing CPAN deps could be a mess (especially for Win32)
  • 78. Cons #2: Whenever Ingy releases new Kwiki, lots of plugins just break.
  • 79. Kwiki plugin storage return if grep {$page->id} @{$self-> config->cached_display_ignore }; my $html = io->catfile( $self-> plugin_directory ,$page->id )->utf8;
  • 82. Except: plugins are now in SVN repository
  • 83.  
  • 84. Plagger plugin package Plagger::Plugin::Publish::iCal; use strict; use base qw( Plagger::Plugin ); use Data::ICal; use Data::ICal::Entry::Event; use DateTime::Duration; use DateTime::Format::ICal; sub register { my($self, $context) = @_; $context-> register_hook ( $self, ' publish.feed ' => \&publish_feed, ' plugin.init ' => \&plugin_init, ); }
  • 85. Plagger plugin (cont) sub plugin_init { my($self, $context) = @_; my $dir = $self->conf->{dir}; unless (-e $dir && -d _) { mkdir $dir, 0755 or $context->error("Failed to mkdir $dir: $!"); } }
  • 86. Plagger plugin storage $self->conf->{invindex} ||= $self-> cache->path_to ('invindex');
  • 87. Plagger plugin config # The order matters in config.yaml # if they're in the same hooks plugins: - module: Subscription::Config config: feed: - http://www.example.com/ - module: Filter::DegradeYouTube config: dev_id: XYZXYZ - module: Publish::Gmail disable: 1
  • 88. Plugins Install & Upgrade cpan> notest install Plagger # or … > svn co http://…/plagger/trunk plagger > svn update
  • 89. Plagger impl. ripped off by many apps now
  • 91. mod_perl for SMTP Runs with tcpserver, forkserver or Danga::Socket standalone
  • 92. Plugins: Flat files rock:/home/miyagawa/svn/qpsmtpd> ls -F plugins async/ greylisting auth/ hosts_allow check_badmailfrom http_config check_badmailfromto ident/ check_badrcptto logging/ check_badrcptto_patterns milter check_basicheaders parse_addr_withhelo check_earlytalker queue/ check_loop quit_fortune check_norelay rcpt_ok check_relay relay_only check_spamhelo require_resolvable_fromhost content_log rhsbl count_unrecognized_commands sender_permitted_from dns_whitelist_soft spamassassin dnsbl tls domainkeys tls_cert* dont_require_anglebrackets virus/
  • 93. qpsmtpd plugin sub hook_mail { my ($self, $transaction, $sender, %param) = @_; my @badmailfrom = $self->qp->config ("badmailfrom") or return (DECLINED); for my $bad (@badmailfrom) { my $reason = $bad; $bad =~ s/^\s*(\S+).*/$1/; next unless $bad; $transaction->notes('badmailfrom', $reason) … } return (DECLINED); }
  • 94. Actually qpsmtpd Plugins are "compiled" to modules
  • 95. my $eval = join("\n", "package $package;", 'use Qpsmtpd::Constants;', "require Qpsmtpd::Plugin;", 'use vars qw(@ISA);', 'use strict;', '@ISA = qw(Qpsmtpd::Plugin);', ($test_mode ? 'use Test::More;' : ''), "sub plugin_name { qq[$plugin] }", $line, $sub, "\n", # last line comment without newline? ); $eval =~ m/(.*)/s; $eval = $1; eval $eval; die "eval $@" if $@;
  • 96. qpsmtpd plugin config rock:/home/miyagawa/svn/qpsmtpd> ls config.sample/ config.sample: IP logging require_resolvable_fromhost badhelo loglevel rhsbl_zones badrcptto_patterns plugins size_threshold dnsbl_zones rcpthosts tls_before_auth invalid_resolvable_fromhost relayclients tls_ciphers
  • 97. config/plugins # content filters virus/klez_filter # rejects mails with a SA score higher than 2 spamassassin reject_threshold 20
  • 98. config/badhelo # these domains never uses their domain when greeting us, so reject transactions aol.com yahoo.com
  • 99. Install & Upgrade plugins Just use subversion
  • 100.  
  • 101. MT plugins are flat-files (or scripts that call modules)
  • 102. MT plugin code package MT::Plugin::BanASCII; our $Method = "deny"; use MT; use MT::Plugin; my $plugin = MT::Plugin->new({ name => "BanASCII v$VERSION", description => "Deny or moderate ASCII or Latin-1 comment", }); MT->add_plugin($plugin); MT->add_callback('CommentFilter', 2, $plugin, \&handler);
  • 103. MT plugin code (cont) sub init_app { my $plugin = shift; $plugin->SUPER::init_app(@_); my($app) = @_; return unless $app->isa('MT::App::CMS'); $app-> add_itemset_action ({ type => 'comment', key => 'spam_submission_comment', label => 'Report SPAM Comment(s)', code => sub { $plugin->submit_spams_action('MT::Comment', @_) }, } );
  • 104.  
  • 105.  
  • 106. MT plugin storage require MT::PluginData; my $data = MT::PluginData->load({ plugin => 'sidebar-manager', key => $blog_id }, ); unless ($data) { $data = MT::PluginData->new; $data->plugin('sidebar-manager'); $data->key($blog_id); } $data->data( \$modulesets ); $data->save or die $data->errstr;
  • 107. Order control MT->add_callback('CMSPostEntrySave', 9 , $rightfields, \&CMSPostEntrySave); MT->add_callback('CMSPreSave_entry', 9 , $rightfields, \&CMSPreSave_entry); MT::Entry->add_callback('pre_remove', 9 , $rightfields, \&entry_pre_remove); Defined in plugins. No Control on users end
  • 109. Flat-files: ☺ Easy to install (Just grab it) ☻ Hard to upgrade OK for simple plugins
  • 110. Modules: ☺ Full-access to Perl OO goodness ☺ Avoid duplicate efforts of CPAN ☻ Might be hard to resolve deps. Subversion to the rescue (could be a barrier for newbies)
  • 111. Nice-to-haves: Order control Temporarily disable plugins Per plugin config Per plugin storage
  • 112. Resources Class::Trigger http:// search.cpan.org /dist/Class-Trigger/ Module::Pluggable http:// search.cpan.org /dist/Module-Pluggable/ Ask Bjorn Hansen: Build Easily Extensible Perl Programs http://conferences.oreillynet.com/cs/os2005/view/e_sess/6806 qpsmtpd http:// smtpd.develooper.com / MT plugins http:// www.sixapart.com/pronet/plugins / Kwiki http:// www.kwiki.org / Plagger http:// plagger.org /