From: David Baird Date: Fri, 4 Nov 2005 15:21:14 +0000 (+0000) Subject: Improved Maypole::load_model_subclass(). Added sequence X-Git-Tag: 2.11~109 X-Git-Url: https://git.decadent.org.uk/gitweb/?a=commitdiff_plain;h=85dcd6751d0499f04d3e64ae3a894cf878224da5;p=maypole.git Improved Maypole::load_model_subclass(). Added sequence diagrams to illustrate workflow. git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@409 48953598-375a-da11-a14b-00016c27c3ee --- diff --git a/ex/BeerDB.pm b/ex/BeerDB.pm index 778185a..466aa5e 100644 --- a/ex/BeerDB.pm +++ b/ex/BeerDB.pm @@ -2,7 +2,7 @@ package BeerDB; use Maypole::Application; use Class::DBI::Loader::Relationship; -sub debug { $ENV{BEERDB_DEBUG} } +sub debug { $ENV{BEERDB_DEBUG} || 0 } # This is the sample application. Change this to the path to your # database. (or use mysql or something) use constant DBI_DRIVER => 'SQLite'; diff --git a/lib/Maypole.pm b/lib/Maypole.pm index 6c6ff52..d8e893d 100644 --- a/lib/Maypole.pm +++ b/lib/Maypole.pm @@ -168,10 +168,47 @@ __PACKAGE__->config( Maypole::Config->new() ); __PACKAGE__->init_done(0); +=head1 HOOKABLE METHODS + +As a framework, Maypole provides a number of B - methods that are +intended to be overridden. Some of these methods come with useful default +behaviour, others do nothing by default. Likely hooks include: + + Class methods + ------------- + debug + setup + setup_model + load_model_subclass + init + + Instance methods + ---------------- + start_request_hook + is_model_applicable + get_session + authenticate + exception + additional_data + preprocess_path + =head1 CLASS METHODS =over 4 +=item debug + + sub My::App::debug {1} + +Returns the debugging flag. Override this in your application class to +enable/disable debugging. + +You can also set the C flag via L. + +=cut + +sub debug { 0 } + =item config Returns the L object @@ -230,17 +267,54 @@ sub setup_model { no strict 'refs'; unshift @{ $subclass . "::ISA" }, $config->model; - $config->model->adopt($subclass) - if $config->model->can("adopt"); - - # Load custom model code, if it exists - nb this must happen after the - # unshift, to allow code attributes to work - eval "use $subclass"; - die "Error loading $subclass: $@" - if $@ and $@ !~ /Can\'t locate \S+ in \@INC/; + + # Load custom model code, if it exists - nb this must happen after the + # unshift, to allow code attributes to work, but before adopt(), + # in case adopt() calls overridden methods on $subclass + $class->load_model_subclass($subclass); + + $config->model->adopt($subclass) if $config->model->can("adopt"); + +# eval "use $subclass"; +# die "Error loading $subclass: $@" +# if $@ and $@ !~ /Can\'t locate \S+ in \@INC/; } } +=item load_model_subclass($subclass) + +This method is called from C. It attempts to load the +C<$subclass> package, if one exists. So if you make a customized C +package, you don't need to explicitly load it. + +If, perhaps during development, you don't want to load up custom classes, you +can override this method and load them manually. + +=cut + +sub load_model_subclass +{ + my ($class, $subclass) = @_; + + my $config = $class->config; + + # Load any external files for the model base class or subclasses + # (e.g. BeerDB/DBI.pm or BeerDB/Beer.pm) based on code borrowed from + # Maypole::Plugin::Loader and Class::DBI. + if ( $subclass->require ) + { + warn "Loaded external module for '$subclass'\n" if $class->debug > 1; + } + else + { + (my $filename = $subclass) =~ s!::!/!g; + die "Loading '$subclass' failed: $@\n" + unless $@ =~ /Can\'t locate \Q$filename\E\.pm/; + warn "Did not find external module for '$subclass'\n" + if $class->debug > 1; + } +} + =item init Loads the view class and instantiates the view object. @@ -285,30 +359,6 @@ sub new Get/set the Maypole::View object -=item debug - - sub My::App::debug {1} - -Returns the debugging flag. Override this in your application class to -enable/disable debugging. - -You can also set the C flag via L. - -=cut - -sub debug { 0 } - -=item get_template_root - -Implementation-specific path to template root. - -You should only need to define this method if you are writing a new Maypole -backend. Otherwise, see L - -=cut - -sub get_template_root {'.'} - =back =head1 INSTANCE METHODS @@ -377,7 +427,7 @@ sub handler_guts { my ($self) = @_; - $self->__load_model; + $self->__load_request_model; my $applicable = $self->is_model_applicable; @@ -434,7 +484,7 @@ sub handler_guts return $self->__call_process_view; } -sub __load_model +sub __load_request_model { my ($self) = @_; $self->model_class( $self->config->model->class_of($self, $self->table) ); @@ -853,6 +903,25 @@ Turns post data and query string paramaters into a hash of C. You should only need to define this method if you are writing a new Maypole backend. +=cut + +sub parse_args +{ + die "parse_args() is a virtual method. Do not use Maypole directly; ". + "use Apache::MVC or similar"; +} + +=item get_template_root + +Implementation-specific path to template root. + +You should only need to define this method if you are writing a new Maypole +backend. Otherwise, see L + +=cut + +sub get_template_root {'.'} + =back =head2 Request properties @@ -1061,6 +1130,107 @@ sub make_random_id { =back +=head1 SEQUENCE DIAGRAMS + +See L for a detailed discussion of the sequence of +calls during processing of a request. This is a brief summary: + + INITIALIZATION + Model e.g. + BeerDB Maypole::Model::CDBI + | | + setup | | + o-------->|| | + || setup_model | setup_database() creates + ||------+ | a subclass of the Model + |||<----+ | for each table + ||| | | + ||| setup_database | | + |||--------------------->|| 'create' * + ||| ||----------> $subclass + ||| | | + ||| load_model_subclass | | + foreach |||------+ ($subclass) | | + $subclass ||||<----+ | require | + ||||--------------------------------------->| + ||| | | + ||| adopt($subclass) | | + |||--------------------->|| | + | | | + | | | + |-----+ init | | + ||<---+ | | + || | new | view_object: e.g + ||---------------------------------------------> Maypole::View::TT + | | | | + | | | | + | | | | + | | | | + | | | | + + + + HANDLING A REQUEST + + + BeerDB Model $subclass view_object + | | | | + handler | | | | + o-------->| new | | | + |-----> r:BeerDB | | | + | | | | | + | | | | | + | || | | | + | ||-----+ parse_location | | | + | |||<---+ | | | + | || | | | + | ||-----+ start_request_hook | | | + | |||<---+ | | | + | || | | | + | ||-----+ get_session | | | + | |||<---+ | | | + | || | | | + | ||-----+ handler_guts | | | + | |||<---+ | | | + | ||| class_of($table) | | | + | |||------------------------->|| | | + | ||| $subclass || | | + | |||<-------------------------|| | | + | ||| | | | + | |||-----+ is_model_applicable| | | + | ||||<---+ | | | + | ||| | | | + | |||-----+ call_authenticate | | | + | ||||<---+ | | | + | ||| | | | + | |||-----+ additional_data | | | + | ||||<---+ | | | + | ||| process | | fetch_objects + | |||--------------------------------->||-----+ | + | ||| | |||<---+ | + | ||| | || | + | ||| | || $action + | ||| | ||-----+ | + | ||| | |||<---+ | + | ||| | | | + | ||| process | | | + | |||------------------------------------------->|| template + | ||| | | ||-----+ + | ||| | | |||<---+ + | ||| | | | + | || send_output | | | + | ||-----+ | | | + | |||<---+ | | | + $status | || | | | + <------------------|| | | | + | | | | | + | X | | | + | | | | + | | | | + | | | | + + + =head1 SEE ALSO There's more documentation, examples, and a information on our mailing lists diff --git a/t/01basics.t b/t/01basics.t index e40fa85..5de19c8 100644 --- a/t/01basics.t +++ b/t/01basics.t @@ -2,12 +2,15 @@ use Test::More; use lib 'ex'; # Where BeerDB should live BEGIN { + #$ENV{BEERDB_DEBUG} = 2; + eval { require BeerDB }; Test::More->import( skip_all => "SQLite not working or BeerDB module could not be loaded: $@" ) if $@; plan tests => 18; + } use Maypole::CLI qw(BeerDB); use Maypole::Constants; @@ -42,7 +45,7 @@ is($classdata{list_columns}, 'score name price style brewery url', 'classdata.list_columns'); is ($classdata{related_accessors},'pubs','classdata.related_accessors'); -# test if successfully loaded customised model class +# test Maypole::load_custom_class() can_ok(BeerDB::Beer => 'fooey'); # defined in BeerDB::Beer can_ok(BeerDB::Beer => 'floob'); # defined in BeerDB::Base is_deeply( [@BeerDB::Beer::ISA], [qw/Maypole::Model::CDBI Class::DBI::SQLite BeerDB::Base/] );