thin_async 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +25 -0
- data/example/simple.ru +19 -0
- data/lib/thin/async.rb +72 -0
- data/thin_async.gemspec +13 -0
- metadata +68 -0
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Thin Asynchronous Response API
|
2
|
+
A nice wrapper around Thin's obscure async callback used to send response body asynchronously.
|
3
|
+
Which means you can send the response in chunks while allowing Thin to process other requests.
|
4
|
+
|
5
|
+
Crazy delicious with em-http-request for file upload, image processing, proxying, etc.
|
6
|
+
|
7
|
+
## _WARNING_
|
8
|
+
You should not use long blocking operations (Net::HTTP or slow shell calls) with this as it
|
9
|
+
will prevent the EventMachine event loop from running and block all other requests.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
Inside your Rack app #call(env):
|
13
|
+
|
14
|
+
Thin::AsyncResponse.new(env) do |response|
|
15
|
+
response << "this is ... "
|
16
|
+
EM.add_timer(1) do
|
17
|
+
# This will be sent to the client 1 sec later without blocking other requests.
|
18
|
+
response << "async!"
|
19
|
+
response.done
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
See example/ dir for more.
|
24
|
+
|
25
|
+
(c) macournoyer
|
data/example/simple.ru
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/thin/async"
|
2
|
+
|
3
|
+
class Simple
|
4
|
+
def call(env)
|
5
|
+
response = Thin::AsyncResponse.new(env)
|
6
|
+
|
7
|
+
response << " " * 1024
|
8
|
+
|
9
|
+
response << "this is ... "
|
10
|
+
EM.add_timer(1) do
|
11
|
+
response << "async stuff!"
|
12
|
+
response.done
|
13
|
+
end
|
14
|
+
|
15
|
+
response.finish
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
run Simple.new
|
data/lib/thin/async.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module Thin
|
2
|
+
unless defined?(DeferrableBody)
|
3
|
+
# Based on version from James Tucker <raggi@rubyforge.org>
|
4
|
+
class DeferrableBody
|
5
|
+
include EM::Deferrable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@queue = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(body)
|
12
|
+
@queue << body
|
13
|
+
schedule_dequeue
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&blk)
|
17
|
+
@body_callback = blk
|
18
|
+
schedule_dequeue
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def schedule_dequeue
|
23
|
+
return unless @body_callback
|
24
|
+
EM.next_tick do
|
25
|
+
next unless body = @queue.shift
|
26
|
+
body.each do |chunk|
|
27
|
+
@body_callback.call(chunk)
|
28
|
+
end
|
29
|
+
schedule_dequeue unless @queue.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Response whos body is sent asynchronously.
|
36
|
+
class AsyncResponse
|
37
|
+
include Rack::Response::Helpers
|
38
|
+
|
39
|
+
Marker = [-1, {}, []].freeze
|
40
|
+
|
41
|
+
attr_reader :headers
|
42
|
+
attr_accessor :status
|
43
|
+
|
44
|
+
def initialize(env)
|
45
|
+
@callback = env['async.callback']
|
46
|
+
@body = DeferrableBody.new
|
47
|
+
@status = 200
|
48
|
+
@headers = {}
|
49
|
+
|
50
|
+
if block_given?
|
51
|
+
yield self
|
52
|
+
finish
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def write(body)
|
57
|
+
@body.call(body.respond_to?(:each) ? body : [body])
|
58
|
+
end
|
59
|
+
alias :<< :write
|
60
|
+
|
61
|
+
def done
|
62
|
+
EM.next_tick do
|
63
|
+
@body.succeed
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def finish
|
68
|
+
@callback.call [@status, @headers, @body]
|
69
|
+
Marker
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/thin_async.gemspec
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "thin_async"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "A nice wrapper to send response body asynchronously with Thin"
|
5
|
+
|
6
|
+
s.author = "Marc-Andre Cournoyer"
|
7
|
+
s.email = "macournoyer@gmail.com"
|
8
|
+
s.files = Dir["**/*"]
|
9
|
+
s.homepage = "http://github.com/macournoyer/thin_async"
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
|
12
|
+
s.add_dependency "thin", ">= 1.2.1"
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thin_async
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marc-Andre Cournoyer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-13 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thin
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.1
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: macournoyer@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- example/simple.ru
|
35
|
+
- lib/thin/async.rb
|
36
|
+
- README.md
|
37
|
+
- thin_async-0.0.1.gem
|
38
|
+
- thin_async.gemspec
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/macournoyer/thin_async
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: A nice wrapper to send response body asynchronously with Thin
|
67
|
+
test_files: []
|
68
|
+
|