Skip to content

Instantly share code, notes, and snippets.

@jedediah
Created June 30, 2009 23:23
Show Gist options
  • Save jedediah/138493 to your computer and use it in GitHub Desktop.
Save jedediah/138493 to your computer and use it in GitHub Desktop.
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