cramp 0.12 → 0.13
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/lib/cramp.rb +10 -1
- data/lib/cramp/abstract.rb +19 -7
- data/lib/cramp/action.rb +50 -2
- data/lib/cramp/callbacks.rb +9 -3
- data/lib/cramp/fiber_pool.rb +34 -0
- data/lib/cramp/keep_connection_alive.rb +1 -1
- data/lib/cramp/long_polling.rb +6 -0
- data/lib/cramp/periodic_timer.rb +8 -4
- data/lib/cramp/sse.rb +5 -0
- data/lib/cramp/test_case.rb +6 -2
- data/lib/cramp/websocket.rb +9 -45
- data/lib/cramp/websocket/extension.rb +82 -0
- data/lib/cramp/websocket/thin_backend.rb +11 -0
- data/lib/vendor/fiber_pool.rb +85 -0
- metadata +13 -8
data/lib/cramp.rb
CHANGED
@@ -2,6 +2,7 @@ require 'eventmachine'
|
|
2
2
|
EM.epoll
|
3
3
|
|
4
4
|
require 'active_support'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
5
6
|
require 'active_support/core_ext/class/inheritable_attributes'
|
6
7
|
require 'active_support/core_ext/class/attribute_accessors'
|
7
8
|
require 'active_support/core_ext/module/aliasing'
|
@@ -13,17 +14,25 @@ require 'active_support/buffered_logger'
|
|
13
14
|
|
14
15
|
require 'rack'
|
15
16
|
|
17
|
+
if RUBY_VERSION >= '1.9.1'
|
18
|
+
require File.join(File.dirname(__FILE__), 'vendor/fiber_pool')
|
19
|
+
end
|
20
|
+
|
16
21
|
module Cramp
|
17
|
-
VERSION = '0.
|
22
|
+
VERSION = '0.13'
|
18
23
|
|
19
24
|
mattr_accessor :logger
|
20
25
|
|
21
26
|
autoload :Action, "cramp/action"
|
22
27
|
autoload :Websocket, "cramp/websocket"
|
28
|
+
autoload :WebsocketExtension, "cramp/websocket/extension"
|
29
|
+
autoload :SSE, "cramp/sse"
|
30
|
+
autoload :LongPolling, "cramp/long_polling"
|
23
31
|
autoload :Body, "cramp/body"
|
24
32
|
autoload :PeriodicTimer, "cramp/periodic_timer"
|
25
33
|
autoload :KeepConnectionAlive, "cramp/keep_connection_alive"
|
26
34
|
autoload :Abstract, "cramp/abstract"
|
27
35
|
autoload :Callbacks, "cramp/callbacks"
|
36
|
+
autoload :FiberPool, "cramp/fiber_pool"
|
28
37
|
autoload :TestCase, "cramp/test_case"
|
29
38
|
end
|
data/lib/cramp/abstract.rb
CHANGED
@@ -2,10 +2,11 @@ require 'active_support/core_ext/hash/keys'
|
|
2
2
|
|
3
3
|
module Cramp
|
4
4
|
class Abstract
|
5
|
-
|
6
5
|
include Callbacks
|
6
|
+
include FiberPool
|
7
7
|
|
8
|
-
|
8
|
+
class_attribute :transport
|
9
|
+
self.transport = :regular
|
9
10
|
|
10
11
|
class << self
|
11
12
|
def call(env)
|
@@ -15,20 +16,22 @@ module Cramp
|
|
15
16
|
|
16
17
|
def initialize(env)
|
17
18
|
@env = env
|
19
|
+
@finished = false
|
18
20
|
end
|
19
21
|
|
20
22
|
def process
|
21
23
|
EM.next_tick { before_start }
|
22
|
-
|
24
|
+
throw :async
|
23
25
|
end
|
24
26
|
|
27
|
+
protected
|
28
|
+
|
25
29
|
def continue
|
26
30
|
init_async_body
|
27
31
|
|
28
32
|
status, headers = respond_with
|
29
33
|
send_initial_response(status, headers, @body)
|
30
34
|
|
31
|
-
EM.next_tick { start } if respond_to?(:start)
|
32
35
|
EM.next_tick { on_start }
|
33
36
|
end
|
34
37
|
|
@@ -45,16 +48,25 @@ module Cramp
|
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
51
|
+
def finished?
|
52
|
+
!!@finished
|
53
|
+
end
|
54
|
+
|
48
55
|
def finish
|
56
|
+
@finished = true
|
49
57
|
@body.succeed
|
50
58
|
end
|
51
59
|
|
52
60
|
def send_initial_response(response_status, response_headers, response_body)
|
53
|
-
|
61
|
+
send_response(response_status, response_headers, response_body)
|
54
62
|
end
|
55
63
|
|
56
64
|
def halt(status, headers = {}, halt_body = '')
|
57
|
-
|
65
|
+
send_response(status, headers, halt_body)
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_response(response_status, response_headers, response_body)
|
69
|
+
@env['async.callback'].call [response_status, response_headers, response_body]
|
58
70
|
end
|
59
71
|
|
60
72
|
def request
|
@@ -66,7 +78,7 @@ module Cramp
|
|
66
78
|
end
|
67
79
|
|
68
80
|
def route_params
|
69
|
-
@env['router.params']
|
81
|
+
@env['router.params'] || @env['usher.params']
|
70
82
|
end
|
71
83
|
end
|
72
84
|
end
|
data/lib/cramp/action.rb
CHANGED
@@ -1,12 +1,60 @@
|
|
1
1
|
module Cramp
|
2
2
|
class Action < Abstract
|
3
|
-
|
4
3
|
include PeriodicTimer
|
5
4
|
include KeepConnectionAlive
|
6
5
|
|
7
|
-
|
6
|
+
protected
|
7
|
+
|
8
|
+
def render(body, *args)
|
9
|
+
send(:"render_#{transport}", body, *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_initial_response(*)
|
13
|
+
case transport
|
14
|
+
when :long_polling
|
15
|
+
# Dont send no initial response
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_with
|
22
|
+
case transport
|
23
|
+
when :sse
|
24
|
+
[200, {'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive'}]
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_regular(body, *)
|
8
31
|
@body.call(body)
|
9
32
|
end
|
10
33
|
|
34
|
+
def render_long_polling(data, *)
|
35
|
+
status, headers = respond_with
|
36
|
+
headers['Content-Length'] = data.size.to_s
|
37
|
+
|
38
|
+
send_response(status, headers, @body)
|
39
|
+
@body.call(data)
|
40
|
+
|
41
|
+
finish
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_sse(data, options = {})
|
45
|
+
result = "id: #{sse_event_id}\n"
|
46
|
+
result << "retry: #{options[:retry]}\n" if options[:retry]
|
47
|
+
|
48
|
+
data.split(/\n/).each {|d| result << "data: #{d}\n" }
|
49
|
+
result << "\n"
|
50
|
+
|
51
|
+
@body.call(result)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Used by SSE
|
55
|
+
def sse_event_id
|
56
|
+
@sse_event_id ||= Time.now.to_i
|
57
|
+
end
|
58
|
+
|
11
59
|
end
|
12
60
|
end
|
data/lib/cramp/callbacks.rb
CHANGED
@@ -26,23 +26,29 @@ module Cramp
|
|
26
26
|
|
27
27
|
def before_start(n = 0)
|
28
28
|
if callback = self.class.before_start_callbacks[n]
|
29
|
-
|
29
|
+
callback_wrapper { send(callback) { before_start(n+1) } }
|
30
30
|
else
|
31
31
|
continue
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def on_start
|
36
|
+
callback_wrapper { start } if respond_to?(:start)
|
37
|
+
|
36
38
|
self.class.on_start_callback.each do |callback|
|
37
|
-
|
39
|
+
callback_wrapper { send(callback) unless @finished }
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
def on_finish
|
42
44
|
self.class.on_finish_callbacks.each do |callback|
|
43
|
-
|
45
|
+
callback_wrapper { send(callback) }
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
49
|
+
def callback_wrapper
|
50
|
+
EM.next_tick { yield }
|
51
|
+
end
|
52
|
+
|
47
53
|
end
|
48
54
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Cramp
|
2
|
+
module FiberPool
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :fiber_pool
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def use_fiber_pool(options = {})
|
11
|
+
if RUBY_VERSION < '1.9.1'
|
12
|
+
raise "Fibers are supported only for Rubies >= 1.9.1"
|
13
|
+
end
|
14
|
+
|
15
|
+
self.fiber_pool = ::FiberPool.new(options[:size] || 100)
|
16
|
+
yield self.fiber_pool if block_given?
|
17
|
+
include UsesFiberPool
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module UsesFiberPool
|
22
|
+
# Overrides wrapper methods to run callbacks in a fiber
|
23
|
+
|
24
|
+
def callback_wrapper
|
25
|
+
self.fiber_pool.spawn { yield }
|
26
|
+
end
|
27
|
+
|
28
|
+
def timer_method_wrapper(method)
|
29
|
+
self.fiber_pool.spawn { send(method) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/cramp/periodic_timer.rb
CHANGED
@@ -19,9 +19,11 @@ module Cramp
|
|
19
19
|
@timers = []
|
20
20
|
end
|
21
21
|
|
22
|
+
protected
|
23
|
+
|
22
24
|
def continue
|
23
25
|
super
|
24
|
-
|
26
|
+
start_periodic_timers
|
25
27
|
end
|
26
28
|
|
27
29
|
def init_async_body
|
@@ -33,11 +35,9 @@ module Cramp
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
private
|
37
|
-
|
38
38
|
def start_periodic_timers
|
39
39
|
self.class.periodic_timers.each do |method, options|
|
40
|
-
@timers << EventMachine::PeriodicTimer.new(options[:every] || 1) {
|
40
|
+
@timers << EventMachine::PeriodicTimer.new(options[:every] || 1) { timer_method_wrapper(method) unless @finished }
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -45,5 +45,9 @@ module Cramp
|
|
45
45
|
@timers.each {|t| t.cancel }
|
46
46
|
end
|
47
47
|
|
48
|
+
def timer_method_wrapper(method)
|
49
|
+
send(method)
|
50
|
+
end
|
51
|
+
|
48
52
|
end
|
49
53
|
end
|
data/lib/cramp/sse.rb
ADDED
data/lib/cramp/test_case.rb
CHANGED
@@ -14,7 +14,9 @@ module Cramp
|
|
14
14
|
callback = options.delete(:callback) || block
|
15
15
|
headers = headers.merge('async.callback' => callback)
|
16
16
|
|
17
|
-
EM.run
|
17
|
+
EM.run do
|
18
|
+
catch(:async) { @request.get(path, headers) }
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
def get_body(path, options = {}, headers = {}, &block)
|
@@ -22,7 +24,9 @@ module Cramp
|
|
22
24
|
response_callback = proc {|response| response[-1].each {|chunk| callback.call(chunk) } }
|
23
25
|
headers = headers.merge('async.callback' => response_callback)
|
24
26
|
|
25
|
-
EM.run
|
27
|
+
EM.run do
|
28
|
+
catch(:async) { @request.get(path, headers) }
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
def get_body_chunks(path, options = {}, headers = {}, &block)
|
data/lib/cramp/websocket.rb
CHANGED
@@ -1,47 +1,4 @@
|
|
1
1
|
module Cramp
|
2
|
-
module WebsocketExtension
|
3
|
-
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
|
4
|
-
|
5
|
-
def websocket?
|
6
|
-
@env['HTTP_CONNECTION'] == 'Upgrade' && @env['HTTP_UPGRADE'] == 'WebSocket'
|
7
|
-
end
|
8
|
-
|
9
|
-
def websocket_upgrade_data
|
10
|
-
location = "ws://#{@env['HTTP_HOST']}#{@env['REQUEST_PATH']}"
|
11
|
-
challenge = solve_challange(
|
12
|
-
@env['HTTP_SEC_WEBSOCKET_KEY1'],
|
13
|
-
@env['HTTP_SEC_WEBSOCKET_KEY2'],
|
14
|
-
@env['rack.input'].read
|
15
|
-
)
|
16
|
-
|
17
|
-
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
18
|
-
upgrade << "Upgrade: WebSocket\r\n"
|
19
|
-
upgrade << "Connection: Upgrade\r\n"
|
20
|
-
upgrade << "Sec-WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
|
21
|
-
upgrade << "Sec-WebSocket-Location: #{location}\r\n\r\n"
|
22
|
-
upgrade << challenge
|
23
|
-
|
24
|
-
upgrade
|
25
|
-
end
|
26
|
-
|
27
|
-
def solve_challange(first, second, third)
|
28
|
-
# Refer to 5.2 4-9 of the draft 76
|
29
|
-
sum =
|
30
|
-
[extract_nums(first) / count_spaces(first)].pack("N*") +
|
31
|
-
[extract_nums(second) / count_spaces(second)].pack("N*") +
|
32
|
-
third
|
33
|
-
Digest::MD5.digest(sum)
|
34
|
-
end
|
35
|
-
|
36
|
-
def extract_nums(string)
|
37
|
-
string.scan(/[0-9]/).join.to_i
|
38
|
-
end
|
39
|
-
|
40
|
-
def count_spaces(string)
|
41
|
-
string.scan(/ /).size
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
2
|
class Websocket < Abstract
|
46
3
|
include PeriodicTimer
|
47
4
|
|
@@ -68,7 +25,8 @@ module Cramp
|
|
68
25
|
end
|
69
26
|
|
70
27
|
def render(body)
|
71
|
-
|
28
|
+
data = ["\x00", body, "\xFF"].map(&method(:encode)) * ''
|
29
|
+
@body.call(data)
|
72
30
|
end
|
73
31
|
|
74
32
|
def _on_data_receive(data)
|
@@ -79,6 +37,12 @@ module Cramp
|
|
79
37
|
end
|
80
38
|
end
|
81
39
|
end
|
82
|
-
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def encode(string, encoding = 'UTF-8')
|
44
|
+
string.respond_to?(:force_encoding) ? string.force_encoding(encoding) : string
|
45
|
+
end
|
46
|
+
|
83
47
|
end
|
84
48
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Cramp
|
2
|
+
module WebsocketExtension
|
3
|
+
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
|
4
|
+
|
5
|
+
def websocket?
|
6
|
+
@env['HTTP_CONNECTION'] == 'Upgrade' && @env['HTTP_UPGRADE'] == 'WebSocket'
|
7
|
+
end
|
8
|
+
|
9
|
+
def secure_websocket?
|
10
|
+
if @env.has_key?('HTTP_X_FORWARDED_PROTO')
|
11
|
+
@env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
12
|
+
else
|
13
|
+
@env['HTTP_ORIGIN'] =~ /^https:/i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def websocket_url
|
18
|
+
scheme = secure_websocket? ? 'wss:' : 'ws:'
|
19
|
+
@env['websocket.url'] = "#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
|
20
|
+
end
|
21
|
+
|
22
|
+
class WebSocketHandler
|
23
|
+
def initialize(env, websocket_url, body)
|
24
|
+
@env = env
|
25
|
+
@websocket_url = websocket_url
|
26
|
+
@body = body
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Protocol75 < WebSocketHandler
|
31
|
+
def handshake
|
32
|
+
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
33
|
+
upgrade << "Upgrade: WebSocket\r\n"
|
34
|
+
upgrade << "Connection: Upgrade\r\n"
|
35
|
+
upgrade << "WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
|
36
|
+
upgrade << "WebSocket-Location: #{@websocket_url}\r\n\r\n"
|
37
|
+
upgrade
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Protocol76 < WebSocketHandler
|
42
|
+
def handshake
|
43
|
+
key1 = @env['HTTP_SEC_WEBSOCKET_KEY1']
|
44
|
+
value1 = number_from_key(key1) / spaces_in_key(key1)
|
45
|
+
|
46
|
+
key2 = @env['HTTP_SEC_WEBSOCKET_KEY2']
|
47
|
+
value2 = number_from_key(key2) / spaces_in_key(key2)
|
48
|
+
|
49
|
+
hash = Digest::MD5.digest(big_endian(value1) +
|
50
|
+
big_endian(value2) +
|
51
|
+
@body)
|
52
|
+
|
53
|
+
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
54
|
+
upgrade << "Upgrade: WebSocket\r\n"
|
55
|
+
upgrade << "Connection: Upgrade\r\n"
|
56
|
+
upgrade << "Sec-WebSocket-Origin: #{@env['HTTP_ORIGIN']}\r\n"
|
57
|
+
upgrade << "Sec-WebSocket-Location: #{@websocket_url}\r\n\r\n"
|
58
|
+
upgrade << hash
|
59
|
+
upgrade
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def number_from_key(key)
|
65
|
+
key.scan(/[0-9]/).join('').to_i(10)
|
66
|
+
end
|
67
|
+
|
68
|
+
def spaces_in_key(key)
|
69
|
+
key.scan(/ /).size
|
70
|
+
end
|
71
|
+
|
72
|
+
def big_endian(number)
|
73
|
+
string = ''
|
74
|
+
[24,16,8,0].each do |offset|
|
75
|
+
string << (number >> offset & 0xFF).chr
|
76
|
+
end
|
77
|
+
string
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -31,6 +31,17 @@ end
|
|
31
31
|
|
32
32
|
class Thin::Request
|
33
33
|
include Cramp::WebsocketExtension
|
34
|
+
|
35
|
+
def websocket_upgrade_data
|
36
|
+
handler = if @env['HTTP_SEC_WEBSOCKET_KEY1'] and @env['HTTP_SEC_WEBSOCKET_KEY2']
|
37
|
+
Protocol76
|
38
|
+
else
|
39
|
+
Protocol75
|
40
|
+
end
|
41
|
+
|
42
|
+
handler.new(@env, websocket_url, body.read).handshake
|
43
|
+
end
|
44
|
+
|
34
45
|
end
|
35
46
|
|
36
47
|
class Thin::Response
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
2
|
+
# Copyright:: Copyright (c) 2008 eSpace, Inc.
|
3
|
+
# License:: Distributes under the same terms as Ruby
|
4
|
+
|
5
|
+
require 'fiber'
|
6
|
+
|
7
|
+
class Fiber
|
8
|
+
|
9
|
+
#Attribute Reference--Returns the value of a fiber-local variable, using
|
10
|
+
#either a symbol or a string name. If the specified variable does not exist,
|
11
|
+
#returns nil.
|
12
|
+
def [](key)
|
13
|
+
local_fiber_variables[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
#Attribute Assignment--Sets or creates the value of a fiber-local variable,
|
17
|
+
#using either a symbol or a string. See also Fiber#[].
|
18
|
+
def []=(key,value)
|
19
|
+
local_fiber_variables[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def local_fiber_variables
|
25
|
+
@local_fiber_variables ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FiberPool
|
30
|
+
|
31
|
+
# gives access to the currently free fibers
|
32
|
+
attr_reader :fibers
|
33
|
+
attr_reader :busy_fibers
|
34
|
+
|
35
|
+
# Code can register a proc with this FiberPool to be called
|
36
|
+
# every time a Fiber is finished. Good for releasing resources
|
37
|
+
# like ActiveRecord database connections.
|
38
|
+
attr_accessor :generic_callbacks
|
39
|
+
|
40
|
+
# Prepare a list of fibers that are able to run different blocks of code
|
41
|
+
# every time. Once a fiber is done with its block, it attempts to fetch
|
42
|
+
# another one from the queue
|
43
|
+
def initialize(count = 100)
|
44
|
+
@fibers,@busy_fibers,@queue,@generic_callbacks = [],{},[],[]
|
45
|
+
count.times do |i|
|
46
|
+
fiber = Fiber.new do |block|
|
47
|
+
loop do
|
48
|
+
block.call
|
49
|
+
|
50
|
+
# callbacks are called in a reverse order, much like c++ destructor
|
51
|
+
Fiber.current[:callbacks].pop.call while Fiber.current[:callbacks].length > 0
|
52
|
+
generic_callbacks.each do |cb|
|
53
|
+
cb.call
|
54
|
+
end
|
55
|
+
|
56
|
+
if @queue.any?
|
57
|
+
block = @queue.shift
|
58
|
+
else
|
59
|
+
@busy_fibers.delete(Fiber.current.object_id)
|
60
|
+
@fibers.unshift Fiber.current
|
61
|
+
block = Fiber.yield
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
fiber[:callbacks] = []
|
67
|
+
fiber[:em_keys] = []
|
68
|
+
@fibers << fiber
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# If there is an available fiber use it, otherwise, leave it to linger
|
73
|
+
# in a queue
|
74
|
+
def spawn(&block)
|
75
|
+
if fiber = @fibers.shift
|
76
|
+
fiber[:callbacks] = []
|
77
|
+
@busy_fibers[fiber.object_id] = fiber
|
78
|
+
fiber.resume(block)
|
79
|
+
else
|
80
|
+
@queue << block
|
81
|
+
end
|
82
|
+
self # we are keen on hiding our queue
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cramp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 13
|
9
|
+
version: "0.13"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Pratik Naik
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-07-31 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -25,12 +25,12 @@ dependencies:
|
|
25
25
|
requirements:
|
26
26
|
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
28
|
+
hash: 21
|
29
29
|
segments:
|
30
30
|
- 3
|
31
31
|
- 0
|
32
|
-
-
|
33
|
-
version: 3.0.
|
32
|
+
- 9
|
33
|
+
version: 3.0.9
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -79,14 +79,19 @@ files:
|
|
79
79
|
- lib/cramp/action.rb
|
80
80
|
- lib/cramp/body.rb
|
81
81
|
- lib/cramp/callbacks.rb
|
82
|
+
- lib/cramp/fiber_pool.rb
|
82
83
|
- lib/cramp/keep_connection_alive.rb
|
84
|
+
- lib/cramp/long_polling.rb
|
83
85
|
- lib/cramp/periodic_timer.rb
|
84
86
|
- lib/cramp/rendering.rb
|
87
|
+
- lib/cramp/sse.rb
|
85
88
|
- lib/cramp/test_case.rb
|
89
|
+
- lib/cramp/websocket/extension.rb
|
86
90
|
- lib/cramp/websocket/rainbows_backend.rb
|
87
91
|
- lib/cramp/websocket/thin_backend.rb
|
88
92
|
- lib/cramp/websocket.rb
|
89
93
|
- lib/cramp.rb
|
94
|
+
- lib/vendor/fiber_pool.rb
|
90
95
|
has_rdoc: true
|
91
96
|
homepage: http://m.onkey.org
|
92
97
|
licenses: []
|
@@ -117,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
122
|
requirements: []
|
118
123
|
|
119
124
|
rubyforge_project:
|
120
|
-
rubygems_version: 1.
|
125
|
+
rubygems_version: 1.6.2
|
121
126
|
signing_key:
|
122
127
|
specification_version: 3
|
123
128
|
summary: Asynchronous web framework.
|