MOP4Import-Declare

 view release on metacpan or  search on metacpan

intro_runnable_module.pod  view on Meta::CPAN

=encoding utf-8

=head1 NAME

intro_runnable_module - A brief introduction to Runnable-Module design pattern

=head1 INTRO

This document briefly describes a programming idiom (or design
pattern, maybe) which I call it as I<Runnable-Module>.
It seems to me that it is not so well known in perl community
but I can find L<some|https://www.perlmonks.org/?node_id=621378> L<articles|https://www.perlmonks.org/?node_id=396759> for it. Same pattern can also be found in other dynamic scripting language like L<python|https://stackoverflow.com/questions/419163...
sure it already has better name. If you know about it, please tell me.

Note: original version of this document (written in Japanese) can be found at
my blog post: L<https://hkoba.hatenablog.com/entry/2017/09/06/185029>
日本語版もあるよ!.

=head1 Runnable module - What, Why and How

I<Runnable-module> is a programming idiom which allows you to write
a script to be used both as a standalone program and as a library file
(can be used via C<use>, C<require>). In perl, it is usually implemented using
C<unless caller> block.

=head2 What is C<unless caller>?

Have you ever read perl code which ends with something like following?:

    unless (caller) {
      ...Some interesting code...
    }

This C<unless (caller) {...}> guards C<...> portion of code to run
only when this program file is directly executed.
When the same script is C<eval()>ed as a string or C<require()>d as a module, the C<...> portion is not executed. I knew this idiom in context of L<Tk>
on comp.lang.perl.tk IIRC and used it
like L<this post|https://groups.google.com/d/msg/comp.lang.perl.tk/qFyt08fZhGo/uzI1QmL9ZKkJ>. At that time, it was used like:


    MainLoop unless caller;

and achieved following tricks:

=over 4

=item * When this script is executed directly, run C<Tk::MainLoop()>
so that correctly start GUI drawing and event loop.

=item * Otherwise (i.e. C<eval()>ed from clipboard and/or C<do "script">) do nothing.

=back

=head2 Let's write F<MyScript.pm> instead of F<myscript.pl>

This C<unless caller> idiom is useful not only in Tk scripts,
but also in normal perl scriptings because it enables you
to write dual purpose script: your script can be used as a module
and also as a standalone script.

To achieve it, what you need is

=over 4

=item 1
Name your script like F<MyScript.pm> instead of F<myscript.pl>.

=item 2
Do C<chmod a+x MyScript.pm> from shell.

=item 3
Add shbang C<#!/usr/bin/env perl> at first line of it.

=item 4
Add C<package MyScript;> declaration and also C<1;> at the end of this script.

=back

And finally, you can write some codes guarded in C<unless (caller) {...}> block
for standalone mode. Here is typical skeleton of such script.

    #!/usr/bin/env perl
    package MyScript;
    
    ...
    
    unless (caller) {
       my @opts; 
       push @opts, split /=/, $_, 2 while @ARGV and $ARGV[0] =~ /=/; # XXX:minimum!
       my $app = MyScript->new(@opts);
       $app->main(@ARGV);
    }
    
    1;

Now, your script became a I<Runnable-Module>. You can use this F<MyScript.pm>
not only as a CLI tool (don't forget C<chmod a+x>;-), but also as a module
and call some internal functions/methods freely.


    # Invoke as a command and execute MyScript->new(x=>100,y=>100)->main('foo','bar')
    % ./MyScript.pm x=100 y=100 foo bar
    
    # Use as a module, instantiate and call method foo
    % perl -I. -MMyScript -le 'print MyScript->new->foo'


=head2 Dispatching subcommands to methods turns your script into multi-role editor

In above example, C<unless (caller) {...}> block is hard-wired to call



( run in 2.730 seconds using v1.01-cache-2.11-cpan-84e82930d8c )