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.
An Arrow applet is made up of a signature, one or more actions, and an optional delegator method.
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
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
Stringapplet_description
String"(none)"
.applet_maintainer
StringServerAdmin
of Apache is set to, or the
empty string if the ServerAdmin
setting is not available.applet_version
StringVersion
, 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'_default'
.templates
Hash#load_template
method of the applet object. Defaults to an empty hash.
See the templating tutorial for more coverage of thisvalidator
Hashappicon
String'application-x-executable.png'
,
which is an icon in the 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.
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
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
The two declarations are equivalent.
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.
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.
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
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
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:
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:
Note that we’re required to call the action ‘greet
’ explicitly to be able to pass in URL-style
arguments.