Permalink
Browse files

basic async responses, wrap sync in an em::defer

  • Loading branch information...
Josh Hull
Josh Hull committed Jan 18, 2011
1 parent 6de6341 commit d4ff339973c1bee4f304948aa9a9381eb2109d72
Showing with 103 additions and 25 deletions.
  1. +5 −2 Gemfile.lock
  2. +2 −2 apiary.gemspec
  3. +29 −21 lib/apiary.rb
  4. +5 −0 lib/apiary/api_method.rb
  5. +10 −0 lib/apiary/async_response.rb
  6. +21 −0 test/fixtures/basic_async.rb
  7. +1 −0 test/helper.rb
  8. +30 −0 test/test_async.rb
View
@@ -1,13 +1,13 @@
PATH
remote: .
specs:
- apiary (0.0.2)
+ apiary (0.0.3)
callsite
- em-http-request
http_router
method-args
rack
thin
+ thin_async
GEM
remote: http://rubygems.org/
@@ -39,6 +39,8 @@ GEM
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
+ thin_async (0.1.1)
+ thin (>= 1.2.1)
url_mount (0.2.1)
rack
@@ -55,3 +57,4 @@ DEPENDENCIES
rack
rake
thin
+ thin_async
View
@@ -24,8 +24,8 @@ Gem::Specification.new do |s|
s.add_dependency 'thin'
s.add_dependency 'http_router'
s.add_dependency 'rack'
- s.add_dependency 'em-http-request'
+ s.add_dependency 'thin_async'
s.add_development_dependency 'rake'
+ s.add_development_dependency 'em-http-request'
s.add_development_dependency 'minitest', '~> 2.0.0'
-
end
View
@@ -4,45 +4,41 @@
require 'thin'
require 'rack'
require 'apiary/version'
+require 'apiary/api_method'
+require 'apiary/async_response'
module Apiary
- ApiMethod = Struct.new(:method, :http_method, :path)
- attr_accessor :rack_env
+ attr_accessor :rack_env, :async_response
module ClassMethods
- def get(path = nil)
- __set_routing(:get, path)
- end
-
- def post(path = nil)
- __set_routing(:get, path)
- end
-
- def put(path = nil)
- __set_routing(:put, path)
- end
+ [:get, :post, :put, :delete].each do |m|
+ class_eval "
+ def a#{m}(path=nil)
+ __set_routing(:get, path, true)
+ end
- def delete(path = nil)
- __set_routing(:put, path)
+ def #{m}(path=nil)
+ __set_routing(:get, path)
+ end
+ "
end
def version(number)
@version = number
end
- def __set_routing(method, path)
- @http_method, @path = method, path
+ def __set_routing(method, path, async = false)
+ @http_method, @path, @async = method, path, async
end
def method_added(m)
if @http_method
MethodArgs.register(Callsite.parse(caller.first).filename)
@cmds ||= []
- @cmds << ApiMethod.new(m, @http_method, @path)
- @http_method = nil
- @path = nil
+ @cmds << ApiMethod.new(m, @http_method, @path, @async)
+ @http_method, @path, @async = nil, nil, nil
end
end
@@ -65,7 +61,19 @@ def __as_app(&blk)
route.to { |env|
instance = (blk ? blk.call : new)
instance.rack_env = env
- Rack::Response.new(instance.send(cmd.method, *env['router.response'].param_values).to_s).finish
+ response = AsyncResponse.new(env)
+ if cmd.async?
+ instance.async_response = response
+ instance.send(cmd.method, *env['router.response'].param_values)
+ else
+ EM.defer(proc{
+ response.status = 200
+ response << instance.send(cmd.method, *env['router.response'].param_values).to_s
+ }, proc{
+ response.done
+ })
+ end
+ response.finish
}
end
router
View
@@ -0,0 +1,5 @@
+module Apiary
+ class ApiMethod < Struct.new(:method, :http_method, :path, :async)
+ alias_method :async?, :async
+ end
+end
@@ -0,0 +1,10 @@
+require 'thin/async'
+
+module Apiary
+ class AsyncResponse < Thin::AsyncResponse
+ def end(out = nil)
+ self.<<(out) if out
+ done
+ end
+ end
+end
@@ -0,0 +1,21 @@
+class BasicAsync
+ include Apiary
+
+ version '1.0'
+
+ def initialize(var = nil)
+ @var = var
+ end
+
+ aget
+ def ping
+ EM.add_timer(0.1) do
+ async_response.end 'ping'
+ end
+ end
+
+ aget
+ def var
+ async_response.end @var
+ end
+end
View
@@ -1,6 +1,7 @@
require 'minitest/autorun'
require 'em-http'
require 'test/fixtures/basic'
+require 'test/fixtures/basic_async'
class MiniTest::Unit::TestCase
def run_with(cls, optional_blk = nil, &blk)
View
@@ -0,0 +1,30 @@
+class TestAsync < MiniTest::Unit::TestCase
+ def test_simple
+ run_with(BasicAsync) do
+ request('/1.0/ping') do |http|
+ assert_equal 200, http.response_header.status
+ assert_equal 'ping', http.response
+ done
+ end
+ end
+ end
+
+ def test_not_found
+ run_with(BasicAsync) do
+ request('/1.0/something_else') do |http|
+ assert_equal 404, http.response_header.status
+ done
+ end
+ end
+ end
+
+ def test_custom_initializer
+ run_with(BasicAsync, proc{ BasicAsync.new('hi there')}) do
+ request('/1.0/var') do |http|
+ assert_equal 200, http.response_header.status
+ assert_equal 'hi there', http.response
+ done
+ end
+ end
+ end
+end

0 comments on commit d4ff339

Please sign in to comment.