Provided by: libjifty-perl_1.10518+dfsg-3ubuntu1_all bug

NAME

       Jifty::Manual::Continuations - There And Back Again

DESCRIPTION

       Continuations are a powerful concept in computer science -- in a nutshell, they allow you
       to store away the state of of the interpreter at any given point.  More importantly, they
       allow you to return to that state at any later time, by calling the continuation with, and
       evaluation of that interpreter state will resume.  They are a concept that first arose in
       LISP, but have implementations these days in Ruby, Scheme, Haskell, and Smalltalk, to name
       a few.

       Thus, continuations allow you to preserve context, and return to it later.  This is
       amazingly useful in web programming, which is limited to "HTTP", which is an inherently
       stateless protocol.  By passing around continuations, we can keep track of the context
       that got us to the current page.

       While we can't construct full continuations at the interpreter level -- because Perl does
       not support them -- we can implement them at the level of HTTP requests.  In technical
       terms, because they capture the control stack up from the beginning of a user's session,
       they are called delimited continuations.

       Continuations are more useful than sessions. Sessions store information across browser
       windows. Sessions may also break in the presence of the back button, as the information
       displayed on the screen, and the information stored in the session may differ.  Since
       continuations are immutable, and a new one is produced every time a change is made, the
       information displayed in the browser cannot get out of sync with the information contained
       in any associated continuation.

USING CONTINUATIONS

   As simple links in templates
       The simplest form of continuation use is in a template, using "tangent" in Jifty::Web, as
       follows:

           <% Jifty->web->tangent( url   => "/someplace",
                                   label => "Go someplace") %>

       This will create a link, which, when clicked, will store the current request into a
       continuation, and jump to the url "/someplace".  In the "/someplace" template, you can
       display information, and possibly have the user navigate between multiple pages before
       returning to the previous page:

           <% Jifty->web->return( label => "Back to whence you came" ) %>

       Because this "return" does not carry a result value, you can think of it as a form of
       "gosub".  In comparison, ordinary hyperlinks are akin to "goto" statements.

       Sometimes, it may be possible for the user to get to a location without having a
       continuation set.  In that case, clicking on the "Back to whence you came" link will
       appear to do nothing -- which may be slightly confusing to the user.  To remedy this,
       Jifty provides a way to specify a default location to return to:

           <% Jifty->web->return( to => "/default", label => "Go back" ) %>

   Using return values
       All of the above examples generate links, which means that they don't interact at all with
       actions.  However, continuations can also be useful in creating complex multi-page
       actions.

       Continuations are saved -- and the browser is redirected to the new URL -- just after all
       actions have been checked for validation but before any of them are run.  This means that
       the new request has access to the full validation state of its parent's actions.

       When a continuation is called, it first checks that all actions in the request were
       successful; if any failed, then the continuation is not called.  If the request's actions
       were all successful, it merges together the Jifty::Results of current Jifty::Response with
       those in the Jifty::Response stored in the continuation.  In doing so, parameters are
       mapped using Jifty::Request::Mapper.  This makes it possible to return values from
       continuations into arbitrary places.  For example:

           % my $action = Jifty->web->new_action(class => 'AddTwoNumbers');
           <% Jifty->web->form->start %>
           <% $action->form_field( 'first_number' ) %>
           <% $action->form_field( 'second_number',
                  default_value => {
                      request_argument => "number",
                  }
              ) %>
           <% Jifty->web->tangent(
                   url    => '/pagetwo',
                   label  => 'Enter a second number',
                   submit => $action
              ) %>
           <% Jifty->web->form->end %>

       ..and in "/pagetwo":

           <% Jifty->web->form->start %>
           <input type="text" name="number" />
           %# We use as_button to tell Jifty that we want a button, not a link
           <% Jifty->web->return( label => 'Pick', as_button => 1 ) %>
           <% Jifty->web->form->end %>

       ..and assuming that "AddTwoNumbers"'s "take_action" resembles:

           sub take_action {
               my $self = shift;
               my $one = $self->argument_value("first_number");
               my $two = $self->argument_value("second_number");
               $self->result->message("Got " . ($one + $two));
           }

       The first page renders the entry box for the first number; the second input is hidden
       because Jifty notices that it is based on a mapped value: i.e., its default is set to
       "{request_argument => "number"}" instead of a plain scalar value.

       Pressing the button validates the action but does not complete running it.  At this point,
       the "second_number" argument to the "AddTwoNumbers" action has no real value -- however,
       it knows that it will, at the earliest possible opportunity, fill in its value from the
       "number" request parameter.

       Jifty tangents to "/pagetwo", where we enter and submit a "number" argument.  Control then
       returns to the original page, where the request mapper maps the "number" value into the
       "second_number" argument of the "AddTwoNumbers" action, which then runs because it has
       received all arguments it requires.

       Note that in the example above, the "number" argument is a plain request argument, not
       part of another action.  More complex mappings are possible, including grabbing the
       results of or arguments to actions.  This would make it possible, for instance, to use an
       action on the second page to validate the number before returning.  This is slightly
       different from placing a validator on the "AddTwoNumbers" action, as that validator only
       gets called after control has already returned to the first page.

   As dispatcher rules
       The "tangent" in Jifty::Web function is context-aware -- if it is called in void context,
       it immediately saves the continuation and redirects to the new url.  This is particularly
       useful, say, for authentication protection in "before" blocks:

           before '/protected' => sub {
               # shorthand for: Jifty->web->tangent( url => '/login' )
               tangent('/login') unless Jifty->web->current_user->id;
           };

       And in the "/login" template:

           % my $action = Jifty->web->new_action(class   => 'Login',
           %                                     moniker => 'loginbox' );
           <% Jifty->web->form->start %>
           <% $action->form_field('username') %>
           <% $action->form_field('password') %>
           <% Jifty->web->return( to     => "/protected",
                                  label  => 'Login',
                                  submit =>  $action) %>
           <% Jifty->web->form->end %>

       This establishes a button, which, if the "Login" action is successful, calls the stored
       continuation, or, lacking one, redirects to "/protected".

       As currently implemented, these redirect-from-dispatcher tangents works exactly like
       rendered-as-links tangents, in that when they return, all rules in the dispatcher are
       still executed from the start.  Therefore the "unless" guard in the "before '/protected'"
       rule above is necessary to prevent recursion.

