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

NAME

App::installguide::hosted - Instructions on installing the App::Context framework in a web-hosting (non-root) environment

DESCRIPTION

These are instructions on installing the App::Context framework in a web-hosting (non-root) environment.

ASSUMPTIONS

 * You get command line access but not root.
 * You have access to a MySQL database engine (and permissions to create at least 3 databases)

SET UP THE CPAN SHELL

Installing software from CPAN into a non-system area requires a little setup.

 * Find CPAN/Config.pm and make a local copy of it to MyConfig.pm
 * Modify MyConfig.pm to use local directories

The first thing to do is to find CPAN/Config.pm.

 > which perl
 /usr/bin/perl

Since perl is installed in /usr/bin, the perl libraries are most likely stored in /usr/lib/perl5.

 > find /usr/lib/perl5 -name Config.pm -print
 /usr/lib/perl5/site_perl/5.8.7/x86_64-linux/Template/Config.pm
 /usr/lib/perl5/site_perl/5.8.7/Apache/Admin/Config.pm
 /usr/lib/perl5/5.8.5/CPAN/Config.pm
 /usr/lib/perl5/5.8.5/Net/Config.pm
 /usr/lib/perl5/5.8.7/x86_64-linux/Encode/Config.pm
 /usr/lib/perl5/5.8.7/x86_64-linux/Config.pm
 /usr/lib/perl5/5.8.7/CPAN/Config.pm
 /usr/lib/perl5/5.8.7/Net/Config.pm
 

Now make a copy of CPAN::Config.

 mkdir ~/.cpan
 mkdir ~/.cpan/CPAN
 cp /usr/lib/perl5/5.8.7/CPAN/Config.pm ~/.cpan/CPAN/MyConfig.pm

Now edit it.

 vi ~/.cpan/CPAN/MyConfig.pm

There might be lines like the following.

  'build_dir' => q[/root/.cpan/build],
  'cpan_home' => q[/root/.cpan],
  'histfile' => q[/root/.cpan/histfile],
  'keep_source_where' => q[/root/.cpan/sources],
  'makepl_arg' => q[],
  'make_install_arg' => q[UNINST=1],
  'mbuildpl_arg' => q[],

Change them to something like the following.

  'build_dir' => q[/home/username/.cpan/build],
  'cpan_home' => q[/home/username/.cpan],
  'histfile' => q[/home/username/.cpan/histfile],
  'keep_source_where' => q[/home/username/.cpan/sources],
  'makepl_arg' => q[PREFIX=/home/username],
  'make_install_arg' => q[],
  'mbuildpl_arg' => q[install_base=/home/username],

Then fire up the CPAN shell and install something. Then verify that it installed.

   # perl -MCPAN -e shell
   cpan> install App::Options
   cpan> exit
   # find ~/lib -name Options.pm -print
   /home/username/lib/perl5/site_perl/5.8.7/App/Options.pm

Yay. Success. We used the CPAN shell to install modules into our private perl library directory.

A REASONABLE DEVELOPMENT PROCESS AND PROMOTION PATH

The advice of this section is highly subject to preference. What is described here is a reasonable way to manage the development process of promoting code changes and database changes from development through test into production.

