Applets

The Arrow::Applet class is the main workhorse of the Arrow framework; it, in concert with Arrow::Transaction, provides most of the functionality that application developers will be concerned with.

Applets make up the functionality of an application; each distinct group of tasks that a user can do is expressed as a separate class which inherits from Arrow::Applet. The base Applet class defines much of what is covered in the rest of this tutorial, so if you don’t like the templating system or the argument validation system built into the base applet class, you can easily define your own that uses something else instead without having to give up any of the other functionality.

For now, though, this tutorial will focus on the default functionality, with mention made of how to override or replace it where appropriate.

The Parts of an Applet

An Arrow applet is made up of a signature, one or more actions, and an optional delegator method.

The Signature

The signature is a Hash of metadata which describes the applet to the app framework, and includes information like name and author, and configures some of the logic and functionality provided by the Arrow::Applet class itself. An Applet’s signature is set by calling class methods in the definition to configure it.

Here’s an example of an applet signature for a Hello World applet:

   1  require 'arrow/applet'
   2  
   3  class HelloWorldApplet < Arrow::Applet
   4      # Applet signature
   5      applet_name        "Hello World"
   6      applet_description "This is yet another implementation of Hello World"
   7      applet_maintainer  "ged@FaerieMUD.org"
   8      applet_version     "1.2.2"
   9  
  10      default_action :greet
  11      template :greeting => 'templates/greeting.tmpl'
  12  
  13      # Define some actions...
  14  end
Setting applet signature values

Signature Values

No element of the signature is mandatory: the base applet class will try to guess appropriate values for any missing elements. The values the base applet class understands are listed below along with what they do.

applet_name String
The name of the applet, used for display purposes in introspection and for automated bug- or error-reporting. Defaults to the name of the Applet class.
applet_description String
A brief explanation of what the applet does or is for; used in introspection utilities. Defaults to "(none)".
applet_maintainer String
The RFC822-formatted email address of the person to praise and/or blame for the functioning of the applet. This is intended to be used in introspection utilities and also for automated bug- or error- reporting. Defaults to whatever the ServerAdmin of Apache is set to, or the empty string if the ServerAdmin setting is not available.
applet_version String
The version of the applet. If the applet defines any of the Version, Revision, SVNId, or Rcsid constants, the value it contains is used as a default. Otherwise the mtime of the file which contains the applet is used.
default_action Symbol
When no action is specified by the incoming request, this determines which one will be used. Defaults to '_default'.
templates Hash
This should be set to a hash filled with file names of the templates that will be used by this applet. Templates listed in this hash can later be loaded via the #load_template method of the applet object. Defaults to an empty hash. See the templating tutorial for more coverage of this
validator Hash
A hash containing profiles for the built in form validator, one per action. See the input validation section of the tutorial for more.
appicon String
The URL for the icon that will be used to represent this applet in introspection output. Optional, defaults to 'application-x-executable.png', which is an icon in the

Actions

Actions provide the actual functionality of the applet. An action is just a specially-named method which corresponds to a part of the URI the client requests, and represents a step or phase of the applet’s execution.

Action Method Form

Each action method’s name must end with _action. For example, if you’re defining the ‘display’ action, you’d define a method called display_action:

   1  require 'arrow/applet'
   2  
   3  class HelloWorldApplet < Arrow::Applet
   4  
   5      ### Action: Display some content
   6      def display_action( txn, *args )
   7          # ...do something with the transaction 'txn'...
   8          return "Some content"
   9      end
  10  
  11  end
Defining an action

Action methods are called with an Arrow::Transaction object and zero or more arguments that can be passed REST-style in the URI. Only the transaction argument is mandatory; defining an action method without parameters will result in an error.

If you like, you can also distinguish actions from other methods in the applet by using the def_action declaration to define an action:

   1  require 'arrow/applet'
   2  
   3  class HelloWorldApplet < Arrow::Applet
   4  
   5      ### Action: Display some content
   6      def_action :display do |txn, *args|
   7          # ...do something with the transaction 'txn'...
   8          return "Some content"
   9      end
  10  
  11  end
Defining the same action with `def_action’

The two declarations are equivalent.

Action Execution

When an action method is called, it should do the necessary processing to build the response data and return it to its caller. This return value will then be converted to a String (via #to_s) and sent to the client after doing the necessary header senting and Apache housekeeping. Returning a non-true value causes a DECLINED status to be sent back to Apache. This can be used to pass control to a further handler if the action determines it cannot handle the request.

For return behaviors outside of these (for example, altered headers or a different status), it is necessary to access the Apache::Request object directly (through txn.request in the examples above). See the mod ruby site for more information on what to do with this.

Delegator Method

As mentioned before, applets can also be chained together to build more complex functionality. Apps which are intended to be chained through should define a #delegate method. This delegation method is called by the broker when the applet is being chained through, and is responsible for calling yield if and when it wishes to pass control to the next applet in the chain, passing any desired additional arguments. Delegation methods are invoked with the same arguments as action methods, including any added by previous applets in the chain.

A simple applet – Hello

We’ll start with a dead-simple applet that doesn’t really do much of anything useful, snippets of which you’ve already seen above. In keeping with tradition, of course, this applet will be yet another implementation of ‘Hello World’:

   1  #
   2  # A "hello world" Arrow applet
   3  #
   4  
   5  require 'arrow/applet'
   6  
   7  # Define the applet class; inherit from the base applet class.
   8  class HelloWorldApplet < Arrow::Applet
   9  
  10    # Applet signature
  11    applet_name "Hello World"
  12    applet_description "This is yet another implementation of Hello World"
  13    applet_maintainer "ged@FaerieMUD.org"
  14    
  15    default_action :greet
  16    
  17    # Define the 'greet' (default) action
  18    def greet_action( txn )
  19      return <<-EOF
  20      <html>
  21        <head><title>Hello</title></head>
  22        <body><p>Hello, world.</p></body>
  23      </html>
  24      EOF
  25    end
  26  
  27  end
A Hello World applet

The applet’s class is HelloWorldApplet, which means to install it into the server’s URI-space you’d add a line like the following to the layout hash of the applets section of the arrow config file:

   1  applets:
   2    layout:
   3      "/hello": Hello
Installing the Hello World applet.

After the config has been loaded, requests for /<arrow-root>/hello/greet (and simply /<arrow-root>/hello, because greet is the default action) will return something that looks like:

Passing REST-style Arguments

Now let’s add a bit of personalization to the applet. Perhaps it should be able to generate customizable messages in addition to greeting the entire world. To do this, we can make the greet action take an additional argument specified in the URL:

   1  # Define the 'greet' (default) action
   2    def greet_action( txn, name=nil )
   3      name ||= "world"
   4  
   5      return \
   6          %{<html>} +
   7          %{  <head><title>Hello</title></head>} +
   8          %{  <body><p>Hello, #{name.capitalize}.</p></body>} +
   9          %{</html>}
  10    end

Calling the applet with a URL like /hello/greet/Dave will now cause it to greet only Dave instead:

I don't think I can do that, Dave.

Note that we’re required to call the action ‘greet’ explicitly to be able to pass in URL-style arguments.

Special Applets

Error Handlers

Missing Applet Applet