GORY DETAILS

       Jifty's continuations are implemented in Jifty::Continuation, which is very little more
       than a place to store a Jifty::Request and its associated Jifty::Response.

       The following diagram diagrams the stages of continuation handling, and their interaction
       with the dispatcher.  For clarity, the page region handling code is included, but page
       regions do not currently interact with continuation processing.

                                       /--------------\
                 +---------------------v-+            |
                 |........Request........|            |
                 +-|-------------------|-+            |
                   |                   |  RETURN  +---|---------------------+
           /----\  |                   \----------> Replace request with    |
           |  +-|--|-+ +==============+           | request in continuation |
           |  |.v..v.---> SETUP rules |           +-------------------------+
           |  |......| +==============+
           |  |..D...|
           |  |..I...| +~~~~~~~~~~~~~~~~~~~+      +-------------------------+
           |  |..S...---> Validate actions |      | Store current request   |
           |  |..P...| +~~~~~|~~~~~~~~~|~~~+ SAVE | and response, redirect  |
           |  |..A...|       |         \----------> to new scope and URL    |
           |  |..T...|       |                    +-------------------------+
           |  |..C...| +~~~~~v~~~~~~~~~~~~~+
           |  |..E...| |  Run actions      |      +-------------------------+
           |  |..R...| +~~~~~~~~~~~~~~~|~~~+ CALL | Merge results into the  |
           |  |......|                 \----------> continuation's results; |
           |  |......|                            | redirect to return URL  |
           |  |......| +==============+           +-------------------------+
           |  |......---> RUN rules   |
           |  |......| +=====|========+
           |  |......|       |
           |  |......|    +--v---------------+
           |  |......|    | Show templates   |
           |  |......|    +-------|----------+
           |  |......|            |
           |  |......|    +-------v----------+
           |  |......|    | Show page region ---------------------\
           |  |......|    +------------------+                    |
           |  |......|                                            |
           |  |......| +==============+                           |
           |  |......---> AFTER rules |                           |
           |  +------+ +==============+                           |
           |                                                      |
           \------------------------------------------------------/

       As shown in the diagram above, there are three different operations that continuations
       use.  The first is "SAVE", which is triggered by the query parameter <J:CREATE>.
       Continuations are saved after validating actions; the continuation itself is attached to
       the user's session object.

       The current saved continuation is automatically preserved across requests.  When the time
       comes to call the continuation, the "CALL" operation is performed; this is usually
       triggered by the presence of the <J:CALL> query parameter.  This causes the stored request
       to be query-mapped using Jifty::Request::Mapper, but using the current request and
       response (not the continuation!) as the sources for mapping values.  Then, the result
       objects are merged, with results from the stored response taking precedence.  This new
       mapped request and new merged response are formed into a new continuation.

       In order to ensure that the browser's URL matches the URL of the request in the
       continuation, Jifty then does a redirect to the URL of the request stored in the
       continuation, starting the last continuation operation, the "RETURN".  When Jifty detects
       the "RETURN" operation, most often by the presence of "J:RETURN", it loads the
       continuation and reads the stored request and response into the current request and
       response.