Brian Candler <B.Candler / pobox.com> wrote in message news:<20021006110515.A13624 / uk.tiscali.com>...
> Hello,
> 
> One of the things which interests me most about Ruby is its built-in
> threading capability.
> 
> In particular, I would like to know if anyone has written a library which
> allows CGI sessions to be modelled as threads? This would make it extremely
> easy to write interactive CGI applications, as the thread would only see
> exchanges between itself and its one client.

Brian,

I've done a lot of work on and with libraries very much like what you
describe. My talk at RubyConf, in fact, will be on exactly this
approach.  Most of my work has been in Smalltalk, although I did spend
some time this summer porting the basic ideas to Ruby; I'll be
updating that port before my talk. If anyone is interested in taking a
look at it, email me, but if you can wait until November it will be in
much better shape.

A few comments:

> For example, you might start writing a simple login screen like this:
> 
> ----------------------------------------------------------------------------
> retries = 0
> 
> begin
>   show 'login_page.htm'
>   u = wait_response
>   retries+=1
> end while retries < 3 && u.username!='brian' && u.password!='mypass'

The explicit wait_response in unnecessary - this can just be

begin
  u = show 'login_page.htm'
  retries += 1
end while retries < 3 && u.username!='brian' && u.password!='mypass'
 
> wait_response blocks until the next CGI request (HTTP post) which is
> associated with this particular thread arrives. It also commits suicide if
> no response is received within an idle timeout period.
<snip>
> Perhaps for safety, the 'show' function should set a different cookie every
> time, to ensure that the user cannot press 'back' multiple times and then
> submit an out-of-sequence form - or else the server thread should have the
> option of handling this case itself.

Yes, exactly, although you need to encode this in the URL, not in a
cookie, so that you can actually detect which page the user
backtracked to.  What I've found works best is this:

- by default, if the user backtracks (or clones the browser window, or
whatever), everything keeps working as before.  If the user submits a
form multiple times, *that call to show() simply returns multiple
times*, and the thread proceeds from there.  This is easy to do with
ruby's callcc function, and is really what makes the whole approach so
cool.

- some times that's not appropiate; for that, you need to be able to
set up "transactions" in the thread.  For example, a piece of a
shopping cart app might look like

cart = confirm_cart_contents
cc = ask_for_credit_card
process_payment(cart, cc)
show_thankyou_page

You don't want a user to hit the back button and change the cart
contents once the credit card has gone through!  So you have something
like this:

session.isolate do
  cart = confirm_cart_contents
  cc = ask_for_credit_card
  process_payment(cart, cc)
end
show_thankyou_page

While inside the "isolate" block, backtracking works normally (they
can, for example, go back and change the cart).  As soon as the
"isolate" block ends, all of those pages immediately "expire", in that
attempts to click any links or submit any forms from them will result
in a redirect back to the thankyou page.
 
> Anyway, it seems to me that this ought to be fairly straightforward to
> implement, so has anybody done this already? I have a lot of Ruby learning
> to do before I would attempt it myself :-)

The core is indeed very straightforward to implement; it's probably
about 40 lines of code.  What's fun is all the great stuff you can
build on top once you have this basic facility...

Incidentally, as others have pointed out, this won't work as straight
CGI; the expirements I've done in Ruby use WEBrick, but FastCGI would
also work.

Cheers,
Avi