Like all ModPerl content handlers, this simple module implements a handler() method that is called whenever the URI associated with this module is requested. Here, that method creates a new Magpie application by calling the new() constructor of the Magpie::Machine class-- all Magpie applications are an instance of that class.

By itself, this instance of the Machine class does nothing useful; we must load the Application Class (or classes) that will perform required operations and the Output Class that will generate the appropriate response. We do this by calling the Machine instance's pipeline() method and passing in a list containing a mix of either the Perl package names of the classes we want to load or blessed instances of those classes. In the example above, we loaded two classes into the Machine's application pipeline: My::Greetings and Magpie::Output::Scalar by simply passing in the class names.

Finally, we set the application in motion by calling the Machine instance's run() method. This method takes a single argument that may contain any sort of Perl reference (hash reference, array reference, blessed object). and, whatever data structure or object that is passed as that argument is made available to all methods in the Application and Output classes that are called as the application runs (more about how this works in the next section). When the run() method is called, each class that was passed in via the pipeline() method is loaded in the order they we passed and all event handler methods implemented in those classes that match the current application state are called. When the event handlers from one application class are completed the Machine loads the next class and calls its methods-- and so on, until the last method in the Output class is called, at which point the response has been sent and the application pipeline terminates.

Application Classes

In Magpie, application classes are implemented as subclasses of an event model. This underlying Event class is responsible for registering event handlers with Magpie's internal queue and for determining which of those registered handlers will be fired in response to the current state. In short, the Event class determines which state the application is in, and which of the registered event handler methods will be fired in response to that state. Usually, the details of mapping states to events are never visible to the developer beyond the initial choice of which Event model to use as a base class (different Event classes use different conditions to determine application state). In daily practice, you simply implement and register the events that you application needs and let Magpie do the rest.

First, notice that the application class is a subclass of the Magpie::Component. Through this interface (and the roles it consumes), you get access to the core attributes and methods of the Magpie application framework (see the section titled 'know thy $self' below). In Magpie::Event::Simple you register a list of state events via the required registerEvents() function. The event model then determines which event handler method to fire by examining the value of a specific querystring or POSTed form parameter. (The default param is named "appstate" but that can be overridden by implementing the state_param() method and returning some other value). If the underlying Event model finds a registered event whose name matches the value returned by the state_param() method, the event handler method named event_<eventname> is called. So, for example, a request to the URI that exposes the class above like the following:

http://example.org/apps/greet?appstate=morning

would cause the event_morning() method to be called. If no matching state is found, the event_default() event handler method is called as a fallback. In addition, most Magpie Event model classes also implement an event_init() handler and an event_exit() handler. These methods are optional and, if implemented, event_init() will be called after the event queue is initialized but before the first state-determined event handler is fired while event_exit() is called just before the application exits.

Note that Magpie::Event::Simple (that determines application state based on a single form param) is only one possible event model and you are free to choose another and/or write your own-- even mixing application classes based on different models within the same application pipeline. This flexibility is one of Magpie's key strengths.

Event Handler Methods

Every Magpie Application class will implement one or more event handler methods that will be called conditionally based on the state that is determined by the underlying event model. It is within these methods that the real business of the application takes place. In the above example, these methods merely set a key in the application-wide $ctxt hash reference, but there is no limit to what you can do. Remember, part of the point of Magpie is to separate state detection from application code-- this is achieved by having the event model parent class determine the state then call the event handler methods that implement the code that should be run for that state.

Each event handler method is passed two arguments: the $self class instance member, and a special application context member (named $ctxt in the examples above).

Know Thy $self

In Magpie Application classes the $self class instance member offers access to a handful of common attributes and methods:

$self->request

This offers acccess to Plack::Request object representing the current client request.

Example:

if ( $self->request->method eq 'POST' ) {
...
}

Keeping Things In $ctxt

The second argument passed to each event handler method is the context member (named $ctxt in these examples). The context member-- which can be any kind of Perl object or reference to another data structure-- is passed as the sole argument to the application pipeline's run() method.

In keeping with Magpie's general goal of letting developers do what makes the most sense to them, Magpie does not enforce many rules about what the $ctxt can be, or how it can be used. The only constraint is that the context member must be a scalar or reference. Typically, the context member is a reference to a Perl hash, an anonymous hash reference, or a blessed object. Again, some examples that might appear in your in your interface module/script:

Obviously, the role of the context member will vary greatly depending upon your coding style and the needs of the application. In general, though, the most common use of the $ctxt is to accumulate the data needed to render the proper output for the current request. For example, if you are using the Template Toolkit Transformer class (Magpie::Transformer::TT2) you might use a plain hash reference as the context member, then use it to capture the template name and variables that your templates depend on to deliver the content.

Event Handler Return Codes

Each event handler method must return one of a number of event handler return codes. The codes signal Magpie's internal event loop about what to do after the current event handler method is finished. The codes themselves are numeric, but are implemented as convenient Perl constants via the Magpie::Constants module so you do not have to try to remember what the numeric codes are (this is similar to the way the Apache::Constants module works for HTTP return codes).

Returning DECLINED from your event handler method tells Magpie's internal event queue to skip to the next application or output class in the pipeline. Any other methods in the current application class that would usually be fired based on the current state will be skipped.

Where the DECLINED return code signals Magpie to skip to the very next class in the pipeline, returning OUTPUT from your event handler method tells Magpie's internal event queue to skip to very last class in the application pipeline (which is presumed to be the Output class for the current application).

Returning DONE from your event handler method stops the application pipeline dead in its tracks. All subsequent classes that may be in the pipeline are skippedIt is rarely used, given it typically stops the application before any data is sent to the client, but it can be useful for sending appropriate HTTP response codes.