Leo Simons challenged me again =) Okay, thinking about it some. No code but...
You don't really need continuations for linear web programming. They are the best existing way of representing the idea though. You need something to track the state, and something to get compiled (or interpreted and have state stored) representing the state machine. Ruby doesn't have macros (tons of fun stories there) but method_missing, mixins, and singletons (not GoF singletons) allow for very similar domain specific structures.
The problem, I tend to think a little non-linearly, sorry for jumps, being addressed is linear programming for web apps in ruby. Rails rocks, but it is another action based system. Ruby supports first class continuations. They are, however, bound to the (ruby) thread from which they were generated. This is a bit tricky for web applications. I don't know for sure how Borges deals with it, but from playing with Borges... I am going to guess "not well" is the answer.
Okay, so we need to either use a single-thread model, be able to re-attach a request to the thread that the session is associated with... hmm, I wonder if continuations span fork
in ruby, anyway... or fake out continuations by managing a seperate binding stack (convenient as, while the performance is less than ideal) you can treat a scope binding as a first class object in ruby.
More brain dumping, need some music... okay. So we need to simulate the behavior. It is acceptable to require explicit binding of state which traverses responses to something (we'll call this a wombat for lack of good name and not wanting to interrupt thoughts), that is reasonable. It beats state machines (wheee), even if it is a touch unnatural. So you have something like:
class BlogPoster < Wombat
include RepositoryHelper
def create_entry
title, body = get_title_and_body until title and body
category = case body
when /[Jj]ava/, /argumentative/ then "java"
when /[Gg]roovy/ then "groovy"
when /[Rr]uby/ then "ruby"
else "misc"
end
begin
entry = repo.createEntry(category, title, body)
rescue
send_page "data_error"
end
send_page "home"
end
# returns request params specified
def get_title_and_body
self.get_response("entry_input", [:title, :body])
end
end
Hmm, looking at that and no go. Need to keep stack information, pointer to execution, etc. Continuation. Okay, so we know we need continuations, and we know continuations are bound to the ruby thread that spawned them. get_response
specifically needs to do a callcc
to something which will hold the continuation and come back on next call from client.
Okay, diving into Borges to see how they handle multithreading... argh am supposed to be hacking other things...