async_sinatra 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +8 -2
- data/Rakefile +11 -12
- data/examples/basic.ru +9 -0
- data/lib/sinatra/async.rb +66 -29
- data/lib/sinatra/async/test.rb +67 -0
- data/test/borked_test_crohr.rb +161 -0
- data/test/test_async.rb +116 -0
- metadata +53 -30
data/README.rdoc
CHANGED
@@ -7,11 +7,17 @@
|
|
7
7
|
== DESCRIPTION:
|
8
8
|
|
9
9
|
A Sinatra plugin to provide convenience whilst performing asynchronous
|
10
|
-
responses inside of the Sinatra framework running under
|
10
|
+
responses inside of the Sinatra framework running under async webservers.
|
11
11
|
|
12
12
|
To properly utilise this package, some knowledge of EventMachine and/or
|
13
13
|
asynchronous patterns is recommended.
|
14
14
|
|
15
|
+
Currently, supporting servers include:
|
16
|
+
|
17
|
+
* Thin
|
18
|
+
* Rainbows
|
19
|
+
* Zbatery
|
20
|
+
|
15
21
|
== SYNOPSIS:
|
16
22
|
|
17
23
|
A quick example:
|
@@ -36,7 +42,7 @@ See Sinatra::Async for more details.
|
|
36
42
|
== REQUIREMENTS:
|
37
43
|
|
38
44
|
* Sinatra (>= 0.9)
|
39
|
-
*
|
45
|
+
* An async webserver
|
40
46
|
|
41
47
|
== INSTALL:
|
42
48
|
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require 'rake/clean'
|
3
3
|
|
4
|
-
task :default => :
|
4
|
+
task :default => :test
|
5
5
|
|
6
6
|
def spec(file = Dir['*.gemspec'].first)
|
7
7
|
@spec ||=
|
@@ -19,27 +19,25 @@ end
|
|
19
19
|
|
20
20
|
def manifest; @manifest ||= `git ls-files`.split("\n").reject{|s|s=~/\.gemspec$|\.gitignore$/}; end
|
21
21
|
|
22
|
-
require '
|
23
|
-
def gem_task; @gem_task ||=
|
22
|
+
require 'rubygems/package_task'
|
23
|
+
def gem_task; @gem_task ||= Gem::PackageTask.new(spec); end
|
24
24
|
gem_task.define
|
25
25
|
Rake::Task[:clobber].enhance [:clobber_package]
|
26
26
|
|
27
27
|
require 'rake/testtask'
|
28
|
-
Rake::TestTask.new
|
28
|
+
Rake::TestTask.new do |t|
|
29
29
|
t.test_files = spec.test_files
|
30
|
-
t.ruby_opts = ['-rubygems']
|
30
|
+
t.ruby_opts = ['-rubygems']
|
31
31
|
t.warning = true
|
32
32
|
end unless spec.test_files.empty?
|
33
33
|
|
34
|
-
require '
|
35
|
-
|
36
|
-
rdtask = Rake::RDocTask.new do |rd|
|
34
|
+
require 'rdoc/task'
|
35
|
+
rdtask = RDoc::Task.new do |rd|
|
37
36
|
rd.title = spec.name
|
38
37
|
rd.main = spec.extra_rdoc_files.first
|
39
38
|
lib_rexp = spec.require_paths.map { |p| Regexp.escape p }.join('|')
|
40
39
|
rd.rdoc_files.include(*manifest.grep(/^(?:#{lib_rexp})/))
|
41
40
|
rd.rdoc_files.include(*spec.extra_rdoc_files)
|
42
|
-
rd.template = 'darkfish' if df
|
43
41
|
end
|
44
42
|
|
45
43
|
Rake::Task[:clobber].enhance [:clobber_rdoc]
|
@@ -70,7 +68,7 @@ task :gemspec => spec.filename
|
|
70
68
|
|
71
69
|
task spec.filename do
|
72
70
|
spec.files = manifest
|
73
|
-
spec.test_files = manifest.grep(/
|
71
|
+
spec.test_files = manifest.grep(%r{test/test_.*\.rb})
|
74
72
|
open(spec.filename, 'w') { |w| w.write spec.to_ruby }
|
75
73
|
end
|
76
74
|
|
@@ -90,7 +88,8 @@ task :tag do
|
|
90
88
|
end
|
91
89
|
end
|
92
90
|
|
93
|
-
desc "Release #{gem_task.
|
91
|
+
desc "Release #{gem_task.gem_spec.file_name}"
|
94
92
|
task :release => [:tag, :gem, :publish] do |t|
|
95
|
-
sh "rubyforge add_release #{spec.rubyforge_project} #{spec.name} #{spec.version} #{gem_task.package_dir}/#{gem_task.
|
93
|
+
sh "rubyforge add_release #{spec.rubyforge_project} #{spec.name} #{spec.version} #{gem_task.package_dir}/#{gem_task.gem_spec.file_name}"
|
94
|
+
sh "gem push #{gem_task.package_dir}/#{gem_task.gem_spec.file_name}"
|
96
95
|
end
|
data/examples/basic.ru
CHANGED
@@ -18,6 +18,15 @@ class AsyncTest < Sinatra::Base
|
|
18
18
|
raise 'boom'
|
19
19
|
end
|
20
20
|
|
21
|
+
aget '/araise' do
|
22
|
+
EM.add_timer(1) { body { raise "boom" } }
|
23
|
+
end
|
24
|
+
|
25
|
+
# This will blow up in thin currently
|
26
|
+
aget '/raise/die' do
|
27
|
+
EM.add_timer(1) { raise 'die' }
|
28
|
+
end
|
29
|
+
|
21
30
|
end
|
22
31
|
|
23
32
|
run AsyncTest.new
|
data/lib/sinatra/async.rb
CHANGED
@@ -35,6 +35,7 @@ module Sinatra #:nodoc:
|
|
35
35
|
#
|
36
36
|
# end
|
37
37
|
module Async
|
38
|
+
|
38
39
|
# Similar to Sinatra::Base#get, but the block will be scheduled to run
|
39
40
|
# during the next tick of the EventMachine reactor. In the meantime,
|
40
41
|
# Thin will hold onto the client connection, awaiting a call to
|
@@ -58,43 +59,79 @@ module Sinatra #:nodoc:
|
|
58
59
|
|
59
60
|
private
|
60
61
|
def aroute(verb, path, opts = {}, &block) #:nodoc:
|
62
|
+
method = "A#{verb} #{path}".to_sym
|
63
|
+
define_method method, &block
|
64
|
+
|
61
65
|
route(verb, path, opts) do |*bargs|
|
62
|
-
method
|
66
|
+
async_runner(method, *bargs)
|
67
|
+
async_response
|
68
|
+
end
|
69
|
+
end
|
63
70
|
|
64
|
-
|
65
|
-
|
66
|
-
|
71
|
+
module Helpers
|
72
|
+
# Send the given body or block as the final response to the asynchronous
|
73
|
+
# request.
|
74
|
+
def body(*args)
|
75
|
+
if @async_running
|
76
|
+
block_given? ? async_handle_exception { super yield } : super
|
77
|
+
request.env['async.callback'][
|
78
|
+
[response.status, response.headers, response.body]
|
79
|
+
]
|
80
|
+
else
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
67
84
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
# By default async_schedule calls EventMachine#next_tick, if you're using
|
86
|
+
# threads or some other scheduling mechanism, it must take the block
|
87
|
+
# passed here.
|
88
|
+
def async_schedule(&b)
|
89
|
+
if options.environment == :test
|
90
|
+
options.set :async_schedules, [] unless options.respond_to? :async_schedules
|
91
|
+
options.async_schedules << b
|
92
|
+
else
|
93
|
+
EM.next_tick(&b)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Defaults to throw async as that is most commonly used by servers.
|
98
|
+
def async_response
|
99
|
+
throw :async
|
100
|
+
end
|
101
|
+
|
102
|
+
def async_runner(method, *bargs)
|
103
|
+
async_schedule do
|
104
|
+
@async_running = true
|
105
|
+
async_handle_exception do
|
106
|
+
if h = catch(:halt) { __send__(method, *bargs); nil }
|
107
|
+
invoke { halt h }
|
108
|
+
invoke { error_block! response.status }
|
109
|
+
body(response.body)
|
82
110
|
end
|
83
111
|
end
|
84
|
-
|
112
|
+
end
|
113
|
+
end
|
85
114
|
|
86
|
-
|
115
|
+
def async_handle_exception
|
116
|
+
yield
|
117
|
+
rescue ::Exception => boom
|
118
|
+
if options.show_exceptions?
|
119
|
+
printer = Sinatra::ShowExceptions.new(proc{ raise boom })
|
120
|
+
s, h, b = printer.call(request.env)
|
121
|
+
response.status = s
|
122
|
+
response.headers.replace(h)
|
123
|
+
response.body = b
|
124
|
+
else
|
125
|
+
body(handle_exception!(boom))
|
126
|
+
end
|
87
127
|
end
|
88
|
-
end
|
89
128
|
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
[response.status, response.headers, response.body]
|
97
|
-
] if respond_to?(:__async_callback)
|
129
|
+
# Asynchronous halt must be used when the halt is occuring outside of
|
130
|
+
# the original call stack.
|
131
|
+
def ahalt(*args)
|
132
|
+
invoke { halt(*args) }
|
133
|
+
invoke { error_block! response.status }
|
134
|
+
body response.body
|
98
135
|
end
|
99
136
|
end
|
100
137
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'sinatra/async'
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
class Rack::MockResponse
|
5
|
+
def async?
|
6
|
+
self.status == -1
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Sinatra::Async::Test
|
11
|
+
class AsyncSession < Rack::MockSession
|
12
|
+
def request(uri, env)
|
13
|
+
env['async.callback'] = lambda { |r| s,h,b = *r; handle_last_response(uri, env, s,h,b) }
|
14
|
+
env['async.close'] = lambda { raise 'close connection' } # XXX deal with this
|
15
|
+
catch(:async) { super }
|
16
|
+
@last_response ||= Rack::MockResponse.new(-1, {}, [], env["rack.errors"].flush)
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle_last_response(uri, env, status, headers, body)
|
20
|
+
@last_response = Rack::MockResponse.new(status, headers, body, env["rack.errors"].flush)
|
21
|
+
body.close if body.respond_to?(:close)
|
22
|
+
|
23
|
+
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
|
24
|
+
|
25
|
+
@after_request.each { |hook| hook.call }
|
26
|
+
@last_response
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Methods
|
31
|
+
include Rack::Test::Methods
|
32
|
+
|
33
|
+
%w(get put post delete head).each do |m|
|
34
|
+
eval <<-RUBY, binding, __FILE__, __LINE__ + 1
|
35
|
+
def a#{m}(*args)
|
36
|
+
#{m}(*args)
|
37
|
+
assert_async
|
38
|
+
async_continue
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_rack_mock_session # XXX move me
|
44
|
+
Sinatra::Async::Test::AsyncSession.new(app)
|
45
|
+
end
|
46
|
+
|
47
|
+
def assert_async
|
48
|
+
assert last_response.async?
|
49
|
+
end
|
50
|
+
|
51
|
+
def async_continue
|
52
|
+
while b = app.options.async_schedules.shift
|
53
|
+
b.call
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def em_async_continue(timeout = 10)
|
58
|
+
timed = false
|
59
|
+
EM.run do
|
60
|
+
async_continue
|
61
|
+
EM.tick_loop { EM.stop unless last_response.async? }
|
62
|
+
EM.add_timer(timeout) { timed = true; EM.stop }
|
63
|
+
end
|
64
|
+
assert !timed, "asynchronous timeout after #{timeout} seconds"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require "sinatra/async/test"
|
3
|
+
|
4
|
+
require 'spec/autorun'
|
5
|
+
require 'spec/interop/test'
|
6
|
+
|
7
|
+
require 'em-http'
|
8
|
+
|
9
|
+
Spec::Runner.configure { |c| }
|
10
|
+
|
11
|
+
def server(base=Sinatra::Base, &block)
|
12
|
+
s = Sinatra.new(base, &block).new
|
13
|
+
s.options.set :environment, :test
|
14
|
+
s
|
15
|
+
end
|
16
|
+
|
17
|
+
def app
|
18
|
+
@app
|
19
|
+
end
|
20
|
+
|
21
|
+
include Sinatra::Async::Test::Methods
|
22
|
+
|
23
|
+
describe "Asynchronous routes" do
|
24
|
+
it "should still work as usual" do
|
25
|
+
@app = server do
|
26
|
+
register Sinatra::Async
|
27
|
+
disable :raise_errors, :show_exceptions
|
28
|
+
|
29
|
+
aget '/' do
|
30
|
+
body "hello async"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
aget '/'
|
34
|
+
last_response.status.should_equal 200
|
35
|
+
last_response.body.should == "hello async"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should correctly deal with raised exceptions" do
|
39
|
+
@app = server do
|
40
|
+
register Sinatra::Async
|
41
|
+
disable :raise_errors, :show_exceptions
|
42
|
+
aget '/' do
|
43
|
+
raise "boom"
|
44
|
+
body "hello async"
|
45
|
+
end
|
46
|
+
error Exception do
|
47
|
+
e = request.env['sinatra.error']
|
48
|
+
"problem: #{e.class.name} #{e.message}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
aget '/'
|
52
|
+
last_response.status.should == 500
|
53
|
+
last_response.body.should == "problem: RuntimeError boom"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should correctly deal with halts" do
|
57
|
+
@app = server do
|
58
|
+
register Sinatra::Async
|
59
|
+
disable :raise_errors, :show_exceptions
|
60
|
+
aget '/' do
|
61
|
+
halt 406, "Format not supported"
|
62
|
+
body "never called"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
aget '/'
|
67
|
+
last_response.status.should == 406
|
68
|
+
last_response.body.should == "Format not supported"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should correctly deal with halts and pass it to the defined error blocks if any" do
|
72
|
+
@app = server do
|
73
|
+
register Sinatra::Async
|
74
|
+
disable :raise_errors, :show_exceptions
|
75
|
+
aget '/' do
|
76
|
+
halt 406, "Format not supported"
|
77
|
+
body "never called"
|
78
|
+
end
|
79
|
+
error 406 do
|
80
|
+
response['Content-Type'] = "text/plain"
|
81
|
+
"problem: #{response.body.to_s}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
aget '/'
|
85
|
+
last_response.status.should == 406
|
86
|
+
last_response.headers['Content-Type'].should == "text/plain"
|
87
|
+
last_response.body.should == "problem: Format not supported"
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "using EM libraries inside route block" do
|
91
|
+
it "should still work as usual" do
|
92
|
+
@app = server do
|
93
|
+
register Sinatra::Async
|
94
|
+
disable :raise_errors, :show_exceptions
|
95
|
+
aget '/' do
|
96
|
+
url = "http://ruby.activeventure.com/programmingruby/book/tut_exceptions.html"
|
97
|
+
http = EM::HttpRequest.new(url).get
|
98
|
+
http.callback {
|
99
|
+
status http.response_header.status
|
100
|
+
body "ok"
|
101
|
+
}
|
102
|
+
http.errback {
|
103
|
+
body "nok"
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
EM.run { EM.add_timer(1) { EM.stop }; aget '/' }
|
108
|
+
last_response.status.should == 200
|
109
|
+
last_response.body.should == "ok"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should correctly deal with exceptions raised from within EM callbacks" do
|
113
|
+
@app = server do
|
114
|
+
register Sinatra::Async
|
115
|
+
disable :raise_errors, :show_exceptions
|
116
|
+
aget '/' do
|
117
|
+
url = "http://doesnotexist.local/whatever"
|
118
|
+
http = EM::HttpRequest.new(url).get
|
119
|
+
http.callback {
|
120
|
+
status http.response_header.status
|
121
|
+
body "ok"
|
122
|
+
}
|
123
|
+
http.errback {
|
124
|
+
raise "boom"
|
125
|
+
}
|
126
|
+
end
|
127
|
+
error Exception do
|
128
|
+
e = request.env['sinatra.error']
|
129
|
+
"#{e.class.name}: #{e.message}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
EM.run { EM.add_timer(1) { EM.stop }; aget '/' }
|
133
|
+
last_response.status.should == 500
|
134
|
+
last_response.body.should == "RuntimeError: boom"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should correctly deal with halts thrown from within EM callbacks" do
|
138
|
+
@app = server do
|
139
|
+
register Sinatra::Async
|
140
|
+
disable :raise_errors, :show_exceptions
|
141
|
+
aget '/' do
|
142
|
+
url = "http://doesnotexist.local/whatever"
|
143
|
+
http = EM::HttpRequest.new(url).get
|
144
|
+
http.callback {
|
145
|
+
status http.response_header.status
|
146
|
+
body "ok"
|
147
|
+
}
|
148
|
+
http.errback {
|
149
|
+
halt 503, "error: #{http.errors.inspect}"
|
150
|
+
}
|
151
|
+
end
|
152
|
+
error 503 do
|
153
|
+
"503: #{response.body.to_s}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
EM.run { EM.add_timer(1) { EM.stop }; aget '/' }
|
157
|
+
last_response.status.should == 503
|
158
|
+
last_response.body.should == "503: error: \"unable to resolve server address\""
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/test/test_async.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
gem 'test-unit'
|
2
|
+
require "test/unit"
|
3
|
+
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
require "sinatra/async/test"
|
7
|
+
|
8
|
+
class TestSinatraAsync < Test::Unit::TestCase
|
9
|
+
include Sinatra::Async::Test::Methods
|
10
|
+
|
11
|
+
class TestApp < Sinatra::Base
|
12
|
+
set :environment, :test
|
13
|
+
register Sinatra::Async
|
14
|
+
|
15
|
+
error 401 do
|
16
|
+
'401'
|
17
|
+
end
|
18
|
+
|
19
|
+
aget '/hello' do
|
20
|
+
body { 'hello async' }
|
21
|
+
end
|
22
|
+
|
23
|
+
aget '/em' do
|
24
|
+
EM.add_timer(0.001) { body { 'em' }; EM.stop }
|
25
|
+
end
|
26
|
+
|
27
|
+
aget '/em_timeout' do
|
28
|
+
# never send a response
|
29
|
+
end
|
30
|
+
|
31
|
+
aget '/404' do
|
32
|
+
not_found
|
33
|
+
end
|
34
|
+
|
35
|
+
aget '/302' do
|
36
|
+
ahalt 302
|
37
|
+
end
|
38
|
+
|
39
|
+
aget '/em_halt' do
|
40
|
+
EM.next_tick { ahalt 404 }
|
41
|
+
end
|
42
|
+
|
43
|
+
aget '/s401' do
|
44
|
+
halt 401
|
45
|
+
end
|
46
|
+
|
47
|
+
aget '/a401' do
|
48
|
+
ahalt 401
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def app
|
53
|
+
TestApp.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_basic_async_get
|
57
|
+
get '/hello'
|
58
|
+
assert_async
|
59
|
+
async_continue
|
60
|
+
assert last_response.ok?
|
61
|
+
assert_equal 'hello async', last_response.body
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_em_get
|
65
|
+
get '/em'
|
66
|
+
assert_async
|
67
|
+
em_async_continue
|
68
|
+
assert last_response.ok?
|
69
|
+
assert_equal 'em', last_response.body
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_em_async_continue_timeout
|
73
|
+
get '/em_timeout'
|
74
|
+
assert_async
|
75
|
+
assert_raises(Test::Unit::AssertionFailedError) do
|
76
|
+
em_async_continue(0.001)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_404
|
81
|
+
get '/404'
|
82
|
+
assert_async
|
83
|
+
async_continue
|
84
|
+
assert_equal 404, last_response.status
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_302
|
88
|
+
get '/302'
|
89
|
+
assert_async
|
90
|
+
async_continue
|
91
|
+
assert_equal 302, last_response.status
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_em_halt
|
95
|
+
get '/em_halt'
|
96
|
+
assert_async
|
97
|
+
em_async_continue
|
98
|
+
assert_equal 404, last_response.status
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_error_blocks_sync
|
102
|
+
get '/s401'
|
103
|
+
assert_async
|
104
|
+
async_continue
|
105
|
+
assert_equal 401, last_response.status
|
106
|
+
assert_equal '401', last_response.body
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_error_blocks_async
|
110
|
+
get '/a401'
|
111
|
+
assert_async
|
112
|
+
async_continue
|
113
|
+
assert_equal 401, last_response.status
|
114
|
+
assert_equal '401', last_response.body
|
115
|
+
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async_sinatra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- James Tucker
|
@@ -9,50 +15,58 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2009-03-24 00:00:00
|
18
|
+
date: 2009-03-24 00:00:00 -03:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: sinatra
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 57
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 9
|
33
|
+
- 1
|
23
34
|
version: 0.9.1
|
24
|
-
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: thin
|
27
35
|
type: :runtime
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.2.0
|
34
|
-
version:
|
36
|
+
version_requirements: *id001
|
35
37
|
- !ruby/object:Gem::Dependency
|
36
38
|
name: rdoc
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
40
42
|
requirements:
|
41
43
|
- - ">="
|
42
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 29
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 4
|
49
|
+
- 1
|
43
50
|
version: 2.4.1
|
44
|
-
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
45
53
|
- !ruby/object:Gem::Dependency
|
46
54
|
name: rake
|
47
|
-
|
48
|
-
|
49
|
-
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
50
58
|
requirements:
|
51
59
|
- - ">="
|
52
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 57
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
- 8
|
65
|
+
- 3
|
53
66
|
version: 0.8.3
|
54
|
-
|
55
|
-
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
description: Asynchronous response API for Sinatra
|
56
70
|
email: raggi@rubyforge.org
|
57
71
|
executables: []
|
58
72
|
|
@@ -65,6 +79,9 @@ files:
|
|
65
79
|
- Rakefile
|
66
80
|
- examples/basic.ru
|
67
81
|
- lib/sinatra/async.rb
|
82
|
+
- lib/sinatra/async/test.rb
|
83
|
+
- test/borked_test_crohr.rb
|
84
|
+
- test/test_async.rb
|
68
85
|
has_rdoc: true
|
69
86
|
homepage: http://libraggi.rubyforge.org/async_sinatra
|
70
87
|
licenses: []
|
@@ -80,23 +97,29 @@ rdoc_options:
|
|
80
97
|
require_paths:
|
81
98
|
- lib
|
82
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
83
101
|
requirements:
|
84
102
|
- - ">="
|
85
103
|
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
86
107
|
version: "0"
|
87
|
-
version:
|
88
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
89
110
|
requirements:
|
90
111
|
- - ">="
|
91
112
|
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
92
116
|
version: "0"
|
93
|
-
version:
|
94
117
|
requirements: []
|
95
118
|
|
96
119
|
rubyforge_project: libraggi
|
97
|
-
rubygems_version: 1.3.
|
120
|
+
rubygems_version: 1.3.7
|
98
121
|
signing_key:
|
99
122
|
specification_version: 2
|
100
|
-
summary: Asynchronous response API for Sinatra
|
101
|
-
test_files:
|
102
|
-
|
123
|
+
summary: Asynchronous response API for Sinatra
|
124
|
+
test_files:
|
125
|
+
- test/test_async.rb
|