[Purdue-pm] Perl 5 Dispatch Tables

Dave Jacoby jacoby.david at gmail.com
Thu May 30 09:30:16 PDT 2019


Mark, I am starting to write something on this. Would you want me to link
to your PDF?

On Wed, May 29, 2019 at 11:57 AM Dave Jacoby <jacoby.david at gmail.com> wrote:

> Of course, the OTHER option is ... bulky?
>
> my %dispatch;
> $dispatch{ foo } = sub { ... code goes here .. }
> $dispatch{ bar } = sub { ... code goes here .. }
> $dispatch{ blee } = sub { ... code goes here .. }
>
> code reuse is almost impossible by this method.
>
>
> On Wed, May 29, 2019 at 10:50 AM Dave Jacoby <jacoby.david at gmail.com>
> wrote:
>
>> I get that. This reminds me of the mess I went through to auto-populate
>> things based on the symbol table.
>>
>> You have a main program that's basically this:
>>
>> use strict ;
>> use warnings ;
>>
>> my $api = API->new(@ARGV) ;
>> $api->run() ;
>>
>> package API ;
>> use lib '/depot/gcore/apps/lib' ;
>> use base 'API_Base' ;
>> use API_PED ;
>>
>> ----- And then API_Base cut down looks like
>> sub new ( $class, @argv ) {
>> my $self ;
>> my $cgi = CGI->new() ;
>> $self->{method} = $ENV{REQUEST_METHOD} || 'GET' ;
>> %{ $self->{param} } = map { $_ => scalar $cgi->param($_) } $cgi->param()
>> ;
>> ( undef, @{ $self->{pathinfo} } ) = split m{/}mxs, $cgi->path_info() ;
>> return bless $self, $class ;
>> }
>>
>> # I honestly don't know why I can't combine this with run_command, but
>> # I tried it and gave up
>> sub run ($self) {
>> my @vars = map{ $self->{$_} } qw{ pathinfo param method } ;
>> return $self->run_command( $self->{pathinfo}, $self->{param}, $self->{method}
>> ) ;
>> }
>>
>> # where the work is handled
>> sub run_command ( $self, $pathinfo, $param, $method ) {
>> my $command = $pathinfo->[0] || 'test' ;
>> my $s = $self->can("api_$command") ;
>>
>> # can() tells us if an object has a method called METHOD, which is
>> # good for telling if it is a usable function
>> # http://perldoc.perl.org/UNIVERSAL.html
>>
>> # y/-/_/ because function names cannot be foo-bar, only foo_bar
>> if (!$s) {
>> $command =~ y/-/_/ ;
>> $s = $self->can("api_$command") ;
>> }
>>
>> # after this, we'll fail out if it still isn't in the table
>> if (!$s) {
>> $self->wrap( $self->fail( $pathinfo, $param, $method ) ) ;
>> exit ;
>> }
>>
>> #if it isn't a code ref, we'll fail out as well
>> if ( 'CODE' ne ref $s ) {
>> $self->wrap( $self->fail( $pathinfo, $param, $method ) ) ;
>> exit ;
>> }
>> # $self->$s if callable, so we'll call it.
>> return $self->wrap( $self->$s( $pathinfo, $param, $method ) ) ;
>> }
>>
>> ----
>>
>> Where the key is, given looking for an entry in the Purdue Electronic
>> Directory, the call would be in the symbol table as api_ped, so we see
>> if that function exists, using can('api_ped'). So, we need to be sure
>> that sub api_ped() is in there.
>> package API_PED ;
>>
>> # simlified somewhat
>> our $VERSION = 0.1 ;
>> our @EXPORT ;
>> # because sub NAME and push @EXPORT, NAME violates DRY
>> for my $entry ( keys %API_PED:: ) {
>> next if $entry !~ /^api_/mxs ;
>> push @EXPORT, $entry ;
>> }
>>
>> sub api_ped ( $self , $etc ) {
>> # talks to PED returns response
>> }
>> ----
>> (Lots of web API JSON specific things cut)
>>
>> So the behavior of program.pl here is determined almost entirely by
>> which modules it uses and if the sub names are prepended with 'api_'.
>>
>> Yes, that CAN violate DRY, but 1) the benefits of data-driven dev
>> are there and powerful, and 2) it doesn't necessary violate DRY. So, I
>> guess the question moves to what is worse: DRY or Scary Magic.
>>
>> On Tue, May 28, 2019 at 8:20 PM Mark Senn <mark at purdue.edu> wrote:
>>
>>> > I'm not sure how a typical dispatch table is violating DRY.
>>> > [... -mark]
>>> > sub create_dispatch_table ($config) {
>>> >   my $dispatch = {
>>> >   'thing_one' => \&thing_one,
>>> >   'thing_two' => \&thing_two,
>>> >   };
>>> >   return($dispatch);
>>> > }
>>> > [... -mark]
>>>
>>> From
>>> https://dzone.com/articles/software-design-principles-dry-and-kiss
>>>     The DRY Principle: Don't Repeat Yourself
>>>     DRY stand for "Don't Repeat Yourself," a basic principle of software
>>>     development aimed at reducing repetition of information.
>>>
>>> From my earlier message:
>>>     In short, I don't use conventional dispatch tables because they
>>>     violate the don't repeat yourself principle.  I'm only dispatching
>>>     to subs based on trusted information and the core of the idea is
>>>
>>> I should have written
>>>     I only use dispatch tables to dispatch to non-anonymous subs
>>>     based on trusted information.
>>>
>>> To me,
>>>     my $dispatch = {
>>>     'thing_one' => \&thing_one,
>>>     'thing_two' => \&thing_two,
>>>     };
>>> is an example of repetition.  For the case I wrote about, this code
>>> is not needed.  Since it's not needed I don't use it---it's just
>>> more code to support.  Given a word w, I always call a subroutine
>>> whose name can be computed from w.  In other words, I don't need to do
>>>     'thing_one' -> \&some_name_that_cannot_be_be computed_from_thing_one
>>>
>>> -mark
>>> _______________________________________________
>>> Purdue-pm mailing list
>>> Purdue-pm at pm.org
>>> https://mail.pm.org/mailman/listinfo/purdue-pm
>>>
>>
>>
>> --
>> Dave Jacoby
>> jacoby.david at gmail.com
>>
>> Don't panic when the crisis is happening, or you won't enjoy it.
>>
>
>
> --
> Dave Jacoby
> jacoby.david at gmail.com
>
> Don't panic when the crisis is happening, or you won't enjoy it.
>


-- 
Dave Jacoby
jacoby.david at gmail.com

Don't panic when the crisis is happening, or you won't enjoy it.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.pm.org/pipermail/purdue-pm/attachments/20190530/0335b80c/attachment-0001.html>


More information about the Purdue-pm mailing list