|
NAMECGI::Builder - Framework to build simple or complex web-appsVERSION 1.36Included in CGI-Builder 1.36 distribution.The latest versions changes are reported in the Changes file in this distribution. INSTALLATION
SYNOPSIS# define your build use CGI::Builder qw| CGI::Builder::AnyExtension AnySuperClass |; DESCRIPTIONThis is the starting point of the documentation of the CGI Builder Framework (CBF). You should read this documentation before any other documentation in any other module that extends the CBF.IMPORTANT INFOThe CBF is growing quickly, likewise its features, documentation and resources: if you use it and you have not subscribed the cgi-builder-announce mailing list, you should remember to check at least monthly for new releases and ALWAYS read the Changes file.Mailing listsThe CBF has 3 mailing lists which may be very useful for developement:
LinksA simple and useful navigation system between the various CBF Extensions (and other modules used by the CBF) is available at this URL: <http://perl.4pro.net>JobsIf you are interested in being hired as a developer for any commercial project, and if you have a good knowledge of the CBF, please send me a message with your CV including you usual hourly and monthly rate.ApplicationsIf you realize any interesting application by using the CBF and you want to share its link to increment your traffic, please send me a message. I am planning to publish a list of real world applications to show as successful CBF examples to the users. This will be a place where to show also information about your skills and other useful data about your work.About CGI::Application and CGI::Application::PlusImportant Note: If you are familiar with the API of these two modules, you will appreciate the CGI::Builder::CgiAppAPI which supplies an API compatible with those modules. It supplies also very useful hints which will suggest you what to change in your old code and what to read in the documentation in order to smoothly trasform your old cgiapp into a CGI::Builder application or simply learn this framework faster.POD Conventions
CGI Builder Framework (CBF)Definition: A "framework" in object-oriented systems, is a set of classes that embodies an abstract design for solutions to a number of related problems.In simpler and more specific words, CBF is a set of modules providing the structure, the features and the solutions you need to easily write scalable, expandable, reusable and easy to maintain web applications (sometime called just 'CGI script' :-). In even simpler words: if you invest a few hours in learning this documentation, you will save a lot of hours writing your future CGI scripts ;-). Features
ConceptIn a (very simplified) web client-server transaction, when a client requests a static html page to the server, the server sends that page to the client; when the object of the request is a CGI script, that script is supposed to somehow create the 'page' to be sent to the client.Note: In the CBF metaphor, the page concept is not strictly related with the HTML file concept: 'page' is just the most used name of the entity that is the object of the request/response transaction, and so we use it as a simple synonym of requested/served entity. The CBF metaphor is constructed around this simple concept: the web application using the CBF is interfaced with the client through 'pages'. A page (page_name) is requested by the client and a page (page_content) is sent to the client as the response; what happens in between is a totally customizable application process that your application inherits from the CBF. The application process is segmented into "Process Phases" which will call specific "Handlers" to allow your application to execute code at specific time during the process. Note: You will find this technique very familiar if you have some knowledge about mod_perl handlers. As for most CGI frameworks, a complete CGI application is usually composed by 2 parts: The Instance Script and CGI Builder Build (CBB). Instance ScriptThe instance script is used as the CGI script that manage the client's request: it is usually a very short script that just creates a new instance of your application class, and executes the process() method. This is a complete typical instance script needed to use e.g. the 'My::WebApp' CBB:#!/usr/bin/perl -w use My::WebApp ; $webapp = My::WebApp->new() ; $webapp->process() ; Note: This script could be completely eliminated by the use of the "Apache::CGI::Builder" extension (usable under mod_perl) which transparently executes the process. CGI Builder Build (CBB)This is the part of your application that implements the CBF features.Note: In this documentation we call the package that uses C::B (and that eventually includes any extension and super class) a "CGI Builder Build" or simply CBB for shortness. The CBB is not intended to be used as a CGI script by itself, but as a class defining the methods, properties and handlers needed to integrates the CBF capability to generates the pages with your very specific needs. Your application will inherit the CBF capability by simply using the base module CGI::Builder: package My::WebApp; use CGI::Builder; It can inherit from more extensions or super classes including them in the 'use' statement: package My::WebApp; use CGI::Builder qw| CGI::Builder::SomeExtension My::SuperClass ... |; WARNING: Don't use the statement 'use base 'CGI::Builder;'. You must just 'use' C::B because the CGI::Builder::import sub has to setup the overruning methods and will internally update @ISA on its own (see details in the "import" advanced method). A complete CBB module is usually as simple as this one: package My::WebApp ; # your class name # CBB definition use CGI::Builder qw| CGI::Builder::SomeExtension My::SuperClass ... |; # optional instance initialization executed for each request sub OH_init { ... } # optional Pre Process Handler executed for each request sub OH_pre_process { ... } # Switch Handler executed only when page_name is 'foo' sub SH_foo { ... } # Page Handler executed only when page_name is 'foo' sub PH_foo { ... } # Page Handler executed only when page_name is 'bar' sub PH_bar { ... } # optional Fixup Handler executed for each request sub OH_fixup { ... } # optional Cleanup Handler executed for each request sub OH_cleanup { ... } 1; Internal StructureThe CBF uses properties and property groups accessors to store and retrieve all the internal data into and from the object.The accessors to the internal data object are provided by the OOTools pragmas, which are very efficient function templates that the CBF imports at compile time. "This technique saves on both compile time and memory use, and is less error-prone as well, since syntax checks happen at compile time." (quoted from "Function Templates" in the perlref manpage). With just 2 imported methods (property and property group) the CBF can handle dozens of different properties in just a few lines, with the same memory and the same friendly and consistent interface. Another benefit that comes from the use of OOTools is that the internal hash structure always mirrors the public interface. This is very important specially for a module like C::B that is supposed to be used as a base class. In the C::B and its extensions there are no dirty statements like $self->{__SOME_INTERNAL_STUFF} = 'something' that may introduce an undocumented and possibly conflicting key and/or bypass the accessors. This practice allows to avoid the conflict between extensions and allows any subclass to override anything with the warranty to have it changed across the whole classes included in the CBB. Besides, there are no mistakes about the internal structure of the object: when you write your super class or any special extension, you don't need to go through all the source to see if any key has already been used for internal purpose, because ALL the used key are documented, just because all have a consistent public accessor that uses the same identifier. # this accessor $s->some_property # will always refer to $$s{some_property} # and will never refer to any inconsistent $$s{__SOME_PROPERTY} Note: In order to make your code forward/backward compatible, you should always use the provided accessors. Process PhasesThe CBF makes available to your sub class a pre-structured and very flexible process that your application will use to do what it need to do and produce the requested 'pages' with minimum programming effort.This process is composed by the following phases, internally handled by the CBF. Each phase provides one or more hooks to allow your application to execute code at specific phases of the process and to customize the process itself. Important Note: Remember that your CBB does not need to use each and all these hooks, in most cases it probably will use just a couple of them, so don't be afraid of the apparent complexity of the following table. It is just a simple time line with many hooks: you can attach your code to the hook in the right position in the time line. +-------------------+ | Instance Creation | Creation of the new object |-------------------| usually done by the Instance Script | - new() | or by the Apache::CGI::Builder extension. +-------------------+ | | +---------------+ +-->| CB_INIT phase | Optional initializing hook e.g. used to set any |---------------| property or to start a DB connection. Overrides | - OH_init() | defaults and values passed with new(). +---------------+ +-------------+ | Process | Start of the process phases |-------------| usually done by the Instance Script | - process() | or by the Apache::CGI::Builder extension. +-------------+ | | +-------------------+ |-->| GET_PAGE phase | This phase provides to get the requested | |-------------------| page_name. It is internally handled so no need | | - get_page_name() | to use its method unless you need overriding. | +-------------------+ | | +--------------------+ |-->| PRE_PROCESS phase | This optional hook will be called just after | |--------------------| the GET_PAGE Phase. Used to check access and | | - OH_pre_process() | authorizations and eventually switching to | +--------------------+ another page. | |-------+ | | | +======================+ | SWITCH_HANDLER phase | Per Page Handler. E.g. If defined, the P |======================| 'SH_foo' will be executed when the 'foo' A | - SH_foo | page will be requested; the 'SH_bar' when G | - SH_bar | the 'bar' page... E.g. used to check the E | - SH_baz | input, and eventually switching to another | - ...... | page. S +======================+ W | I +-----------------+ T | PRE_PAGE phase | Overrun Handler called each time a Page C |-----------------| Handler is about to be executed. E.g. used H | - OH_pre_page() | to centrally handle errors. I +-----------------+ N | G +====================+ | PAGE_HANDLER phase | Per Page Handler. E.g. If defined, the C |====================| 'PH_foo' will be executed when the 'foo' Y | - PH_foo | page will be requested; the 'PH_bar' when C | - PH_bar | the 'bar' page... If defined and if no other L | - PH_baz | Page Handler has been found, the optional E | - ...... | 'PH_AUTOLOAD' will be executed instead | - PH_AUTOLOAD | unless the page has some content already | +====================+ | | |-------+ | | +--------------+ |-->| FIXUP phase | This optional hook is called after the | |--------------| PAGE_HANDLER Phase. Used as last hook just | | - OH_fixup() | before the RESPONSE Phase. | +--------------+ | | +------------------------+ |-->| RESPONSE phase | This phase provides to generate the response. | |------------------------| It is internally handled so no need to | | - page_content_check() | use the methods in your applications unless | | - send_header() | you really need severe overriding | | - send_content() | | +------------------------+ | | +----------------+ +-->| CLEANUP phase | This optional hook is called at the end of |----------------| the process to allow cleanup. E.g used to | - OH_cleanup() | disconnect from a DB, or for log operations. +----------------+ Your application can execute some optional code at each phase of the process. To do so it has just to define the handlers that will be automatically called during that phase of the process. (see "Handlers") If your CBB doesn't include any template integration extension, the only mandatory requirement for your application will be setting the "page_content" property to some content (i.e. SCALAR, SCALAR ref or CODE ref) before the RESPONSE Phase; if your CBB includes some integration like CGI::Builder::Magic even setting the "page_content" property will become unnecessary. ExceptionsAll the fatal errors (even those originated by other used modules) are trapped and wrapped with the indication of the Phase name which was running and the page name defined at the moment of the error. (see "die_handler")Known Issue: At the moment, if you don't use a 5.8.x perl version, a fatal error might trace also the CGI::Builder internal packages instead of just the line that generates it in your own code. Besides, under certain circumstances and for certain handlers, the error line number might refer to the original call in the Instance Script instead to the statement in the CBB; in this case, the phase name shown in the error message should however point you to the handler that generated the error. No page content Since the CBF 1.1, an empty page_content does not produce a fatal error. It just produce a "204 No Content" http status header or - if you are using the Apache::CGI::Builder integration - a "404 Not Found" http status header, if no other status has been set until the RESPONSE phase. This means that if your application doesn't implement some system to handle unknown page_names on its own (i.e. page names that don't produce any page conent), the CBF will handle them automatically. (see also the "page_content_check" advanced method) The Extension SystemExtensions and Super ClassesAn extension is a CPAN module that extends the capability of the CBF with some documented features which can extend the capability of any CBB. (see " Extensions List")A super class is usually a 'private' module containing some application specific capability, used as the base class for one or more CBB. Both - extensions and super classes - use and benefit from the same extension system capabilities; the differences between them are just scope differences, being aimed to generic/public use, or to specific/private use. A CBB can include both extensions and super classes by just adding them to the build list: # define your application build use CGI::Builder qw| CGI::Builder::SomeExtension SomeSuperClass |; Inheritance, Overriding and OverrunningThe CBF implements 3 main features in your CBB: the classical Inheritance and Overriding, plus a CBF exclusive Overrunning.Inheritance The inheritance allows your CBB to inherit the structure, methods, and properties of the CBF, so your methods can set and retrieve properties and call methods defined by C::B or any other extensions you may include in your build. Inheritance is provided by the simply 'use' of C::B, that will also update the @ISA array of your sub class with the base classes it inherits from. Overriding The overriding allows a class to override methods or properties defined by some base class (i.e. C::B or any other extensions). Your sub class can override a method by just defining the same method in its package, or can override the default of a property, by just setting another value. In the CBB next entry will override previous entries: package My::WebApp; # the effect on @ISA of this build definition use CGI::Builder qw| My::SuperClassA My::SuperClassB My::SuperClassC |; # is equal to this @My::WebApp::ISA = qw| My::SuperClassC My::SuperClassB My::SuperClassA CGI::Builder |; Methods with the same identifier defined in class My::SuperClassC will override those of class My::SuperClassB which override those of My::SuperClassA, which override those in CGI::Builder itself. Overrunning The overrunning allows multiple base classes to 'overrun' the same method, that is: runnig the 'foo' method of each base class that defines a 'foo' method (sort of multiple stacked execution). This is a very useful feature that adds more power and flexibility to the extension system, allowing extensions and super classes to have automatically executed some code at specific phases of the process. All the 'OH_*' handlers are Overrun Handlers so they ALWAYS will have their code executed at the proper phase time (i.e. they will not override each other). This tecnique is particularly useful to use super classes as plug-ins in multiple CBBs. Simple methods could be skipped (not executed) if the base class that use the super class would define that same method, while Overrun Handlers are always executed (unless your CBB explicitly use the overrun_handler_map() class accessor to skip them). (see "overrun_handler_map") Important Note: A DB super class, could define an OH_init() and an OH_cleanup() that would be called at the correct time to connect and disconnect to/from a DB. Another super class could use the same handlers (defined in its own package) to do something competely different, and (with automatic overrunnig) your CBBs would have just to include them in the build definition, and each handler of each super class will be called at the correct time, so everything would work as expected. Extensions List
METHODSnew ( [ properties ] )The new() method construct a new instance of your application. It accepts and sets any known object property, storing any unknow property as a new param.Use this feature to set the default of the properties of the new object before any other method calls. For example you could install just one CBB on your server but use it in different domains, and you want to give the possibility to the webmasters that use it to pass different parameters to your CBB. This feature just adds a flexible possibility to configure your application from the cgi script that uses it instead from the usual CBB "OH_init": # not always useful here, but possible $webapp = WebApp->new( page_name => 'mySpecialPage', cgi_page_param => 'pp', page_handler_map => { start => \&myPH } , myParam => 'myDATA',# sets param ... more here... ); Note: This feature is fully utilized by Apache::CGI::Builder, which internally initialize the object with some defaults. process ( [ page_name ] )The process method starts the pre-structured CBF process (see "Process Phases").You usually will use this in the Instance Script without any argument. If you need to temporarly force the application to send a particular page, you can add it as an argument (e.g. useful when testing a particular Page Handler): $webapp->process('mySpecialPage'); switch_to ( page_name [, arguments] )This method will switch the process to a page_name, e.g. useful when validating some condition in any handler. This cause a sort of internal redirect: use the "redirect( url )" method to make the client do a new request.sub PH_myPage { my $s = shift; some_condition || return $s->switch_to('myOtherPage', @optional_arg) ... } The arguments passed to this method are optional and not needed by the CBF itself; they are just passed to your other handlers, just in case you need to keep track of something, or whatever you need to do. This is an example, to show a possible use of the arguments: sub SH_any_handler { my $s = shift; if (any_condition) { return switch_to("other_page", $my_argument) } } sub SH_other_page { my ($s, $my_argument) = @_ ; if ( $my_argument eq "something" ){ do_something } else { do_something_else } } sub PH_other_page { my ($s, $my_argument) = @_ ; $s->page_content = $my_argument ? "You requested "any_page"" : "You requested "other_page"" } Note: You can use this method from the PRE_PROCESS phase until the FIXUP phase. redirect( url )This method will redirect the client to the url, bypassing all the remaining phases until the CLEANUP phase that will be executed as usual after the client has been redirect to the url. You can use this method from the GET_PAGE phase until the RESPONSE phase.return $s->redirect('http://domain.com/some/url'); Note: This method will add the url to the header, and will use the CGI::redirect() method to redirect the client, passing it also the whole header hash you set so far (see also header() method). cgi_new ( [arguments] )This method is internally used by the "cgi" property in order to create a new CGI object. You can also use this method if you need to create a CGI object with your own param (e.g. useful to fill a form with HTML::FillInForm).Override it if you want to use any CGI object different than the canonical CGI.pm (e.g. CGI::Simple). This method should return an object which implements at least: "param", "header", "redirect" and "cookie", which are methods the CBF and its Extension use. PROPERTY ACCESSORSA CBF property is a lvalue accessor to an object value. 'lvalue' means that you can create a reference to it, assign to it and apply a regex to it (see also "KNOWN ISSUE"). Besides, a property can have a default value, some validation entry rules, etc. and you can use them as an argument to the new() method as well.# 'page_content' is a property accessor $webapp = WebApp->new(page_content => 'dummy default') ; $pc = \ $s->page_content ; $s->page_content = 'some content ' ; $s->page_content .= 'some more content' ; $s->page_content =~ s/some/SOME/ ; # old debugger-safe way still works $s->page_content('some content') $pageContent = $s->page_content There are several CBF standard properties and each extensions can add some other specific property to the set of the CBF. You can see a description for each property used by an extension in its own POD. Note: The properties in this section are ordered by importance/frequency of use, listing first the most frequently used and needed, and last the properties that you might probably ignore for the rest of your life :-). cgiThis property allows you to access and set the cgi object. The default for this property is a CGI.pm object, but you can override this default if you redefine the "cgi_new()" method.If you use the default you have just to use the "cgi" property which will return the current CGI object without the need to create it by yourself. # in any handler $my_query_param = $s->cgi->param('any_query_parameter') If, for any reason, you want to use your own cgi object, you can pass this property to the new() method, or you can also directly set it at some point in the process. $cbb = WebAppClass ->new( cgi => CGI->new({myOwnQuery => 'something'}) ) $s->cgi = CGI->new({myOwnQuery => 'something'}) ; page_nameThis property allows you to access and set the page name. The default for this property is 'index'. This means that the 'index' page will be requested if no other page has been explicitly requested.Set the "page_name" to redefine the default page_name. This default will be used whenever the value of the CGI form parameter specified by the "cgi_page_param" property is not defined. $current_page_name = $s->page_name # override default page name in OH_init $s->page_name = 'myStart' ; requested_pageThis property holds the original requested page name. Read only.page_contentThis property allows you to access and set the content of the page (or a reference to it) to send to the client. The default for this property is the empty string '' so be aware that it is always defined even if you don't set it.During the process the "page_content" property will be set to some page content, to a reference to it or to a CODE reference that will print the output on its own. In this case the refereced code will be called after the printing of the header. sub PH_myPage { ...do_something_useful... $s->page_content = 'something' $s->page_content .= 'something more' } sub PH_myOtherPage { ...do_something_useful... $s->page_content = \&print_the_content } The main advantage to set this property to a CODE reference is that you avoid to charge the memory with the whole (and sometime huge) output and print it while it is produced. This feature is fully utilized in CGI::Builder::Magic, but you can also use it with your own subroutines. cgi_page_paramThis property allows you to access and set the name of the query parameter used to retrieve the page_name. The default for this property is 'p'.page_pathThis property allows you to access and set the path of the page (e.g used to address a template or a web directory). The default for this property is './tm' (relative to the Instance Script), but your code or other extensions may set it otherwise.page_suffixThis property allows you to access and set the suffix string used by some template extension to compose the page file path. The default for this property is the empty string '' so be aware that it is always defined even if you don't set it, anyway your code or other extensions may set it otherwise.PROPERTY GROUP ACCESSORSA property group accessor is simply an accessor which can handle multiple data (or properties) of a same group. With all the property group accessors you can set, add, retrieve, delete, check for existance with only one method. You can use them as an argument to the new() method as well. See the param() method to learn the feature of the group accessors (remember that all the property group accessors use the same interface).param ( [ key | hash ] )This accessor handles the parameters of your application# pass a parameter to the new object $webapp = WebApp->new(param => {myParam => 'myD'}) # sets or adds several params $s->param(myParam1=>'myDATA1', myParam2=>'myDATA2') ; # sets or adds several params $s->param(\%other_param) ; # retrieve a value $myParam1 = $s->param('myParam1') # retrieve a slice of values (requires OOTools >= 1.77) @slice = $s->param(['myParam1', 'myParam3']) # retrieve the reference to the param hash $param_ref = $s->param ; while ( my ($p, $v) = each %$param_ref ) { do_something_useful } # retrieve the keys of the param hash (new OOTools recommendation) @param_keys = keys %{$s->param} ; # copying he whole hash (requires OOTools >= 1.8) %param = $s->param # check if exists any param exists $s->param->{myParam} ; # delete any param delete $s->param->{myParam} ; A special feature only for the param() accessor, is the automatic loading and retrieving using the parameter key as it was a defined property or method. This feature uses the "AUTOLOAD" method: # with the automatic AUTOLOAD of param, these statements $webapp = WebApp->new(param => {myParam => 'myD'}) ; $s->param( myParam=>'myD') ; $p = $s->param('myParam') ; # can be written simply as $webapp = WebApp->new(myParam => 'myD') ; $s->myParam = 'myD'; $p = $s->myParam ; Important Note: If you use the AUTOLOAD feature, and if you want to write code that will not break, you should always follow the CBF convention and name your param with the 'my_' or '_' prefixes. If you don't do that, it might happen that in the future, your 'special_data' parameter loaded with AUTOLOAD ("$s->special_data") will call instead a special_data() method implemented by any new release of any extension :-). The 'my_special_data' or '_special_data' are safer choices. header( [ header ] )This accessor works for header exactly like param() works for param. You can use it to change the header that your application will use in the RESPONSE phase.Note: The keys/values pairs you are setting as the headers will be internally passed to the "CGI::header()" method at the correct time, so you should always use the leading '-' character as you do for "CGI::header" (please, refer also to the documentation of the header() method in CGI.pm, or other module if you have overridden the "cgi_new()" method in order to return a different CGI object). page_errorYou can use this property group accessor to store and retrieve page errors. You can add key value pairs each time you find an error, and retrieve the whole hash later, e.g. to show a feedback of the errors:$s->page_error(email_field => 'Not a valid address') unless email_condition ; $s->page_error(other_field => 'Bad parameter') unless otherfield_condition ; ... exists $s->page_error->{email_field} ; Note: It is automatically set by CGI::Builder::DFVCheck. HANDLERSThe CBF provides several optional handlers that will be called at specific phases during the process: your CBB has just to define the specific handler of the specific phase. E.g. if your application needs to check the authorization for a user at the start of the process, it should define a "OH_pre_process" method and put the needed code in that handler.All the handlers are optional; this means that your CBB should define just the handlers that it need. Note: Here they are ordered by category and not by execution time, see "Process Phases" to have them ordered by execution time. Overrun HandlersThe handlers in this category - if defined in your CBB - are executed at each request at the specific Phase time and for each base class that defines them. (see "Overrunning")OH_init This handler is executed in the CB_INIT Phase (i.e. just after the creation of the new object) and it is internally called by the new() method. You can use it to initialize some properties or param of your application, or to connect with a DB. Note: The OH_init() handlers defined in different classes are executed with the same order as the CBB inclusion order. OH_pre_process This handler is executed in the PRE_PROCESS Phase (i.e. at the very start of the process). You can use it to control AAA (Authentication and Authorization, and Access) and eventually switch_to() another page on failure. Note: The OH_pre_process() handlers defined in different classes are executed with the same order as the CBB inclusion order. OH_pre_page This handler is executed in the PAGE_PROCESS Phase (i.e. after the SWITCH_HANDLER Phase, and just before the PAGE_HANDLER Phase) at each switching cycle. This is the only Overrun Handler that may be executed multiple times in the same process (i.e. each time the switch_to() method is internally or explicitly called and no Switch Handler has been set). You can use it e.g. to centrally handle page errors with a single handler. Note: The OH_pre_page() handlers defined in different classes are executed with the same order as the CBB inclusion order. OH_fixup This handler is executed in the FIXUP Phase (i.e. after the PAGE_HANDLER Phase, and just before the RESPONSE Phase). It gives the last chance to do things before the response is generated (e.g. modify the header or the page_content just before they are sent to the client). Note: The OH_fixup() handlers defined in different classes are executed with a reversed CBB inclusion order. OH_cleanup This handler is executed in the CLEANUP Phase (i.e. after the RESPONSE Phase). At this Phase, the page has already been sent to the client, and you can use it to cleanup e.g. closing some opened DB connection or logging execution. Note: The OH_cleanup() handlers defined in different classes are executed with a reversed CBB inclusion order. Per Page HandlersThese are the handlers called on a per page basis, i.e. each per Page Handler is called ONLY for a certain requested page.SH_* (Switch Handlers) This handlers are prefixed by 'SH_' (i.e. Switch Handler). (e.g. If defined, the 'SH_foo' will be executed when the 'foo' page will be requested). You can use this handlers to check some condition just before the PRE_PAGE Phase so giving you the possibility to switch to another page before the execution of that phase. PH_* (Page Handlers) This handlers are prefixed by 'PH_' (i.e. Page Handler). (e.g. If defined, the 'PH_foo' will be executed when the 'foo' page will be requested). You can use this handlers to do something specific for different pages, such as e.g. executing some specific code just for that specific request (or creating the specific page content when your handler don't use any automagic template integration) PH_AUTOLOAD This is a special Page Handler which will be called IF defined and IF there are no other defined page handler for the specific requested page and UNLESS the page_content_check() return true (i.e. there is no page content so far). The main purpose of this handler is giving you one more option to generate the page_content if no other handler has generated it so far, so you can use it e.g. as the last chance to redirect the client or switch_to your special 'Not found' page during the PAGE_HANDLER phase. You can also use the "page_handler_map" advanced accessor to map the AUTOLOAD handler to any method you prefer. Note: The execution of this handler is skipped by the presence of:
If you need a wider solution you should use an "OH_pre_page" or an "OH_fixup" handler instead, which get always called without any restriction. ADVANCED FEATURESIn this section you can find all the most advanced or less used features that document all the details of the CBF. In most cases you don't need to use them, anyway, knowing them will not hurt.CONSTANTSThese constant are used to set and check the Process Phase. They return just a progressive integer:CB_INIT 0 GET_PAGE 1 PRE_PROCESS 2 SWITCH_HANDLER 3 PRE_PAGE 4 PAGE_HANDLER 5 FIXUP 6 RESPONSE 7 REDIR 8 CLEANUP 9 Global Variables PersistenceIf you are using mod_perl, you should know the "Global Variables Persistence" issue: this is something that you must consider when your CBB is running under mod_perl, even when your CBB doesn't use Apache::CGI::Builder.More explicitly you should know that the CBF and its extensions may use Global Variables to store certain data which are class scoped (i.e. used for all the processes of your CBB class), thus caching the data and saving some processing. The Global Variables that the CBF uses are always accessed by an OOTool accessor, they are just Class Accessors instead of Object Accessors: the behaviour of a Class accessors (property or group) is the same, but the underlaying accessed variable is a Global Variable, and so it will behave under mod_perl. (See OOTools documentation if you want more details about the differences). Examples of Class Accessors are the "Class Property Group Accessors" of this module, or the "tm", "tm_new_args" and "tm_lookups_package" accessors of the CGI::Builder::Magic extension (which creates the Template::Magic object just once -the first time it is accessed- and uses the same object for all the successive requests that involve template processing). Note: You should clearly distinguish the class accessors among the others because this particular feature is usually written in bold at the start of the accessor doc. Advanced Methodscapture( CODE )This method executes the CODE (which can be a method name or a CODE ref) and returns a ref to the captured output, so allowing you to eventually test your sub classes, or doing something with the output (e.g. in a OH_fixup() when the page content is a CODE reference). $captured_output = $webapp->capture('process'); if ( $$captured_output =~ /something to test/ ){ print 'ÍT WORKS!' } sub OH_fixup { my $s = shift ; if (ref $s->page_content eq 'CODE') { # executes the referenced CODE and capture the output $s->page_content = $s->capture($s->page_content) } # do something with $s->page_content as usual } phase( [phase_number] ) This method returs the current phase name or the name of the passed phase_number argument. Internal MethodsYou don't need to directly use any of these methods because they are used internally but you might need to override them in very special cases, so they are documented here.get_page_name Used internally to set the "page_name" to the value of the "cgi_page_param" query parameter at the very start of the process. (e.g. this query parameter ?p=myPage will set the "page_name" to 'myPage') If you don't want to pass the page parameter as a query parameter, or if you have any other custom need you can override this method which should set the "page_name" as you need. send_header Used internally to send the "header" to the client. This accessor is backed by the "header()" CGI function, so IF (and only if) you implement a different query object not based on CGI.pm, AND the object you use 'can' not "header", THEN you cannot use this method, so you need to override it. send_content Used internally to send the "page_content" to the client. AUTOLOAD This method (not to be confused with the 'PH_AUTOLOAD' Page Handler) implements an handy param accessor. You can store or retrieve some param as it was an object property: # instead of do this $s->param(my_Param => 'some init value') # you can do this directly $s->my_Param = 'some init value' ; # same thing with the new() method $webapp = WebApp->new(my_Param => 'some init value') # or with the explicit assignation $webapp = WebApp->new(param => {my_Param => 'some init value', my_OtherParam => 'some data' } ) # and to retrieve it $p = $s->my_Param Note: If you don't like this feature, just override the AUTOLOAD method. If your application implements its own AUTOLOAD sub and you want to keep this possibility just fall back on the SUPER method when needed. Important Note: If you use this feature, and if you want to write code that does not break, you should always follow the CBF convention and name your param with the 'my_' or '_' prefixes. If you don't do that, it might happen that in the future your 'special_data' parameter loaded with AUTOLOAD ("$s->special_data") will call instead a special_data() method implemented by any new release of any Extension :-). The 'my_special_data' or '_special_data' are safer choices. page_content_check This method is called at the very start of the RESPONSE phase. It checks if the page_content contains some content to be sent. A true returned value will send the "page_content", while a false returned value will prevent the sending of the "page_content", and will set the "-status" header to the value of the "no_page_content_status" property if no status header has ben set yet. The page_content_check() method is overridden by other extensions such as CGI::Builder::Magic, that checks also if the template file exists before using its template print method. This method is used also to handle the AUTOLOAD page handler, which will not be called unless there is no "page_content" so far. die_handler Used internally to implement a localized $SIG{__DIE__}. This method adds useful informations to the error messages (even to those generated by other used modules). It adds the page name and the phase at the moment of the error plus a Data::Dumper::Dumper() of the object itself if the CGI::Builder::Test is included in the build. If you need to implement your own $SIG{__DIE__} you should override this handler in your own CBB. import The C::B module use the import() method to setup inheritance and overrunning of your CBB, and undefines the import() method in your CBB (to avoid inheritance and overrunning propagation). If for any very exotic reason you need to define an import method in your own CBB, you should define it BEFORE the definition of the build, or it will not work. Advanced Property Accessorsdont_send_headerSet to a true value, this property will prevent the sending of the header. Undefined by default. no_page_content_status This Class property is used to supply the default "-status" header that the CBF send in the RESPONSE phase when the "page_content" is empty, but ONLY if the status header is not defined yet. The default of this property is '204 No Content', but other extensions (such as Apache::CGI::Builder) could set it otherwise. PHASE Internal read only property used to control the process and the exceptions. Don't override it unless you know exactly what you are doing!. Class Property Group AccessorsThe accessors in this section are Class Accessors, which are accessors to package variables (i.e. not instance variables) which are class scoped. Usually you sould use them at the start of the CBB code (and out of any handler or method).page_handler_map( [ page => page_handler ] ) With this Class Accessor you can map some page name to a specific Page Handler: __PACKAGE__->page_handler_map ( thisPage => \&special_Phandler, thatPage => 'other_special_Phandler' ); switch_handler_map( [ page => switch_handler ] ) With this Class Accessor you can map some page name to a specific Switch Handler: __PACKAGE__->switch_handler_map ( thisPage => \&special_Shandler, thatPage => 'other_special_Shandler' ); overrun_handler_map The purpose of this Class Accessor is giving you the possibility to override the automatic overrunning of CBF if you want to change the order (or skip) any Overrun Handler defined by some extension or super class. Consider this CBB: package My::WebApp; use CGI::Builder qw| My::SuperClassA # defines its own OH_init() and OH_cleanup() My::SuperClassB # defines its own OH_init() and OH_cleanup() My::SuperClassC # defines its own OH_init() and OH_cleanup() |; sub OH_init { ... } sub OH_cleanup { ... } In the CB_INIT Phase the following handlers will be automatically executed with this order: 1 My::SuperClassA::OH_init 2 | My::SuperClassB::OH_init 3 | | My::SuperClassC::OH_init 4 | | | My::WebApp::OH_init | | | | 5 | | | My::WebApp::OH_cleanup 6 | | My::SuperClassC::OH_cleanup 7 | My::SuperClassB::OH_cleanup 8 My::SuperClassA::OH_cleanup As you can see the OH_init() handlers are executed with the same order of the CBB inclusion order, while the OH_cleanup() execution order is reversed. This way it is created a sort of nested execution so that the class that first inits is the last that ends/destroys. See each handler description to know the execution order. If you want to change the execution order of the Overrun Handlers, you can use this class accessor to change that order: __PACKAGE__->overrun_handler_map ( init => [ 'My::SuperClassC', 'My::SuperClassB', 'My::WebApp', 'My::SuperClassA'] ); After that change the execution order of the handlers will be: My::SuperClassC::OH_init My::SuperClassB::OH_init My::WebApp::OH_init My::SuperClassA::OH_init Remember also that you can change the order of all the Overrun Handlers in a single step: __PACKAGE__->overrun_handler_map ( init => [ 'My::SuperClassC', 'My::SuperClassB', 'My::SuperClassA'], fixup => [ 'My::SuperClassB', 'My::SuperClassA']); Important Note: The keys of this accessor are the handler identifier WITHOUT the 'OH_' overrun handler constant prefix. EXAMPLESAll the examples in this section will use the following Instance Script that use a WebApp.pm CBB (differently organized in each example). In this example the script is supposed to be available at the url: http://domain.com/IScript.cgi#!/usr/bin/perl -w use My::WebApp ; $webapp = My::WebApp->new() ; $webapp->process() ; Hello world!This is the classical example of a minimal output, that will produce a page content with just the "Hello world!" string in it (not a valid HTML page, but it's ok for our purpose). Obviously, without the CBF you could write the same script with less effort, but the usefulness of CBF comes up when the application become more complex:package My::WebApp; # your class name use CGI::Builder; # defines a build with no other extension sub PH_AUTOLOAD { # called for all requested pages (no other PH_*) my $s = shift; $s->page_content = "Hello world!" # defines the page content } 1; As you see in the CBB, it defines just the PH_AUTOLOAD, the special Page Handler that is automatically called when no other Page Handlers are found for the requested page (see "PH_AUTOLOAD"). All we have to do in that handler is set the 'page_content' property to the content we want to send to the client, and the CBF will manage automatically all the process. Hello world! Variant 1Suppose that we want to send the "hello world!" content just for the specific "Hello" page, while for each other page eventually requested, we want to redirect the client to another url:package My::WebApp; # your class name use CGI::Builder; # defines a build with no other extension sub PH_Hello { # called ONLY for page_name 'Hello' my $s = shift; $s->page_content = "Hello world!" # defines the page content } # first alternative # check the requested page in PRE_PROCESS Phase # redirect if page_name is not "Hello" sub OH_pre_process { my $s = shift; if ($s->page_name ne 'Hello') { return $s->redirect('http://my/not/found/page/url') } } # second alternative # check the defined page content in FIXUP Phase # redirect if page_content has not been set yet (by any handler) sub OH_fixup { my $s = shift; return $s->redirect('http://my/not/found/page/url') unless $s->page_content_check; } # third alternative sub PH_AUTOLOAD { my $s = shift; $s->redirect('http://my/not/found/page/url'); } 1; As you see, we have changed the PH_AUTOLOAD with the PH_Hello that will be called only when the page_name is 'Hello', then, we have a few alternatives to choose for the redirection. The first alternative is very specific, and checks exactly for the 'Hello' "page_name", so if we will add another Page Handler in the future, we have to modify accordingly the condition to avoid redirection. The second alternative is more flexible because it checks for the content of the page in the FIXUP Phase, after the PAGE_HANDLER Phase has been tried, redirecting only if no Page Handlers has set the page_content so far, so if we will add a new Page Handler in the future it will work without any changes. The third alternative is simpler since it uses the way the PH_AUTOLOAD get called, and just redirect without checking any condition (the conditions are internally checked BEFORE calling the PH_AUTOLOAD). Note: To request the page "Hello" the client should point to http://domain.com/IScript.cgi?p=Hello. Any other requested page as (e.g. ?p=myTry) will cause a redirection to http://my/not/found/page/url. Hello world! Variant 2To add another page we just add another Page Handler:package My::WebApp; # your class name use CGI::Builder; # defines a build with no other extension sub PH_Hello { # called ONLY for page_name 'Hello' my $s = shift; $s->page_content = "Hello world!" # defines the page content } sub PH_NiceHello { # called ONLY for page_name 'NiceHello' my $s = shift; $s->page_content = "This is a nice HeLlO WoRlD! :-)" } sub PH_AUTOLOAD { my $s = shift; return $s->redirect('http://my/not/found/page/url') } 1; Note: To request the page "NiceHello" the client should point to http://domain.com/IScript.cgi?p=NiceHello. Hello world! Variant 3If instead of a client redirection we want to send a specific page internally generated, we could add a new Page Handler for that page, and/or change a little the OH_fixup:package My::WebApp; # your class name use CGI::Builder; # defines a build with no other extension sub PH_Hello { # called ONLY for page_name 'Hello' my $s = shift; $s->page_content = "Hello world!" # defines the page content } sub PH_NiceHello { # called ONLY for page_name 'NiceHello' my $s = shift; $s->page_content = "This is a nice HeLlO WoRlD! :-)" } # first alternative sub PH_no_page { # internally called by the OH_fixup my $s = shift; $s->page_content = "The page you requested is not available!" } sub PH_AUTOLOAD { my $s = shift; $s->switch_to('no_page') # switches to the no_page Page Handler } # second alternative # you can eliminate the PH_no_Page Handler # and set the page_content directly from the PH_AUTOLOAD sub PH_AUTOLOAD { my $s = shift; $s->page_content = "The page you have requested is not available!" } 1; Note: Any requested page that does not produce any page_content as e.g. ?p=myTry, will print "The page you requested is not available!". Check the inputImagine a simple application that addresses 2 pages: a 'ShowForm' page that contains a form with just a field, and a 'Submitted' page that will show just a message confirming the submission. The form action of the ShowForm page is set to 'IScript.cgi?p=Submitted'.Note: We don't handle the error for this simple example, we just want to check whether the 'email' field is not empty, so showing the Submitted page, or re-send the ShowForm page in case the 'email' field is empty. package My::WebApp; use CGI::Builder; # this is the Page Handler that produce the form sub PH_ShowForm { my $s = shift; ... generates the form $s->page_content = $generated_page_with_form; } # add a Switch Handler for the Submitted page # that checks the 'email' query param and switches on failure sub SH_Submitted { my $s = shift; $s->cgi->param('email') or switch_to('ShowForm'); } # this is the Page Handler that confirms the submission sub PH_Submitted { my $s = shift ; $s->page_content = 'Thank you for filling the form'; } The 'SH_Submitted' handler will be automatically called when the "page_name" is 'Submitted' and just before calling the 'PH_Submitted' handler, so giving you the possibility to check some condition (i.e. the not empty 'email' field). The process will switch to the 'ShowForm' page on failure (so executing the PH_ShowForm'), or will execute the PH_Submitted on success. You could do the same by avoiding the use of the 'SH_Submitted' handler, by moving the switch condition inside the Page Handler itself. In this example it might appear that the elimination of the SH_Submitted method could produce a more clear code, but in real world jobs I find this is less clear, specially because sometimes a handler may contain a lot of conditions and mixing checking with page production is more confusing, anyway... in your applications this is up to you :-). package My::WebApp; use CGI::Builder; # this is the Page Handler that produce the form sub PH_ShowForm { my $s = shift; ... generates the form $s->page_content = $generated_page_with_form; } # this is the Page Handler that confirms the submission sub PH_Submitted { my $s = shift ; $s->cgi->param('email') or return switch_to('ShowForm') ; $s->page_content = 'Thank you for filling the form'; } In this case, notice the "return switch_to()" to return from the handler on switching. Note: You should consider to use the CGI::Builder::DFVCheck extension that integrates C::B and the "Data::FormValidator" module. More examplesYou can find more examples in the mailing list archive and in the CBF_examples dir included in this distribution.Note: While you are experimenting with the CBF, you are probably creating examples that could be useful to other users. Please submit them to the mailing list, and I will add them to the next CBF release, giving you the credit of your code. Thank you in advance for your collaboration. HOW TO...Design your applicationAs general guide, you should keep in mind the "Process Phases": almost all your code should go into some handlers, so decide when (in what Phase) you want it to be executed and define the specific handler you need. Also consider these points as a more detailed guide:
Write code that will not breakSince this framework is implemented by inheritance of possibly many extensions, conflicting keys, methods and properties could become a real problem unless you follow some simple conventions. If you want to write forward/backward compatible CBF code please, consider this:
Avoid common mistakes
Write a Super Class
Write an ExtensionWriting an extension is usually simpler than what you might expect. Since extensions are included in the CBB as base classes, an extension could contain just a simple method of just a few line of code so you don't need to be a guru in order to contribute. The only real requirement is following this simple guide lines:
KNOWN ISSUEDue to the perl bug #17663 (Perl 5 Debugger doesn't handle properly lvalue sub assignment), you must know that under the -d switch the lvalue sub assignment will not work, so your program will not run as you expect.Since version 1.33 the CBF and its extensions don't internally use any lvalue sub assignment although they are fully supported if you decide to use them in your code. In order to avoid the perl-bug you have 3 alternatives:
Maybe a next version of perl will fix the bug, or maybe lvalue subs will be banned forever, meanwhile be careful with lvalue sub assignment. SUPPORTYou can obtain free support, by using the cgi-builder-users mailing list. Before posting, please:
AUTHOR and COPYRIGHT© 2004 by Domizio Demichelis (<http://perl.4pro.net>)All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as perl itself. CREDITSThanks to these people which - in very different ways - have been somehow helpful with their feedback, suggestions or criticism:
POD ERRORSHey! The above document had some coding errors, which are explained below:
Visit the GSP FreeBSD Man Page Interface. |