]> git.decadent.org.uk Git - maypole.git/commitdiff
Doc outline.
authorSimon Cozens <simon@simon-cozens.org>
Wed, 10 Mar 2004 11:34:21 +0000 (11:34 +0000)
committerSimon Cozens <simon@simon-cozens.org>
Wed, 10 Mar 2004 11:34:21 +0000 (11:34 +0000)
git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@81 48953598-375a-da11-a14b-00016c27c3ee

doc/About.pod [new file with mode: 0644]
doc/Beer.pod [new file with mode: 0644]
doc/Flox.pod [new file with mode: 0644]
doc/Model.pod [new file with mode: 0644]
doc/Overview.pod [new file with mode: 0644]
doc/Request.pod [new file with mode: 0644]
doc/StandardTemplates.pod [new file with mode: 0644]
doc/View.pod [new file with mode: 0644]

diff --git a/doc/About.pod b/doc/About.pod
new file mode 100644 (file)
index 0000000..60c3ab7
--- /dev/null
@@ -0,0 +1,12 @@
+
+=head1 Introduction to the Maypole Request Model
+
+=head2 What is MVC for web applications?
+
+=head2 What is Maypole?
+
+=head2 Phases of a Maypole request
+
+=head2 The model class
+
+=head2 The view class
diff --git a/doc/Beer.pod b/doc/Beer.pod
new file mode 100644 (file)
index 0000000..33baddf
--- /dev/null
@@ -0,0 +1,7 @@
+=head1 The Beer Database, Twice
+
+=head2 The big beer problem
+
+=head2 The easy way
+
+=head2 The hard way
diff --git a/doc/Flox.pod b/doc/Flox.pod
new file mode 100644 (file)
index 0000000..ee0a96a
--- /dev/null
@@ -0,0 +1 @@
+=head1 FlOx: A Social Networking Site
diff --git a/doc/Model.pod b/doc/Model.pod
new file mode 100644 (file)
index 0000000..1468de2
--- /dev/null
@@ -0,0 +1,9 @@
+=head1 Maypole Model Classes
+
+=head2 Class::DBI
+
+=head2 Maypole::Model::CDBI
+
+=head2 What Maypole wants from a model
+
+=head2 Building your own model class
diff --git a/doc/Overview.pod b/doc/Overview.pod
new file mode 100644 (file)
index 0000000..8f5382e
--- /dev/null
@@ -0,0 +1,32 @@
+=head1 Overview of Maypole Documentation
+
+The Maypole documentation is arranged over several files; this is a good
+one to start with.
+
+Once you've read this, you should probably look at L<About.pod>, the
+guide to what Maypole is and how the Maypole request works. It also
+describes how to set up a simple CRUD web application in Maypole.
+
+The next two chapters are quite thorough, and you might want to skip
+over them if you already know how L<Class::DBI> and the L<Template>
+toolkit work. L<Model.pod> and L<View.pod> describe these technologies
+and their relationship to Maypole.
+
+Now we present the default actions and templates - the F<factory>
+templates - in L<StandardTemplates.pod>. When you have read these
+chapters, you are ready to start building more complex applications in
+Maypole than just a simple CRUD interface. L<Beer.pod> reintroduces the
+beer database application and shows how to move from the "magic" of the
+automatically supplied templates and actions to a customized application
+with user-specified actions.
+
+L<Request.pod> contains more information about the Maypole request
+object and provides some cookbook-style techniques: how to provide
+authentication, how to provide a REST interface, how to override various
+stages of the content generation process, and so on.
+
+The final chapters are examples of how to construct large web
+applications in Maypole: L<Flox.pod> describes a "social network" site
+similar to Friendster and Orkut; L<PetStore.pod> implements the Java/C#
+benchmark Pet Store application; and L<Recipe.pod> describes a kitchen
+management application.
diff --git a/doc/Request.pod b/doc/Request.pod
new file mode 100644 (file)
index 0000000..815ea1d
--- /dev/null
@@ -0,0 +1,9 @@
+=head1 Maypole Request Hacking Cookbook
+
+=head2 Changing how params are parsed
+
+=head2 Authentication
+
+=head2 REST
+
+=head2 Filling Content Yourself
diff --git a/doc/StandardTemplates.pod b/doc/StandardTemplates.pod
new file mode 100644 (file)
index 0000000..c3c4dd3
--- /dev/null
@@ -0,0 +1,208 @@
+
+=head1 Maypole's Standard Templates and Actions
+
+As we saw in our CRUD example, Maypole does all it can to make your life
+easier; this inclues providing a set of default actions and
+factory-supplied templates. These are written in such a generic way,
+making extensive use of class metadata, that they are more or less
+applicable to any table or application. However, in order to progress
+from automatically generated CRUD applications to real customized
+applications, we need to begin by understanding how these default
+actions do their stuff, and how the default templates are put together.
+Once we have an understanding of what Maypole does for us automatically,
+we can begin to customize and create our own templates and actions.
+
+=head2 The standard actions
+
+A simple, uncustomized Maypole model class, such as one of the classes
+in the beer database application, provides the following default actions
+- that is, provides access to the following URLs:
+
+=over 3
+
+=item C</[table]/view/[id]>
+
+This takes the ID of an object in a table, retrieves the object, and
+presents it to the F<view> template.
+
+=item C</[table]/edit/[id]>
+
+This is the same as C<view>, but uses the F<edit> template to provide a
+web form to edit the object; it submits to C<do_edit>.
+
+=item C</[table]/do_edit/[id]>
+
+=item C</[table]/do_edit/>
+
+This provides both editing and row creation facilities. 
+
+=item C</[table]/delete/id>
+
+This deletes a row, returning to the C<list> page.
+
+=item C</[table]/list/>
+
+This provides a paged list of the table suitable for browsing.
+
+=item C</[table]/search/>
+
+This handles a search query and presents the search results back to the
+F<list> template.
+
+=back
+
+We'll now look at how these actions are implemented, before moving on to
+take a detailed look at the templates they drive.
+
+=head3 C<view> and C<edit>
+
+These actions are very simple; their job is to take a row ID, turn it
+into an object, and hand it to the template to be displayed. However, as
+taking the first argument and turning it into an object is such a common
+action, it is handled directly by the model class's C<process> method.
+Similarly, the default template name provided by the C<process> method
+is the name of the acction, and so will be C<view> or C<edit>
+accordingly. 
+
+So the code required to make these two actions work turns out to be:
+
+    sub view :Exported { }
+    sub edit :Exported { }
+
+That's right - no code at all. This shows the power of the templating
+side of the system. If you think about it for a moment, it is natural
+that these actions should not have any code - after all, we have
+separated out the concerns of "acting" and displaying. Both of these
+"actions" are purely concerned with displaying a record, and don't need
+to do any "acting". Remember that the "edit" method doesn't actually do
+any editing - this is provided by C<do_edit>; it is just another view of
+the data, albeit once which allows the data to be modified later. These
+two methods don't need to modify the row in any way, they don't need to
+do anything clever. They just are.
+
+So why do we need the subroutines at all? If the subroutines did not exist,
+we would be sent to the C<view> and C<edit> templates as would be
+expected, but these templates would not be provided with the right
+arguments; we need to go through the C<process> method in order to turn
+the URL argument into a row and thence into an object to be fed to the
+template. By exporting these methods, even though they contain no code
+themselves, we force Maypole to call C<process> and provide the class
+and object to the templates.
+
+The moral of this story is that if you need to have an action which is
+purely concerned with display, not acting, but needs to receive an ID
+and turn it into an object, then create an empty method. For instance,
+if we want to make an alternate view of a row which only showed the
+important columns, we might create a method
+
+    sub short_view :Exported {}
+
+This will cause the row to be turned into an object and fed to the
+C<short_view> template, and that template would be responsible for
+selecting the particular columns to be displayed.
+
+=head3 C<do_edit>
+
+This action, on the other hand, actually has to do something. If it's
+provided with an ID, this is turned into an object and we're in edit
+mode, acting upon that object. If not, we're in create mode. 
+
+    sub do_edit :Exported {
+        my ($self, $r) = @_;
+        my $h = CGI::Untaint->new(%{$r->{params}});
+        my ($obj) = @{$r->objects || []};
+        if ($obj) {
+            # We have something to edit
+            $obj->update_from_cgi($h);
+        } else {
+            $obj = $self->create_from_cgi($h);
+        }
+
+The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
+into database table data. This in turn uses C<CGI::Untaint> to ensure
+that the data coming in is suitable for the table. If you're using the
+default C<CDBI> model, then, you're going to need to set up your tables
+in a way that makes C<FromCGI> happy.
+
+=over 
+
+=item Digression on C<Class::DBI::FromCGI>
+
+C<CGI::Untaint> is a mechanism for testing that incoming form data
+conforms to various properties. For instance, given a C<CGI::Untaint>
+object that encapsulates some C<POST> parameters, we can extract an
+integer like so:
+
+    $h->extract(-as_integer => "score");
+
+This checks that the C<score> parameter is an integer, and returns it if
+it is; if not, C<< $h->error >> will be set to an appropriate error
+message. Other tests by which you can extract your data are C<as_hex>
+and C<as_printable>, which tests for a valid hex number and an ordinary
+printable string respectively; there are other handlers available on
+CPAN, and you can make your own, as documented in L<CGI::Untaint>.
+
+To tell the C<FromCGI> handler what handler to use for each of your
+columns, you need to use the C<untaint_columns> methods in the classes
+representing your tables. For instance:
+
+    BeerDB::Beer->untaint_columns(
+        integer => ["score", ... ],
+    );
+
+This must be done after the call to C<setup> in your handler, because
+otherwise the model classes won't have been set up to inherit from
+C<Class::DBI::FromCGI>.
+
+Remember that if you want to use drop-downs to set the value of related
+fields, such as the brewery for a beer, you need to untaint these as
+something acceptable for the primary key of that table:
+
+    BeerDB::Beer->untaint_columns(
+        integer => ["score", "brewery", "style" ],
+        ...
+    );
+
+This is usually integer, if you're using numeric IDs for your primary
+key. If not, you probably want C<printable>, but you probably know what
+you're doing anyway.
+
+=back
+
+The data is untainted, and any errors are collected into a hash which is
+passed to the template. We also pass back in the parameters, so that the
+template can re-fill the form fields with the original values. The user
+is then sent back to the C<edit> template.
+
+        if (my %errors = $obj->cgi_update_errors) {
+            # Set it up as it was:
+            warn "There were errors: ".Dumper(\%errors)."\n";
+            $r->{template_args}{cgi_params} = $r->{params};
+            $r->{template_args}{errors} = \%errors;
+            $r->{template} = "edit";
+        }
+
+Otherwise, the user is taken back to viewing the new object:
+
+    } else {
+        $r->{template} = "view";
+    }
+    $r->objects([ $obj ]);
+
+Notice that this does use hard-coded names for the templates to go to next.
+Feel free to override this in your subclasses:
+
+    sub do_edit :Exported {
+        my ($class, $r) = shift;
+        $class->SUPER::do_edit($r);
+        $r->template("my_edit");
+    }
+
+=head3 delete
+
+
+=head3 list
+
+=head3 search
+
+=head2 The templates and macros
diff --git a/doc/View.pod b/doc/View.pod
new file mode 100644 (file)
index 0000000..a5bade0
--- /dev/null
@@ -0,0 +1,9 @@
+=head1 Maypole View Classes
+
+=head2 The Template Toolkit
+
+=head2 Maypole::View::TT
+
+=head2 What Maypole provides to a view
+
+=head2 Building your own view class