Created
June 30, 2009 23:23
-
-
Save jedediah/138493 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra/base' | |
require 'fiber' | |
module Sinatra | |
module Strands | |
REQUEST_METHODS = [:get,:put,:post,:delete] | |
class Strand | |
def initialize *args, &block | |
@secure_id = object_id # TODO: not actually secure | |
@fiber = Fiber.new do | |
@route_context.instance_exec *args, &block | |
end | |
end | |
attr_reader :secure_id | |
def yield response | |
raise ServerError.new "Internal error: tried to yield from outside a strand" unless @fiber == Fiber.current | |
Fiber.yield response | |
end | |
def uri qs={} | |
s = "/_strand/#{@secure_id}" | |
unless qs.empty? | |
s << (qs.map do |k,v| | |
k = Rack::Utils.escape(k) | |
if v.is_a? Enumerable | |
v.map {|v| "#{k}=#{Rack::Utils.escape v}" }.join('&') | |
else | |
"#{k}=#{Rack::Utils.escape v}" | |
end | |
end) | |
end | |
return s | |
end | |
def resume route_context | |
@route_context = route_context | |
@fiber.resume route_context | |
end | |
attr_reader :route_context | |
def alive? | |
@fiber && @fiber.alive? | |
end | |
end # Strand | |
module Helpers | |
def _strand_resume sid, route_context | |
strands = Sinatra::Strands.all_strands | |
$stderr.puts "\e[1;33mresuming strand \e[1;35m#{sid} \e[1;34m(#{strands.size} total)\e[0m" | |
# $stderr.puts "all strands: #{strands.inspect}" | |
raise NotFound.new "Strand #{sid} doesn't exist" unless strands and @_strand = strands[sid] | |
raise NotFound.new "Strand #{sid} is dead" unless @_strand.alive? | |
page = @_strand.resume route_context | |
strands.delete sid unless @_strand.alive? | |
page | |
end | |
def strand_uri qs={} | |
@_strand.uri qs | |
end | |
def show response | |
@_strand.yield response | |
end | |
end # Helpers | |
class << self | |
attr_reader :all_strands | |
def registered app | |
# Sinatra passes us Default which is no good because | |
# Application doesn't inherit Default's routes | |
# This might be fixed in edge | |
app = Sinatra::Application | |
app.helpers Helpers | |
app.configure do | |
Sinatra::Strands.instance_eval { @all_strands = {} } | |
end | |
REQUEST_METHODS.each do |meth| | |
app.send meth, %r{/_strand/(\d+)} do |sid| | |
_strand_resume sid.to_i, self | |
end | |
end | |
end # Strands.registered | |
end # << self | |
def strand_route meth, path, opts={}, &block | |
raise ServerError.new "Can't handle request method '#{meth}' with a strand" unless REQUEST_METHODS.include? meth | |
send meth, path, opts do |*args| | |
strands = Sinatra::Strands.all_strands | |
@_strand = Strand.new *args, &block | |
sid = @_strand.secure_id | |
strands[sid] = @_strand | |
$stderr.puts "\e[1;32mstarted strand \e[1;35m#{sid} \e[1;34m(#{strands.size} total)\e[0m" | |
# $stderr.puts "all strands: #{strands.inspect}" | |
page = @_strand.resume self | |
strands.delete sid unless @_strand.alive? | |
page | |
end | |
end | |
REQUEST_METHODS.each do |meth| | |
define_method "strand_#{meth}" do |path, opts={}, &block| | |
strand_route meth, path, opts, &block | |
end | |
end | |
end # Strands | |
register Strands | |
end # Sinatra |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment