NAME

Jifty::Manual::Actions - Doing Stuff With Jifty

DESCRIPTION

Jifty::Action abstracts around the idea of declaring named ("parameters") at compile time. At runtime, the action collects user input as ("arguments"), do something with them, and returning some result to the user. If this sounds incredibly general, that's because it is -- actions do nearly everything in Jifty.

Jifty::Action will also generate HTML for you from its parameters -- no more manually writing <input> tags and extracting GET and POST arguments by hand and dispatching them where they belong -- Jifty::Action does it all for you.

WRITING ACTIONS

Jifty provides some actions for you out of the box -- see Jifty::Manual::ObjectModel and Jifty::Action::Record for autogenerated actions, as well as Jifty::Action::Redirect, but any non-trivial application will want to define actions of its own. This is how you do it.

Every action is a subclass of Jifty::Action, as well as typically AppName::Action. Actions usually live in the AppName::Action:: namespace; While that's just convention, it will make your life easier if you follow it.

This, the simplest possible action is:

use warnings;
use strict;

package MyApp::Action::DoNothing;
use base qw/MyApp::Action Jifty::Action/;

1;

(Instead of copying-and-pasting that, or typing it in, though, you could just run:

jifty action --name DoNothing

in your application's directory, and Jifty would create a skeleton for you. )

However, if you want to actually do something with your actions, you need to define two things: their parameters, and a "take_action" method.

parameters

Every Jifty::Action subclass should define a schema, which contains some param declarations that describes what arguments it takes. Supposing we were writing a an action to post a blog article, we might start out with parameters like thus:

use Jifty::Param::Schema;
use Jifty::Action schema {

param 'title';
param 'category';
param 'body';

};

However, we've only scratched the surface of the power the param API offers. Parameters can have types, labels, validators, canonicalizers, and even more. To start with, lets add some types and labels:

use Jifty::Param::Schema;
use Jifty::Action schema {

param title =>
    label is 'Title',
    length is 50,
    is mandatory;

param category => 
    label is 'Category',
    length is 30;

param body =>
    label is 'Entry',
    render as 'Textarea';

};

Now, we can ask the action to render form fields, and it will know how to display them. But, we can do even better. Let's improve the look of that category field, by making it a combobox (a combination dropdown/text field), with some default values available:

# ...
param category => 
    label is 'Category',
    render as 'Combobox',
    available are qw( Personal Work Block );
# ...

But a static list is lame. What we really want is a Category model, and to keep track of all the categories users have entered:

# ...
param categories => 
    label is 'Category',
    render as 'Select',
    available are defer {
        my $categories = MyBlog::Model::CategoryCollection->new;
        $categories->unlimit;
        [{
            display_from => 'name',
            value_from   => 'name',
            collection   => $categories,
        }];
    }
...

Now, Jifty will populate the combobox with the result of calling name on each element on $categories. Alternatively, if you set <value_from = 'id'>>, Jifty would automatically return the id of the category, for easy database reference. We don't do this with the combobox, however, since a combobox displays the selected value in its text field.

See Jifty::Action and Jifty::Web::Form::Field for more fields you can set in the param declaration, and see Jifty::Param::Schema for more about the syntax.

validation

Jifty::Action can automatically validate arguments for you, as appropriate. If an argument has valid_values, then Jifty::Action will automatically verify that the given value matches one of them. However, you can also write your own validators. Just write a sub validate_<argument>, and it will be called as appropriate:

use Regexp::Common 'profanity_us';

sub validate_body {
   my $self = shift;
   my $body = shift;
   if ( $body =~ /$RE{profanity}/i) {
       return $self->validation_error( body => 'Would you speak like that in front of your mother? *cough*');
   }
   return $self->validation_ok('body');
}

You can also do validation in the model -- see Jifty::Action::Record

Note that if, instead of failing, you want to automatically modify invalid content to be valid, you want a canonicalizer, not a validator.

Additionally, if you set ajax_validates or ajax_canonicalizes to true for an argument, then Jifty will automatically validate or canonicalize it in an AJAX-enabled browser when the user stops typing.

take_action

Once an action has arguments, it needs to do something with them. An action does so in its take_action sub, which will be called when an action is submitted, and only if its arguments validate.

Inside sub take_action, subclasses can access their arguments via $self->argument_value('foo'). If you need to check whether you've been passed an argument or not (as opposed to being passed a true argument or not), use $self->has_argument('foo').

Once an action has done its thing, it needs to inform the caller whether or not it has succeeded, possibly with some status message. To this end, every Jifty::Action has a Jifty::Result associated with. Jifty::Result carries both a failure/sucess code, and a textual message describing the result of running the action.

Thus, if your action failed for some reason, you would, in take_action, write code like:

$self->result->error('Couldn't write blog post');
return;

If, however, the action completed successfully, you might write:

$self->result->message('Posted to your blog');

Actions will default to successful with an empty message if you don't do anything. Additionally, if you need to return more semantic information than a simple message, you can set arbitrary content on the result, using $self->result->content, e.g:

$self->result->content( id => $new_post->id);

This information can be then used elsewhere to, for example, automatically redirect you to a view page for that new blog post. See Jifty::Request::Mapper for some more information.

USING ACTIONS

At their simplest, you can create and run actions yourself, e.g.:

Jifty->web->new_action(
    class     => 'PostBlogEntry',
    arguments => {
        title    => 'A boring blog entry',
        category => 'Jifty',
        body     => 'This blog entry is lame.'
    }
)->run;

Note that Jifty->web->new_action, and all similar methods (e.g. Jifty::Request::add_action, Jifty::Web::Form::add_action), will automatically qualify the class with either Jifty::Action:: or AppName::Action:: as necessary (I told you putting actions in AppName::Action:: would make your life easier!)

In practice, you'll rarely provide actions with arguments yourself. Instead, you'll create an action with no or partial arguments, often in the dispatcher, or a Mason component's <%init%> block (See "constructor arguments" for details about passing arguments to actions on creation).

my $create = Jifty->web->new_action(
    class   => 'PostBlogEntry',
    moniker => 'post_blog'
);

Having created the action, you will, in one of your Mason components, output a form where the user can fill in the action's arguments:

<% Jifty->web->form->start %>
<div class="post-metadata">
  <% $create->form_field('title') %>
  <% $create->form_field('category') %>
</div>
  <% $create->form_field('body') %>
<% Jifty->web->form->submit(label => "Post") %>
%# or <% Jifty->web->form->link(label => "Post", submit => # $create) %>
%# or <% $action->button(label => "Post"); %>
<% Jifty->web->form->end %>

form_field will render the field, along with the label as an HTML <input> tag that Jifty knows how to interpret to feed back to your action as an argument when the form is submitted. If you need to change the appearance of the field, Jifty outputs classes on the fields, as well as providing some semantic <div>s you can style using CSS. (TODO: Document Jifty HTML classes and default CSS).

See "submit" in Jifty::Web::Form, "link" in Jifty::Web and "button" in Jifty::Action for details on the different ways to generate a submit button.

Additionally, instead of form_field, you can use hidden to generate a hidden input, which will not be viewable or editable in a web browser. (Note that a knowledgeable user *can* still submit a form with a different value for that hidden input; If this concerns you, make sure you have appropriate ACLs in place. If it still worries you, you probably want a continuation.

monikers

You probably noticed the moniker => 'post_blog'. Every action you create in Jifty has an associated moniker. A moniker is simply a unique identifier for the action (unique per request, which in practice typically means per HTML page). Since actions are constantly being serialized (over HTTP, or Javascript AJAX calls, and so on), and unpacked, we need a way refer to specific actions other than just object identity, e.g. to extract its arguments or results in the dispatcher. Monikers give us that. Given a moniker, you can pull information about the associated action out of a request or response.

If a moniker is unspecified, it will be autogenerated.

(XXX TODO Note about action registration here)

Argument Folding

If you write out more than one form_field for a given argument in the same form, and more than one is filled in, Jifty will fold the arguments into an array before filling them in to the action. This provides a way to do, e.g. a BulkEdit action that applies some set of changes to many records at once.

(XXX TODO Note about constructor parameters)

ACTIONS AS WEB SERVICES

Your actions are also automatically published as web services. Clients can POST requsets, usually using the YAML or JSON request format. See bin/service for a trivial generic webservice client.

(XXX TODO More about webservices)

SEE ALSO

Jifty::Action, Jifty::Manual::Tutorial