We use one development environment. An "environment" is a top-level directory which houses an instance of the application completely independent from the next instance of the application. (An alternate style for slightly bigger projects and teams would be to have one development "sandbox" environment per developer, but I'll skip that here.) The development environment points to the development database.

All environments that are not development environments are numbered releases (or release candidates) (i.e. "1.0.0"). A numbered release candidate of a web application is referred to as a test environment. It is pointed to the test database until all tests are completed and the release candidate is approved. Then the release candidate is pointed to the production database, and additional tests are performed that are not destructive to the data.

When it passes these final tests, it ceases to be a test environment any longer, and it becomes the latest production environment. A symbolic link is changed in order to point the "production" web application to the latest numbered version.

PREPARING YOUR SOFTWARE ENVIRONMENT DIRECTORIES

First we choose a base direction where all software environment directories will live.

   export PREFIX_BASE=$HOME/app

[Note: If you were doing this on a server where you had access to space outside the home directory, you might make this something like "/usr/mycompany" instead.]

Next, we prepare the environments. We will assume that we are preparing a "devel" environment, a single numbered release candidate "1.0.0" environment, and a symbolic link for production that points to the "1.0.0" environment.

   mkdir $PREFIX_BASE
   mkdir $PREFIX_BASE/devel
   mkdir $PREFIX_BASE/devel/etc
   mkdir $PREFIX_BASE/devel/etc/app
   mkdir $PREFIX_BASE/devel/bin
   mkdir $PREFIX_BASE/devel/lib
   mkdir $PREFIX_BASE/devel/src
   mkdir $PREFIX_BASE/devel/data
   mkdir $PREFIX_BASE/1.0.0
   mkdir $PREFIX_BASE/1.0.0/etc
   mkdir $PREFIX_BASE/1.0.0/etc/app
   mkdir $PREFIX_BASE/1.0.0/bin
   mkdir $PREFIX_BASE/1.0.0/lib
   mkdir $PREFIX_BASE/1.0.0/src
   mkdir $PREFIX_BASE/1.0.0/data
   ln -s $PREFIX_BASE/1.0.0 $PREFIX_BASE/prod

CONFIGURE YOUR LOGIN ENVIRONMENT

Investigate what the "lib" directories are on the system and decide which of those directories needs to be in the LD_LIBRARY_PATH (search path for libraries).

   # ls -ld /lib /*/lib /*/*/lib /*/*/*/lib

Investigate what the "man" directories are on the system and decide which of those directories needs to be in the MANPATH (search path for "man" pages).

   # ls -ld /man /*/man /*/*/man /*/*/*/man

Make sure we have something intelligent for the following variables in "~/.bash_profile".

   export PATH=$PATH:$HOME/bin

   export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib
   export MANPATH=/man:/usr/man:/usr/share/man:/usr/local/man:/usr/local/share/man
   export PERL5LIB=$HOME/lib/perl5:$HOME/lib/perl5/site_perl

   . prefix $HOME/app/devel

   set -o vi

Note: The prefix script comes with the App::Options distribution. It has been installed in $HOME/bin. The "prefix" script is bash-compatible and ksh-compatible. It enhances the PATH's that have been set as appropriate.

Note 2: "set -o vi" is a bash setting I like to make command-line editing like the vi-style of ksh. Leave this out if you like the default emacs-style command-line editing.

Then log out and log back in. Test that each variable had its desired effect.

   # which prefix
   # man ls
   # man App::Options
   # perl -MApp::Options -e 'print $App::Options::VERSION, "\n";'

INSTALL THE SOFTWARE

   # perl -MCPAN -e shell
   cpan> install App::Context
   cpan> install App::Repository
   cpan> install App::Widget
   cpan> exit

If any distribution fails to build (or any of the distributions they depend on), exit the CPAN shell immediately and try to build manually.

For instance, App::Context depends on Devel::StackTrace. If that module fails to install, then App::Context will fail. Exit the CPAN shell and build it by hand.

   cd ~/.cpan/build/Devel-StackTrace*
   perl Makefile.PL PREFIX=/home/username
   make
   make test
   make install

Or if the distribution uses Module::Build, you may do the following.

   perl Build.PL install_base=/home/username
   ./Build
   ./Build test
   ./Build install

Sometimes a distribution fails some of the tests. If it does, the CPAN shell will not install it. When you build it manually (as shown above), you may decide that it is an error in the test suite rather than an error in the modules themselves. In this case, you can continue with the "install" step. Then reenter the CPAN shell and install the next module.

IMPORTANT: If anyone discovers problems with these instructions or the distributions related to the App::Context framework, please report them to me:

   spadkins@gmail.com

[I want to make this framework dirt simple to set up and use. -- spa]

SETTING UP A MySQL DATABASE

This will vary depending on your web hosting provider (and presumes they support the use of MySQL databases). I went to a control panel and did all the setup. (This is all just an example of how I did it so that I can refer to this in later setup documentation. You will probably do this differently.)

I created three databases: "devel", "test", and "prod". The control panel prepended my hosting username, so they ended up being called "username_devel", "username_test", and "username_prod".

Then I created three users: "dbview", "dbuser", and "dbadmin". Again, the control panel prepended my hosting username, so they ended up being called "username_dbview", "username_dbuser", and "username_dbadmin". I gave them appropriate passwords and recorded what they were for future reference.

Then I went through each combination of database and database-user to assign permissions to each. I assigned all permissions to "username_dbadmin", "select/insert/update/delete" permissions to "username_dbuser" and only "select" permissions to "username_dbview".

I was then able to use the web-based database administration tool, "phpMyAdmin". However, I prefer to do things from the command line.

The next thing I did was put my MySQL login credentials into the $HOME/.my.cnf file.

   [client]
   user            = username_dbadmin
   password        = my_password_here
   host            = localhost

   [mysql]
   database        = username_devel

Then it is *very* important to set the permissions on this file.

   chmod 600 $HOME/.my.cnf

This will keep anyone from reading the contents of the file (which contains your database password). Then you should be able to log in directly with the mysql command line client. Some sample commands are shown, but it is assumed that you will read the MySQL documentation and know what you are doing.

   # mysql
   Welcome to the MySQL monitor.  Commands end with ; or \g.
   Your MySQL connection id is 1742668 to server version: 4.1.20-standard-log

   Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

   mysql> \s                             # show connection status
   mysql> show databases;                # show what databases defined
   mysql> show processlist;              # show all connections to the database
   mysql> show tables;                   # show all tables in this database
   mysql> use username_test;             # change to another database
   mysql> show tables;                   # show all tables in this database
   mysql> use username_prod;             # change back to the first database
   mysql> show tables;                   # show all tables in this database
   mysql> create table foo (foo_id integer not null auto_increment primary key,
          foo_dt date, foo_name varchar(255), foo_num integer) engine=MyISAM;
   mysql> show tables;
   mysql> drop table foo;
   mysql> create table foo (foo_id integer not null auto_increment primary key,
          foo_dt date, foo_name varchar(255), foo_num integer) engine=InnoDB;
   mysql> describe foo;
   mysql> show table status like 'foo';
   mysql> show indexes from foo;
   mysql> insert into foo (foo_dt, foo_name, foo_num) values ('2006-10-08','yowee',7);
   mysql> insert into foo (foo_dt, foo_name, foo_num) values ('2006-09-28','yowza',2);
   mysql> select * from foo;
   mysql> update foo set foo_num = 5 where foo_name = 'yowza';
   mysql> select * from foo;
   mysql> delete from foo where foo_dt = '2006-10-08';
   mysql> select * from foo;
   mysql> exit

SET UP THE FRAMEWORK TO READ THE DATABASE

CREATE app.conf

There are two kinds of configuration files in an application written for the App::Context framework: app.conf and app.pl.

The "app.conf" file is the low-level configuration file. "app.conf" is sometimes called the "options file" because it is read by App::Options. It contains parameters which are specific to the deployment rather than to the structure of the application. It can be thought of as a "deployment descriptor".

For our purposes, the only thing unique about our installation is the database connection information.

   vi $PREFIX/etc/app/app.conf

      dbhost = localhost
      dbname = username_prod
      dbuser = username_dbuser
      dbpass = my_password_here

   chmod 600 $PREFIX/etc/app/app.conf

CREATE app.pl

The "app.pl" file is the Application Configuration file. This is where you define (and assemble) Services (i.e. components) in the App::Context framework.

When an application is developed, a file like "app.pl" is part of the source code of that application. This file is not usually modified at the time it is deployed along with supporting code into production.

For our example, we configure the Repository named "dbprod" as a service in the "App::Repository::MySQL" class. Then we configure the "default" Repository as an alias for "dbprod".

   vi $PREFIX/etc/app/app.pl

      $conf = {
        Repository => {
          default => {
            alias => "dbprod",
          },
          dbprod => {
            class => "App::Repository::MySQL",
          },
        },
      };

TEST THE CONNECTION

With the App-Context distribution comes a generic utility, "app", that can be used to exercise any configured service.

Try the following.

   app Repository default get_rows foo

"app" takes the following arguments.

   1. The service type (here, it is "Repository")
   2. The service name (here, it is "default")
   3. The method to invoke on the service
   4+ The arguments to the method

Then it prints out whatever is returned from the method.

   # app Repository default get_rows foo
   $data = [
     [
       '2',
       '2006-09-28',
       'yowza',
       '5'
     ]
   ];

All programs that are built using the framework (with App-Options, App-Context, and App-Repository) (such as "app") get a number of built-in features for free. You can experiment with the following.

   app --help
   app --debug_options=3
   app --debug_conf
   app --debug_sql Repository default get_rows foo
   app --trace Repository default get_rows foo
   

YOUR FIRST WEB APPLICATION

ACKNOWLEDGEMENTS

 * Author:  Stephen Adkins <spadkins@gmail.com>
 * License: This is free software. It is licensed under the same terms as Perl itself.

SEE ALSO

App::quickstart, App::installguide, App::installguide::win32, App::Options, App::Context, App::Repository, App::Widget