The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::local::lib::helper::rationale - Why write this?

SYNOPSIS

A quick review of why I wrote this application and what problem it solves for me (and possible you.)

DISCUSSION

This document is intended to give futher details and examples of the type of problem I hade which caused me to write this application. For more technical details you should also review the brief documentation written at App::local::lib::helper or simply review the source code since there's not a lot of it :)

Generally speaking, I think local::lib is awesome. It deals very concisely with the age old problem of how to best manage Perl dependecies installed from CPAN. Using local::lib you can easily create a user level CPAN library root directory and install all your application dependencies into it. This way when you need a new CPAN module you don't need root access or to work with an administrator, nor do you worry about how your new CPAN modules might cause trouble for other users of Perl on a shared server. local::lib clarifies and simplifies your development and deployment workflow. If you are not convinced you should review the local::lib documentation first.

However there are two questions that users of local::lib have to answer. One (which is not going to be dealt with here) is "What is the best practice for locating and naming my locally lib managed directories?" For the purposes of the remaining discussion I will usually describe such a directory as being created under the user "home" directory ($HOME or ~ on Unix like machines) however I know some people prefer to create a 'extlib' directory within the root of the application that the local::lib is being created for. No sides on this question will be currently taken.

The second question, the one which this application was written as a possible answer to, is "How do I easily enable or disable Perl recognition of a particular local::lib?" To answer this lets step back and review how Perl searches for CPAN installed dependencies.

The Perl binary, when you install it, will automatically create a set of directories that are used as a sort of "path" to search for dependencies. You can see yours by typing perl -V from the command line. Here's what I see when I do so on my terminal:

  $ perl -V

    [EXTENSIVE OUTPUT SNIPPED]

  @INC:
    /Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1/darwin-thread-multi-2level
    /Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1
    /Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1/darwin-thread-multi-2level
    /Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1

Your default search path (or @INC) is typically installed into a global area on your machine, or as in the case above, when using perlbrew, into a central area in your $HOME directory. You can also see this by dumping @INC and $ENV{PATH}:

    $ perl -e 'use Data::Dumper; warn Dumper @INC, split(":",$ENV{PATH})'
 
    $VAR1 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1/darwin-thread-multi-2level';
    $VAR2 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1';
    $VAR3 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1/darwin-thread-multi-2level';
    $VAR4 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1';
    $VAR5 = '.';
    $VAR6 = '/Users/johnn/perl5/perlbrew/bin';
    $VAR7 = '/Users/johnn/perl5/perlbrew/perls/current/bin';
    $VAR8 = '/usr/bin';
    $VAR9 = '/bin';
    $VAR10 = '/usr/sbin';
    $VAR11 = '/sbin';
    $VAR12 = '/usr/local/bin';
    $VAR13 = '/usr/local/git/bin';
    $VAR14 = '/usr/X11/bin';
    $VAR15 = '/opt/local/bin';
    $VAR16 = '/opt/local/sbin';

Note the directory /Users/johnn/perl5/perlbrew/perls/current/bin is in $PATH This is so that any command line utilities installed by CPAN (such as App::Ack) will be easy to find and not require you to type in the full path to that tool.

Now, when you create a local::lib, Perl has no way to automatically detect it. Perhaps it would be cool if a future version of Perl could notice the presence of a local::lib and add it automatically, but that's not today. As of now you need to tell Perl to modify @INC and $PATH in such a way as that it can use your locally lib installed dependencies.

The documentation for local::lib recommends adding a bit of code to your .bashrc script (assuming you are on a Unixlike system.) This works well if you only have a single local::lib setup, however if you are like me and you create a new local::lib per application this is not going to work very well. You need a way to 'flip' a local::lib on and off as you move from one project to another. Additionally, for the purposes of deployment, you may wish for something that requires less setup.

One option is to manually 'activate' a particular local lib for each perl command you run.

    perl -I ~/mylib/lib/perl5 -Mlocal::lib=~/mylib [COMMAND]

You can see it work like so:

    $ perl -I ~/mylib/lib/perl5/ -Mlocal::lib=~/mylib/ -e 'use Data::Dumper; warn Dumper @INC, split(":",$ENV{PATH})'
    $VAR1 = '/Users/johnn/mylib/lib/perl5/darwin-thread-multi-2level';
    $VAR2 = '/Users/johnn/mylib/lib/perl5';
    $VAR5 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1/darwin-thread-multi-2level';
    $VAR6 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/5.10.1';
    $VAR7 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1/darwin-thread-multi-2level';
    $VAR8 = '/Users/johnn/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1';
    $VAR9 = '.';
    $VAR10 = '/Users/johnn/mylib/bin';
    $VAR11 = '/Users/johnn/perl5/perlbrew/bin';
    $VAR12 = '/Users/johnn/perl5/perlbrew/perls/current/bin';
    $VAR13 = '/usr/bin';
    $VAR14 = '/bin';
    $VAR15 = '/usr/sbin';
    $VAR16 = '/sbin';
    $VAR17 = '/usr/local/bin';
    $VAR18 = '/usr/local/git/bin';
    $VAR19 = '/usr/X11/bin';
    $VAR20 = '/opt/local/bin';
    $VAR21 = '/opt/local/sbin';

As you can see, the local lib is now added to @INC and $PATH has been altered. Additionally, a few other perl %ENV settings are created so that CPAN knows where to install dependencies.

This is a workable option for deployment, since you are probably scripting that and have limited interactivity needs. However it is onerous for developers.

Making it easier for developers is the basic reason for being for this application. The created helper script wraps the above effort into a single and hopefully short command. Additionally, its not limited to Perl commands. So you can use it like:

    ~/mylib/bin/localenv bash

And spawn off a subshell where your @INC, $PATH and other important bits have been properly setup. You can then exit the shell when you need to cancel the alterations. Or you can just use the command like

    ~/mylib/bin/localenv perl -V

And you get the expected output. I personally find this easier but your results may differ. Feedback and thoughts regarding improvements very welcomed.

EXAMPLES

The following are some example usage for this application

Setting up a development environment

Let's say you clone some application down from http://github.com and you are going to start development. You will need to setup a local::lib of that applications dependencies as part of your job. You can do so like (from the directory that contains the application Makefile.PL)

    curl -L http://cpanmin.us | perl - --local-lib ~/mylib App::local::lib::helper .

and then you can use the bash trick from above to flip on your local lib environment:

    ~/mylib/bin/localenv bash

and then you can start working, running code, or even adding to the dependency list.

Installing / Deploying an application

Say you want to install an application from CPAN and just run it on a shared server. Let's assume you have very little or no control beyond your $HOME directory.

    curl -L http://cpanmin.us | perl - --local-lib ~/mygitalist Gitalist App::local::lib::helper

Here we are running cpanminus directly off the web, installing Gitalist, a Catalyst based git web front end, and setting up a local lib helper for it.

We can then startup Gitalist:

    ~/mygitalist/bin/localenv gitalist_server.pl --repo_dir $REPO

Hopefully this eases some of your deployment issues!

Using in an applicatin or module Makefile.PL

If you add this as a requirement to your Makefile.PL and it is installed into a local::lib the helper will automatically be added.

Example Makefile.PL

    #!/usr/bin/env perl

    use strict;
    use warnings FATAL => 'all';
    use inc::Module::Install;

    requires 'App::local::lib::helper';
    ## rest of your file

AUTHOR

See App::local::lib::helper for Authorship information

COPYRIGHT & LICENSE

See App::local::lib::helper for Copyright and Licensing information.