]> git.decadent.org.uk Git - maypole.git/blobdiff - lib/Maypole/Manual/Inheritance.pod
Added Maypole::Manual::Inheritance and Maypole::Manual::Terminology. Renamed Maypole...
[maypole.git] / lib / Maypole / Manual / Inheritance.pod
diff --git a/lib/Maypole/Manual/Inheritance.pod b/lib/Maypole/Manual/Inheritance.pod
new file mode 100644 (file)
index 0000000..2dd0eaf
--- /dev/null
@@ -0,0 +1,264 @@
+\r
+=head1 NAME\r
+\r
+Maypole::Manual::Inheritance - structure of a Maypole application\r
+\r
+=head1 DESCRIPTION\r
+\r
+Discusses the inheritance structure of a basic and a more advanced Maypole\r
+application.\r
+\r
+=head1 CONVENTIONS\r
+          \r
+=over 4\r
+\r
+=item inheritance\r
+\r
+        +\r
+        |\r
+    +--  --+\r
+        |\r
+       |\r
+       +\r
+       \r
+=item notes\r
+\r
+    target *-------- note about the target\r
+\r
+=item association\r
+\r
+    source ------> target\r
+\r
+=back\r
+\r
+=head1 A standard Maypole application\r
+\r
+A minimal Maypole application (such as the Beer database) consists of a \r
+custom driver class (BeerDB.pm), a set of auto-generated model classes, and a \r
+view class: \r
+\r
+                  THE DRIVER\r
+                                          +------- init() is a factory method,\r
+                   1      Maypole         |           it sets up the view\r
+   Maypole::Config <----- config();       |              classes\r
+   model();               init(); *-------+                           THE VIEW\r
+    |                     view_object(); -------+              \r
+    |    +--------------* setup();              |      Maypole::View::Base\r
+    |    |                   +                  |              +\r
+    |    |                   |                  |     1        |\r
+    |    |               Apache::MVC *-----+    +-----> Maypole::View::TT\r
+    |    |                   +             |             (or another view class)\r
+    |    |                   |             |\r
+    |    |                PLUGINS          |\r
+    |    |                   +             |\r
+    |    |                   |             +----- or CGI::Maypole\r
+    |    |                BeerDB                   or MasonX:::Maypole\r
+    |    |\r
+    |   setup() is a factory method,\r
+    |     it sets up the model\r
+    |         classes\r
+    |\r
+    |                                 THE MODEL\r
+    |\r
+    |  Maypole::Model::Base   Class::DBI\r
+    |             +             +\r
+    |             |             |                             \r
+    +-------> Maypole::Model::CDBI                            \r
+                      +                                       \r
+                      |                                       \r
+           +------------+--------+-------+---------+\r
+           |            |        |       |         |\r
+       BeerDB::Pub      |   BeerDB::Beer | BeerDB::Brewery\r
+       beers();         |   pubs();      | beers();\r
+                        |   brewery();   |\r
+                        |   style();     |\r
+          BeerDB::Handpump               |\r
+          pub();                      BeerDB::Style\r
+          beer();                     beers();\r
+\r
+\r
+=head2 What about Maypole::Application - loading plugins\r
+\r
+The main job of L<Maypole::Application> is to insert the plugins into the \r
+hierarchy. It ensures that L<BeerDB> inherits from the first plugin, which \r
+inherits from the next, etc., until the last plugin inherits from the frontend. \r
+\r
+It is also the responsibility of L<Maypole::Application> to decide which \r
+frontend to use. \r
+\r
+From Maypole 2.11, L<Maypole::Application> makes no appearance in the\r
+inheritance structure of a Maypole application. (In prior versions,\r
+L<Maypole::Application> would make itself inherit the plugins, and then insert\r
+itself in the hierarchy, but this was unnecessary).\r
+\r
+The order of inheritance is the same as the order in which plugins are supplied \r
+in the C<use Maypole::Application> statement. \r
+\r
+=head2 Who builds the model?\r
+\r
+First, remember we are talking about the standard, unmodified Maypole here. \r
+It is possible, and common, to override some or all of this stage and build a \r
+customised model.\r
+\r
+The standard model is built in 3 stages. \r
+\r
+First, C<Maypole::setup> calls C<setup_database> on the Maypole model class, in\r
+this case L<Maypole::Model::CDBI>. C<setup_database> then uses\r
+L<Class::DBI::Loader> to autogenerate individual L<Class::DBI> classes for each\r
+of the tables in the database (C<BeerDB::Beer>, C<BeerDB::Pub> etc).\r
+\r
+Next, C<Maypole::setup> B<unshifts> L<Maypole::Model::CDBI> onto the C<@ISA> \r
+array of each of these classes. \r
+\r
+Finally, the relationships among these tables are set up. Either do this\r
+manually, perhaps with the help of L<Class::DBI::Relationship>, or use\r
+L<Maypole::Plugin::Relationship>. In the latter case, you need to set up the\r
+relationships configuration before calling C<setup()>.\r
+\r
+\r
+=head1 An advanced Maypole application\r
+\r
+We'll call it C<BeerDB2>.\r
+\r
+Maypole is a framework, and you can replace different bits as you wish. So what \r
+follows is one example of good practice, other people may do things differently. \r
+\r
+We assume this application is being built from the ground up, but it will often\r
+be straightforward to adapt an existing L<Class::DBI> application to this\r
+general model.\r
+\r
+The main idea is that the autogenerated Maypole model is used as a layer on top\r
+of a separate L<Class::DBI> model. I am going to refer to this model as the\r
+'Offline' model, and the Maypole classes as the 'Maypole' model. The idea is\r
+that the Offline model can (potentially or in actuality) be used as part of\r
+another application, perhaps a command line program or a cron script, whatever.\r
+The Offline model does not know about the Maypole model, whereas the Maypole\r
+model does know about the Offline model.\r
+\r
+Let's call the offline model C<OfflineBeer>. As a traditional L<Class::DBI>\r
+application, individual table classes in this model will inherit from a common\r
+base (C<OfflineBeer>), which inherits from L<Class::DBI>).\r
+\r
+One advantage of this approach is that you can still use Maypole's autogenerated\r
+model. Another is that you do not mix online and offline code in the same\r
+packages.\r
+\r
+=head2 Building it\r
+\r
+Build a driver in a similar way as for the basic app, calling C<setup()> after\r
+setting up all the configuration. \r
+\r
+It is a good habit to use a custom Maypole model class for each application, as\r
+it's a likely first target for customisation. Start it like this:\r
+\r
+    package BeerDB2::Maypole::Model;\r
+    use strict;\r
+    use warnings;\r
+    use base 'Maypole::Model::CDBI';\r
+    1;\r
+    \r
+You can add methods which should be shared by all table classes to this package \r
+as and when required.\r
+    \r
+Configure it like this, before the C<setup()> call in the driver class:\r
+\r
+    # in package BeerDB2\r
+    __PACKAGE__->config->model('BeerDB2::Maypole::Model');\r
+    __PACKAGE__->setup;\r
+\r
+The C<setup()> call will ensure your custom model is loaded via C<require>.\r
+\r
+B<Note>: by default, this will create Maypole/CDBI classes for all the tables in\r
+the database. You can control this by passing options for L<Class::DBI::Loader>\r
+in the call to C<setup()>.\r
+\r
+For each class in the model, you need to create a separate file. So for\r
+C<BeerDB2::Beer>, you would write:\r
+\r
+    package BeerDB2::Beer;\r
+    use strict;\r
+    use warnings;\r
+    use base 'OfflineBeer::Beer';\r
+    1;\r
+    \r
+This package will be loaded automatically during C<setup()>, and\r
+C<BeerDB2::Maypole::Model> is B<unshifted> onto it's C<@ISA>. \r
+\r
+Configure relationships either in the individual C<OfflineBeer::*> classes, or\r
+else all together in C<OfflineBeer> itself i.e. not in the Maypole model. This \r
+way, you only define the relationships in one place.\r
+\r
+The resulting model looks like this:\r
+\r
+                                       Class::DBI\r
+    MAYPOLE 'MODEL'                       |\r
+                                          |\r
+   Maypole::Model::Base                   |\r
+           +                              |\r
+           |       +----------------------+-----------------+\r
+           |       |                                        |\r
+           |       |                                        |\r
+     Maypole::Model::CDBI                                   |     OFFLINE\r
+             +                                              |        MODEL\r
+             |                                              |\r
+     BeerDB2::Maypole::Model                           OfflineBeer\r
+       +                                                    +\r
+       |                                                    |\r
+       |                                                    |\r
+       +--- BeerDB2::Pub --------+ OfflineBeer::Pub --------+\r
+       |                           beers();                 |\r
+       |                                                    |\r
+       |                           OfflineBeer::Handpump ---+\r
+       |                           beer();                  |\r
+       |                           pub();                   |\r
+       |                                                    |\r
+       +--- BeerDB2::Beer -------+ OfflineBeer::Beer -------+\r
+       |                           pubs();                  |\r
+       |                           brewery();               |\r
+       |                           style();                 |\r
+       |                                                    |\r
+       +--- BeerDB2::Style ------+ OfflineBeer::Style ------+\r
+       |                           beers();                 |\r
+       |                                                    |\r
+       +--- BeerDB2::Brewery ----+ OfflineBeer::Brewery ----+\r
+                                   beers();\r
+\r
+\r
+\r
+=head3 Features\r
+\r
+Non-Maypole applications using the Offline model are completely isolated from\r
+the Maypole application, and need not know it exists at all.\r
+\r
+Methods defined in the Maypole table classes, override methods defined in the\r
+Offline table classes, because C<BeerDB2::Maypole::Model> was unshifted onto the\r
+beginning of each Maypole table class's C<@ISA>. Perl's depth first,\r
+left-to-right method lookup from e.g. C<BeerDB2::Beer> starts in\r
+C<BeerDB2::Beer>, then C<BeerDB2::Maypole::Model>, C<Maypole::Model::CDBI>,\r
+C<Maypole::Model::Base>, and C<Class::DBI>, before moving on to\r
+C<OfflineBeer::Beer> and finally C<OfflineBeer>.\r
+\r
+Methods defined in the Maypole model base class (C<BeerDB2::Maypole::Model>),\r
+override methods in the individual Offline table classes, and in the Offline\r
+model base class (C<Offline>). \r
+\r
+Relationships defined in the Offline classes are inherited by the Maypole model.\r
+\r
+The Maypole model has full access to the underlying Offline model. \r
+\r
+=head3 Theory \r
+\r
+This layout illustrates more clearly why the Maypole model may be thought of as\r
+part of the controller, rather than part of the model of MVC. Its function is to \r
+mediate web requests, translating them into method calls on the Offline model, \r
+munging the results, and returning them via the Maypole request object. \r
+\r
+Another way of thinking about it is that Maypole implements a two-layer\r
+controller. The first layer translates a raw request into a single method call\r
+on the Maypole model layer, which then translates that call into one or more\r
+calls on the underlying model.\r
+\r
+Whatever label you prefer to use, this approach provides for clear separation of\r
+concerns between the underlying model and the web/user interface, and that's\r
+what it's all about.\r
+          \r