Author: spadkins
Date: Mon Sep 4 11:25:18 2006
New Revision: 6844
Modified:
p5ee/trunk/App-Context/CHANGES
p5ee/trunk/App-Context/MANIFEST
p5ee/trunk/App-Context/Makefile.PL
p5ee/trunk/App-Context/lib/App/installguide/hosted.pod
p5ee/trunk/App-Context/lib/App/quickstart.pod
Log:
freeze for 0.964
Modified: p5ee/trunk/App-Context/CHANGES
==============================================================================
--- p5ee/trunk/App-Context/CHANGES (original)
+++ p5ee/trunk/App-Context/CHANGES Mon Sep 4 11:25:18 2006
@@ -2,6 +2,11 @@
# CHANGE LOG
#########################################
+VERSION 0.964
+ x mostly documentation updates
+ x moved Apache::Framework::App to Apache::App
+ x trying to improve Makefile.PL to install things in the right place (app in
cgi-bin)
+
VERSION 0.963
x App->use() now detects entries in the symbol table to inhibit an attempt to
load the module
x enhance App::Service::substitute() to understand default syntax (i.e.
{username:NULL})
Modified: p5ee/trunk/App-Context/MANIFEST
==============================================================================
--- p5ee/trunk/App-Context/MANIFEST (original)
+++ p5ee/trunk/App-Context/MANIFEST Mon Sep 4 11:25:18 2006
@@ -6,7 +6,7 @@
bin/app
bin/app-apache
bin/call
-lib/Apache/Framework/App.pm
+lib/Apache/App.pm
lib/App.pm
lib/App/Authentication.pm
lib/App/Authorization.pm
Modified: p5ee/trunk/App-Context/Makefile.PL
==============================================================================
--- p5ee/trunk/App-Context/Makefile.PL (original)
+++ p5ee/trunk/App-Context/Makefile.PL Mon Sep 4 11:25:18 2006
@@ -16,7 +16,7 @@
%opts = (
'NAME' => 'App-Context',
'DISTNAME' => 'App-Context',
- 'VERSION' => '0.963',
+ 'VERSION' => '0.964',
'EXE_FILES' => [ @programs ],
'PREREQ_PM' => {
'App::Options' => "0.01", # for loading a startup
configuration file
@@ -42,7 +42,7 @@
return <<EOF;
install ::
- @\$(MOD_INSTALL) cgi-bin "\$(PREFIX)/cgi-bin"
+ @\$(CP) "\$(PREFIX)/bin/app" "\$(PREFIX)/cgi-bin/app"
EOF
}
Modified: p5ee/trunk/App-Context/lib/App/installguide/hosted.pod
==============================================================================
--- p5ee/trunk/App-Context/lib/App/installguide/hosted.pod (original)
+++ p5ee/trunk/App-Context/lib/App/installguide/hosted.pod Mon Sep 4
11:25:18 2006
@@ -99,7 +99,7 @@
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 long, and it becomes the latest production 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.
Modified: p5ee/trunk/App-Context/lib/App/quickstart.pod
==============================================================================
--- p5ee/trunk/App-Context/lib/App/quickstart.pod (original)
+++ p5ee/trunk/App-Context/lib/App/quickstart.pod Mon Sep 4 11:25:18 2006
@@ -22,7 +22,7 @@
I got started building the App::Context framework a while ago.
The documentation is very limited. This is an effort to bring it all together.
This list shows how all of the documentation fits together and in what state
-it is [???=unknown,RED=incomplete,YEL=ok,GRN=complete].
+it is.
=head2 PURE DOCUMENTATION
@@ -86,9 +86,9 @@
=item * +-- L<App::Context::Server> - A program running in a multi-process
server context.
-=item * +-- L<App::Context::ClusterController> - Running in a multi-node
cluster context.
+=item * =====+-- L<App::Context::ClusterController> - Running in a multi-node
cluster context.
-=item * +-- L<App::Context::ClusterNode> - Running on a single node of a
cluster.
+=item * =====+-- L<App::Context::ClusterNode> - Running on a single node of a
cluster.
=item * +-- L<App::Context::NetServer> - Another flavor of server context
(not yet implemented).
@@ -112,27 +112,27 @@
=item * +-- L<App::Conf>
-=item * +-- L<App::Conf::File>
+=item * =====+-- L<App::Conf::File>
=item * L<App::Service>
=item * +-- L<App::Serializer>
-=item * +-- L<App::Serializer::Properties>
+=item * =====+-- L<App::Serializer::Properties>
-=item * +-- L<App::Serializer::Ini>
+=item * =====+-- L<App::Serializer::Ini>
-=item * +-- L<App::Serializer::Perl>
+=item * =====+-- L<App::Serializer::Perl>
-=item * +-- L<App::Serializer::Xml>
+=item * =====+-- L<App::Serializer::Xml>
-=item * +-- L<App::Serializer::Yaml>
+=item * =====+-- L<App::Serializer::Yaml>
-=item * +-- L<App::Serializer::OneLine>
+=item * =====+-- L<App::Serializer::OneLine>
-=item * +-- L<App::Serializer::TextArray>
+=item * =====+-- L<App::Serializer::TextArray>
-=item * +-- L<App::Serializer::Storable>
+=item * =====+-- L<App::Serializer::Storable>
=item * +-- L<App::SessionObject>
@@ -148,13 +148,13 @@
=item * +-- L<App::CallDispatcher>
-=item * +-- L<App::CallDispatcher::HTTPSimple>
+=item * =====+-- L<App::CallDispatcher::HTTPSimple>
=item * +-- L<App::ResourceLocker>
-=item * +-- L<App::ResourceLocker::IPCSemaphore>
+=item * =====+-- L<App::ResourceLocker::IPCSemaphore>
-=item * +-- L<App::ResourceLocker::IPCLocker>
+=item * =====+-- L<App::ResourceLocker::IPCLocker>
=item * L<Apache::Framework::App>
@@ -174,11 +174,12 @@
cpan> exit
If it's not this easy, I need to work on making it easier.
+Please let me know ([EMAIL PROTECTED]) and I'll fix it.
-=head1 QUICK START TO COMMAND LINE APPLICATIONS
+=head1 QUICK START TO COMMAND LINE DATABASE PROGRAMS
-At the moment, the biggest advantage of using the App::Context Framework for
-programs which are not web applications is in developing a suite of database
programs
+One of the big advantages of using the App::Context Framework for
+command-line programs is in developing a suite of database programs
(particularly for a MySQL database).
Choose a root directory for your system, assigning it to the PREFIX variable.
@@ -202,6 +203,7 @@
db => {
class => "App::Repository::MySQL",
},
+ },
};
vi $PREFIX/bin/prog
@@ -213,18 +215,19 @@
option => {
dbhost => {
description => "database host",
- default => "localhost",
+ required => 1,
},
dbname => {
description => "database name",
- default => "test",
+ required => 1,
},
dbuser => {
description => "database user",
- default => "scott",
+ required => 1,
},
dbpass => {
description => "database password",
+ required => 1,
},
},
);
@@ -239,5 +242,183 @@
# my $age = $db->get("customer", { customer_id => 45 }, "age");
}
+The best parts about writing database programs with App::Repository in the
App::Context framework
+is that all of your programs get automatic SQL debugging, timing, and
explaining.
+
+ prog --debug_sql # show all SQL statements and their timings
+ prog --debug_sql=2 # show all SQL statements and their timings and all
rows returned from selects
+ prog --debug_sql --explain_sql # show SQL statements and explain them
+
+See L<App::Options>, L<App::Repository::quickstart>, and
L<App::Repository::devguide> for more info on this.
+
+=head1 QUICK START TO OBJECT-ORIENTED COMMAND LINE PROGRAMS
+
+Another of the big advantages of using the App::Context Framework for
+command line programs is in developing programs which use and operate on
Business Objects
+(the object-oriented analysis concept, not the commercial reporting software).
+
+Many "object-oriented" programs are written without ever using a true Business
Object.
+The reason for this is that the issues of maintaining object state are so
tricky that
+people rarely get around to using Business Objects in their command line
programs.
+
+Business Objects come in two types: Business Entity Objects and Business
Process Objects.
+Business Entity Objects represent things in the business realm: Customer,
Store, Employee,
+Product, PurchaseOrder. Business Process Objects are those objects that model
business
+processes.
+
+The following abstractions exist in order to subclass for your business
objects.
+As you get into using them, you will learn when to use which one, and you will
find that
+there is of course more than one way to do it.
+
+=over
+
+=item * L<App::RepositoryObject> - An object whose state is shared across
multiple sessions as a single row in a database (repository). These are
generally the Business Entity Objects.
+
+=item * L<App::SessionObject> - A named object whose state is maintained
throughout the session. These are generally the Business Process Objects.
+
+=item * L<App::SessionObject::RepositoryObjectSet> - A named object with
session state and shared state consisting of a set of rows from a single table
in a database (repository).
+
+=item * L<App::SessionObject::RepositoryObjectDomain> - A named object with
session state and shared state consisting of a set of related sets of rows from
multiple tables in a database (repository).
+
+=back
+
+The big advantage of writing object-oriented programs is for code reuse.
+Whenever someone whips up a perl script to do some processing on the system,
that
+programming logic is unavailable for use in any other program (such as an
interactive
+web application).
+
+One way to cure this is to have an L<App::SessionObject> where all of the logic
+for a command line program is implemented as a single method call.
+
+The choice of how many SessionObjects to use for all of the logic is a matter
of
+preference. You could put all this logic on one kitchen-sink-style
SessionObject
+or you could group the functions into like types and put them on different
+SessionObjects. You may wish to have these SessionObject take on the
responsibilities
+of different "roles" played by members of the organization or tasks performed
by
+different departments. These can easily be reorganized later. The important
+thing is to get the logic out of the command line programs and into objects
+so that the logic can be reused later.
+
+When viewed in this light, the command line program only becomes a user
interface
+for a method call provided by a SessionObject. The easy way to start is just
to
+pass the %App::options hash (shown below). However, in many cases the program
+will do a few more steps to make a more natural call to the SessionObject's
method.
+
+=head2 EXAMPLES
+
+vi $PREFIX/etc/app.pl
+
+ $conf = {
+ Repository => {
+ default => { alias => "db", },
+ db => {
+ class => "App::Repository::MySQL",
+ },
+ },
+ SessionObject => {
+ credit_manager => {
+ class => "App::SessionObject::CreditManager",
+ },
+ },
+ };
+
+vi $PREFIX/lib/App/SessionObject/CreditManager.pm
+
+ use App;
+ use App::Repository;
+ package App::SessionObject::CreditManager;
+ $VERSION = "0.50";
+ @ISA = ("App::SessionObject");
+ use strict;
+
+ sub check_customer_credit {
+ my ($self, $options) = @_;
+ my $customer_id = $options->{customer_id} || die "No customer ID
provided";
+ my $context = $self->{context}; # every Service has a reference to its
Context
+ my $db = $context->repository(); # get the default repository from the
Context
+
+ # There must be a table named customer with primary key of "customer_id"
and
+ # at least the columns "approval_ind char(1)", "denial_msg varchar(255)",
+ # "first_name varchar(255)", and "last_name varchar(255)".
+ my $customer = $db->get_object("customer", $customer_id); # returns an
App::RepositoryObject
+
+ my ($approval_ind, $denial_msg);
+
+ # ... do some processing ...
+ if ($customer->{first_name} eq "Bill" && $customer->{last_name} eq
"Gates") {
+ $approval_ind = "N";
+ $denial_msg = "Too rich";
+ }
+ else {
+ $approval_ind = "N";
+ }
+
+ if ($approval_ind eq "Y") {
+ $customer->set("approval_ind",$approval_ind);
+ print "Customer $customer_id approved.\n";
+ }
+ else {
+
$customer->set(["approval_ind","denial_msg"],[$approval_ind,$denial_msg]);
+ print "Customer $customer_id denied: $denial_msg.\n";
+ }
+ }
+
+ 1;
+
+vi $PREFIX/bin/check_customer_credit
+
+ #!/usr/bin/perl -w
+ # NOTE: All of this code is about the User Interface to the logic.
+ # The logic itself is hidden away in
"App::SessionObject::CreditManager".
+ use strict;
+ use App::Options (
+ options => [qw(dbhost dbname dbuser dbpass customer_id)],
+ option => {
+ dbhost => {
+ description => "database host",
+ required => 1,
+ },
+ dbname => {
+ description => "database name",
+ required => 1,
+ },
+ dbuser => {
+ description => "database user",
+ required => 1,
+ },
+ dbpass => {
+ description => "database password",
+ required => 1,
+ },
+ customer_id => {
+ description => "the ID of the customer to check",
+ required => 1,
+ type => "integer",
+ },
+ },
+ );
+ use App;
+ use App::Repository;
+ {
+ my $context = $App->context();
+ my $db = $context->repository();
+ my $credit_manager = $context->session_object("credit_manager");
+ $credit_manager->check_customer_credit(\%App::options);
+ }
+
+=head1 QUICK START TO WEB PROGRAMS
+
+A web program is simply choosing a widget to display on the browser.
+Widgets may be arbitrarily complex, containing other widgets, and so taking
+the form of an entire web application. Since any widget can be viewed in a
+browser, additional widgets may be developed and debugged before they are
+integrated into a larger applications.
+
+Widgets (L<App::Widget>) are simply SessionObject's with an html() method
+which allows them to be viewed nicely by a web browser. They maintain their
+state throughout the duration of the session.
+
+
+
=cut