[Jifty-commit] r1522 - in jifty/trunk: . lib/Jifty lib/Jifty/Request
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Mon Jul 10 17:20:48 EDT 2006
Author: alexmv
Date: Mon Jul 10 17:20:47 2006
New Revision: 1522
Added:
jifty/trunk/lib/Jifty/Manual/Continuations.pod
Modified:
jifty/trunk/ (props changed)
jifty/trunk/lib/Jifty/Continuation.pm
jifty/trunk/lib/Jifty/Request.pm
jifty/trunk/lib/Jifty/Request/Mapper.pm
Log:
r15161 at zoq-fot-pik: chmrr | 2006-07-10 17:19:44 -0400
* Continuation manual
* Set up the output API for mapping of request parameters (input API
already existed)
* Change method of getting results out of response on continuation RETURN
Modified: jifty/trunk/lib/Jifty/Continuation.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Continuation.pm (original)
+++ jifty/trunk/lib/Jifty/Continuation.pm Mon Jul 10 17:20:47 2006
@@ -202,15 +202,8 @@
# Pull state information out of the continuation and set it
# up; we use clone so that the continuation itself is
- # immutable. It is vaguely possible that there are results in
- # the response already (set up by the dispatcher) so we place
- # results from the continuation's response into the existing
- # response only if it wouldn't clobber something.
- my %results = $self->response->results;
- for (keys %results) {
- next if Jifty->web->response->result($_);
- Jifty->web->response->result($_,dclone($results{$_}));
- }
+ # immutable.
+ Jifty->web->response(dclone($self->response));
# Run any code in the continuation
$self->code->(Jifty->web->request)
Added: jifty/trunk/lib/Jifty/Manual/Continuations.pod
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Manual/Continuations.pod Mon Jul 10 17:20:47 2006
@@ -0,0 +1,232 @@
+=head1 NAME
+
+Jifty::Manual::Continuations - There And Back Again
+
+=head1 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're a concept
+that first arose in LISP, but has implementations these days in Ruby,
+Scheme, Haskell, 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 C<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 continuations at the
+interpreter level -- because Perl doesn't support them -- we can
+implement them at the level of HTTP requests. In technical terms,
+they are I<delimited continuations>.
+
+Continuations are more useful than session because 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.
+
+=head1 USING CONTINUATIONS
+
+=head2 As simple links in templates
+
+The simplest form of continuation use is in a template, using
+L<Jifty::Web/tangent>, 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 C</someplace>. You
+can think of this as a form of C<gosub>. In the C</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" ) %>
+
+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" ) %>
+
+=head2 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, first checks that all actions in the
+request were successful; if any failed, then the continuation is
+B<not> called. If the request's actions were all successful, it
+merges together the L<Jifty::Result>s of current L<Jifty::Response>
+with those in the L<Jifty::Response> stored in the continuation. In
+doing so, parameters are mapped using L<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 C</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 C<AddTwoNumbers>'s C<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. Pressing the button validates the action but does not complete
+running it. At this point, the C<second_number> argument to the
+C<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 C<second_number> argument of the C<AddTwoNumbers> action, which
+then runs.
+
+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
+C<AddTwoNumbers> action, as that validator only gets called after
+control has already returned to the first page.
+
+=head2 As dispatcher rules
+
+The L<Jifty::Web/tangent> function is context-aware -- if it is called
+in void context, it immediately saves the continuation and jumps to
+the new url. This is particularly useful, say, for authentication
+protection in C<before> blocks:
+
+ before '/protected' => sub {
+ Jifty->web->tangent( url => '/login' )
+ unless Jifty->web->current_user->id;
+ };
+
+And in the C</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 C<Login> action is
+successful, calls the stored continuation, or, lacking one, redirects
+to C</protected>.
+
+=head1 GORY DETAILS
+
+Jifty's continuations are implemented in L<Jifty::Continuation>, which
+is very little more than a place to store a L<Jifty::Request> and its
+associated L<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 C<SAVE>, which is triggered by
+the query parameter L<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 C<CALL>
+operation is performed; this is usually triggered by the presence of
+the L<J:CALL> query parameter. This causes the stored request to be
+query-mapped using L<Jifty::Request::Mapper>, but using the B<current>
+(not continuation!) request and response 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 C<RETURN>. When Jifty detects the C<RETURN> operation,
+most often by the presence of C<J:RETURN>, it loads the continuation
+and reads the stored request and response into the current request and
+response.
+
+=cut
+
Modified: jifty/trunk/lib/Jifty/Request.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Request.pm (original)
+++ jifty/trunk/lib/Jifty/Request.pm Mon Jul 10 17:20:47 2006
@@ -977,8 +977,9 @@
The current continuation set by passing the parameter C<J:C>, which is
set to the id of the continuation. To create a new continuation, the
-parameter C<J:CREATE> is passed. Calling a continuation is a ssimple
-as passing C<J:CALL> with the id of the continuation to call.
+parameter C<J:CREATE> is passed. Calling a continuation is a simple
+as passing C<J:CALL> with the id of the continuation to call; this
+will redirect to the appropriate url, with L<J:RETURN> set.
=head3 request options
Modified: jifty/trunk/lib/Jifty/Request/Mapper.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Request/Mapper.pm (original)
+++ jifty/trunk/lib/Jifty/Request/Mapper.pm Mon Jul 10 17:20:47 2006
@@ -47,6 +47,11 @@
the result of the C<ACTION>. C<ACTION> may either be a L<Jifty::Action>
object, or a moniker thereof.
+=item C<< KEY => { request_argument => STRING } >>
+
+The C<KEY> will take on the value of the argument named C<STRING> from
+the request.
+
=item C<< KEY => { argument => ACTION } >>
The C<KEY> will take on the value of the argument named C<KEY> from
@@ -75,7 +80,11 @@
if (ref $parameters{$key} eq "HASH") {
my %mapping = %{$parameters{$key}};
- for (grep {/(result(_of)?|argument(_to)?)/} keys %mapping) {
+ if ($mapping{request_argument}) {
+ $return{"J:M-$key"} = join("`","A", $mapping{request_argument});
+ }
+
+ for (grep {/^(result(_of)?|argument(_to)?)$/} keys %mapping) {
my $action = $mapping{$_};
my $moniker = ref $action ? $action->moniker : $action;
my $name = $mapping{name} || $key;
More information about the Jifty-commit
mailing list