firehose 0.2.alpha.5 → 0.2.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- data/firehose.gemspec +3 -2
- data/lib/assets/javascripts/firehose/consumer.js.coffee +21 -20
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +95 -56
- data/lib/assets/javascripts/firehose/transport.js.coffee +7 -9
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +34 -11
- data/lib/firehose/rack/consumer_app.rb +9 -6
- data/lib/firehose/rack.rb +1 -1
- data/lib/firehose/version.rb +2 -2
- data/spec/integrations/shared_examples.rb +1 -1
- metadata +52 -36
data/firehose.gemspec
CHANGED
@@ -5,8 +5,8 @@ require "firehose/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "firehose"
|
7
7
|
s.version = Firehose::VERSION
|
8
|
-
s.authors = ["Brad Gessler", "Steel Fu", "Paul Cortens"]
|
9
|
-
s.email = ["brad@polleverywhere.com", "steel@polleverywhere.com", "paul@polleverywhere.com"]
|
8
|
+
s.authors = ["Brad Gessler", "Steel Fu", "Paul Cortens", "Zach Zolton"]
|
9
|
+
s.email = ["brad@polleverywhere.com", "steel@polleverywhere.com", "paul@polleverywhere.com", "zach@polleverywhere.com"]
|
10
10
|
s.homepage = "http://firehose.io/"
|
11
11
|
s.summary = %q{Build realtime Ruby web applications}
|
12
12
|
s.description = %q{Firehose is a realtime web application toolkit for building realtime Ruby web applications.}
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_runtime_dependency "faraday"
|
26
26
|
s.add_runtime_dependency "faye-websocket"
|
27
27
|
s.add_runtime_dependency "em-http-request", "~> 1.0.0"
|
28
|
+
s.add_runtime_dependency "json"
|
28
29
|
|
29
30
|
s.add_development_dependency "rspec"
|
30
31
|
s.add_development_dependency "webmock"
|
@@ -5,6 +5,10 @@ class Firehose.Consumer
|
|
5
5
|
constructor: (config = {}) ->
|
6
6
|
# List of transport stragies we have to use.
|
7
7
|
config.transports ||= Firehose.Consumer.transports
|
8
|
+
unless config.transports.length > 0
|
9
|
+
throw 'You must provide at least one tranport for Firehose.Consumer'
|
10
|
+
unless typeof config.uri is 'string'
|
11
|
+
throw 'You must provide a Firehose server URI for Firehose.Consumer'
|
8
12
|
# Empty handler for messages.
|
9
13
|
config.message ||= ->
|
10
14
|
# Empty handler for error handling.
|
@@ -13,41 +17,38 @@ class Firehose.Consumer
|
|
13
17
|
config.connected ||= ->
|
14
18
|
# Empty handler for when we're disconnected.
|
15
19
|
config.disconnected ||= ->
|
16
|
-
# The initial connection failed. This is probably triggered when a
|
17
|
-
# is supported by the browser, but for whatever
|
20
|
+
# The initial connection failed. This is probably triggered when a
|
21
|
+
# transport, like WebSockets is supported by the browser, but for whatever
|
22
|
+
# reason it can't connect (probably a firewall)
|
18
23
|
config.failed ||= ->
|
19
24
|
throw "Could not connect"
|
20
|
-
# URL that we'll connect to.
|
21
|
-
config.uri ||= undefined
|
22
25
|
# Params that we'll tack on to the URL.
|
23
|
-
config.params ||= {
|
26
|
+
config.params ||= {}
|
24
27
|
# Do stuff before we send the message into config.message. The sensible
|
25
28
|
# default on the webs is to parse JSON.
|
26
|
-
config.parse
|
27
|
-
JSON.parse(body)
|
28
|
-
|
29
|
+
config.parse ||= JSON.parse
|
29
30
|
# Hang on to these config for when we connect.
|
30
31
|
@config = config
|
31
32
|
# Make sure we return ourself out of the constructor so we can chain.
|
32
33
|
this
|
33
34
|
|
34
|
-
connect: =>
|
35
|
+
connect: (delay=0) =>
|
35
36
|
# Get a list of transports that the browser supports
|
36
|
-
supportedTransports = (
|
37
|
+
supportedTransports = (t for t in @config.transports when t.supported())
|
37
38
|
# Mmmkay, we've got transports supported by the browser, now lets try connecting
|
38
39
|
# to them and dealing with failed connections that might be caused by firewalls,
|
39
40
|
# or other network connectivity issues.
|
40
41
|
transports = for transport in supportedTransports
|
41
|
-
|
42
|
-
config = @config
|
42
|
+
originalFailFun = @config.failed
|
43
43
|
# Map the next transport into the existing transports connectionError
|
44
44
|
# If the connection fails, try the next transport supported by the browser.
|
45
|
-
config.failed = =>
|
46
|
-
# Map the next transport to connect
|
47
|
-
if
|
48
|
-
new
|
49
|
-
|
50
|
-
|
51
|
-
new transport(config)
|
45
|
+
@config.failed = =>
|
46
|
+
# Map the next transport to connect inside of the current transport failures
|
47
|
+
if nextTransportType = supportedTransports.pop()
|
48
|
+
nextTransport = new nextTransportType @config
|
49
|
+
nextTransport.connect delay
|
50
|
+
else originalFailFun?()
|
51
|
+
new transport(@config)
|
52
52
|
# Fire off the first connection attempt.
|
53
|
-
|
53
|
+
[firstTransport] = transports
|
54
|
+
firstTransport.connect delay
|
@@ -1,32 +1,33 @@
|
|
1
1
|
class Firehose.LongPoll extends Firehose.Transport
|
2
|
-
messageSequenceHeader: '
|
2
|
+
messageSequenceHeader: 'Pragma'
|
3
|
+
name: -> 'LongPoll'
|
3
4
|
|
4
|
-
# CORS is supported in
|
5
|
-
|
6
|
-
|
5
|
+
# CORS is kinda supported in IE8+ except that its implementation cannot
|
6
|
+
# access "simple request" response headers. This means we don't yet have a
|
7
|
+
# plan to support IE<10 (when it gets a real XHR2 implementation). Sucks...
|
8
|
+
@ieSupported: ->
|
9
|
+
$.browser.msie and parseInt($.browser.version) >= 10
|
7
10
|
|
8
|
-
@supported:
|
11
|
+
@supported: ->
|
9
12
|
# IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
|
10
13
|
if xhr = $.ajaxSettings.xhr()
|
11
14
|
"withCredentials" of xhr || Firehose.LongPoll.ieSupported()
|
12
15
|
|
13
16
|
constructor: (args) ->
|
14
17
|
super args
|
15
|
-
|
16
18
|
# Configrations specifically for web sockets
|
17
|
-
@config.longPoll
|
19
|
+
@config.longPoll ||= {}
|
18
20
|
# Protocol schema we should use for talking to WS server.
|
19
|
-
@config.longPoll.url
|
21
|
+
@config.longPoll.url ||= "http:#{@config.uri}"
|
20
22
|
# How many ms should we wait before timing out the AJAX connection?
|
21
23
|
@config.longPoll.timeout ||= 25000
|
22
|
-
|
23
24
|
# TODO - What is @_lagTime for? Can't we just use the @_timeout value?
|
24
25
|
# We use the lag time to make the client live longer than the server.
|
25
|
-
@_lagTime
|
26
|
-
@_timeout
|
27
|
-
@_okInterval
|
28
|
-
|
29
|
-
@
|
26
|
+
@_lagTime = 5000
|
27
|
+
@_timeout = @config.longPoll.timeout + @_lagTime
|
28
|
+
@_okInterval = 0
|
29
|
+
@_isConnected = false
|
30
|
+
@_stopRequestLoop = false
|
30
31
|
|
31
32
|
connect: (delay = 0) =>
|
32
33
|
unless @_isConnected
|
@@ -40,53 +41,35 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
40
41
|
# set any HTTP headers for CORS requests.
|
41
42
|
data = @config.params
|
42
43
|
data.last_message_sequence = @_lastMessageSequence
|
43
|
-
# TODO: Some of these options will be deprecated in
|
44
|
+
# TODO: Some of these options will be deprecated in jQuery 1.8
|
44
45
|
# See: http://api.jquery.com/jQuery.ajax/#jqXHR
|
46
|
+
opts =
|
47
|
+
crossDomain: true
|
48
|
+
data: data
|
49
|
+
timeout: @_timeout
|
50
|
+
success: @_success
|
51
|
+
error: @_error
|
52
|
+
complete: @_xhrComplete
|
53
|
+
opts.xhr = hackAroundFirefoxXhrHeadersBug if $.browser.mozilla
|
54
|
+
$.ajax @config.longPoll.url, opts
|
45
55
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
error: @_error
|
52
|
-
xhr: ->
|
53
|
-
# TODO - This while `xrh` attr is a stupid hack to deal with CORS short-comings in jQuery in Firefox.
|
54
|
-
# This ticket can be viewed at http://bugs.jquery.com/ticket/10338. Once jQuery is
|
55
|
-
# upgraded to this version, we can probably remove this, but be sure you test the
|
56
|
-
# crap out of Firefox!
|
57
|
-
#
|
58
|
-
# Its also worth noting that I had to localize this monkey-patch to the Firehose.LongPoll
|
59
|
-
# consumer because a previous global patch on jQuery.ajaxSettings.xhr was breaking regular IE7
|
60
|
-
# loading. I figured its better to localize this anyway to solve that problem and loading order issues.
|
61
|
-
xhr = jQuery.ajaxSettings.xhr()
|
62
|
-
getAllResponseHeaders = xhr.getAllResponseHeaders
|
63
|
-
xhr.getAllResponseHeaders = ->
|
64
|
-
allHeaders = getAllResponseHeaders.call(xhr)
|
65
|
-
return allHeaders if allHeaders
|
66
|
-
allHeaders = ""
|
67
|
-
for headerName in [ "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma" ]
|
68
|
-
do (headerName) ->
|
69
|
-
allHeaders += headerName + ": " + xhr.getResponseHeader(headerName) + "\n" if xhr.getResponseHeader(headerName)
|
70
|
-
allHeaders
|
71
|
-
xhr
|
56
|
+
_xhrComplete: (jqXhr) =>
|
57
|
+
# Get the last sequence from the server if specified.
|
58
|
+
if jqXhr.status is 200
|
59
|
+
val = jqXhr.getResponseHeader @messageSequenceHeader
|
60
|
+
@_lastMessageSequence = val if val?
|
72
61
|
|
73
|
-
|
74
|
-
|
75
|
-
if jqXhr.status == 200
|
76
|
-
@_lastMessageSequence = jqXhr.getResponseHeader(@messageSequenceHeader)
|
77
|
-
if @_lastMessageSequence == null
|
78
|
-
console.log 'ERROR: Unable to get last message sequnce from header'
|
62
|
+
stop: =>
|
63
|
+
@_stopRequestLoop = true
|
79
64
|
|
80
65
|
_success: (data, status, jqXhr) =>
|
81
|
-
|
82
|
-
|
83
|
-
# Call the 'connected' callback if the connection succeeds.
|
84
|
-
@_open(data) unless @_succeeded
|
66
|
+
@_open data unless @_succeeded
|
67
|
+
return if @_stopRequestLoop
|
85
68
|
if jqXhr.status == 204
|
86
69
|
# If we get a 204 back, that means the server timed-out and sent back a 204 with a
|
87
70
|
# X-Http-Next-Request header
|
88
71
|
#
|
89
|
-
# Why did we use a 204 and not a 408? Because
|
72
|
+
# Why did we use a 204 and not a 408? Because Firefox is really stupid about 400 level error
|
90
73
|
# codes and would claims its a 0 error code, which we use for something else. Firefox is IE
|
91
74
|
# in this case
|
92
75
|
@connect(@_okInterval)
|
@@ -109,9 +92,65 @@ class Firehose.LongPoll extends Firehose.Transport
|
|
109
92
|
_error: (jqXhr, status, error) =>
|
110
93
|
@_isConnected = false
|
111
94
|
@config.disconnected()
|
95
|
+
unless @_stopRequestLoop
|
96
|
+
# Ping the server to make sure this isn't a network connectivity error
|
97
|
+
setTimeout @_ping, @_retryDelay + @_lagTime
|
98
|
+
# Reconnect with delay
|
99
|
+
setTimeout @_request, @_retryDelay
|
100
|
+
|
101
|
+
# NB: This is a stupid hack to deal with CORS short-comings in jQuery in
|
102
|
+
# Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
|
103
|
+
# Once jQuery is upgraded to this version we can probably remove this, but be
|
104
|
+
# sure you test the crap out of Firefox!
|
105
|
+
# Its also worth noting that I had to localize this monkey-patch to the
|
106
|
+
# Firehose.LongPoll consumer because a previous global patch on
|
107
|
+
# jQuery.ajaxSettings.xhr was breaking regular IE7 loading. Better to localize
|
108
|
+
# this anyway to solve that problem and loading order issues.
|
109
|
+
hackAroundFirefoxXhrHeadersBug = ->
|
110
|
+
XHR_HEADERS = [
|
111
|
+
"Cache-Control", "Content-Language", "Content-Type"
|
112
|
+
"Expires", "Last-Modified", "Pragma"
|
113
|
+
]
|
114
|
+
xhr = jQuery.ajaxSettings.xhr()
|
115
|
+
originalFun = xhr.getAllResponseHeaders
|
116
|
+
xhr.getAllResponseHeaders = ->
|
117
|
+
return allHeaders if allHeaders = originalFun.call xhr
|
118
|
+
lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
|
119
|
+
"#{name}: #{xhr.getResponseHeader name}"
|
120
|
+
lines.join '\n'
|
121
|
+
xhr
|
112
122
|
|
113
|
-
|
114
|
-
|
123
|
+
# NB: Leaving this here for now. It does work, but won't help us since the XDR
|
124
|
+
# object cannot access any response headers, which Firehose relies on.
|
115
125
|
|
116
|
-
|
117
|
-
|
126
|
+
# # Let's try to hack in support for IE8+ via the XDomainRequest object!
|
127
|
+
# # This was adapted from code shamelessly stolen from:
|
128
|
+
# # https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
|
129
|
+
# if $.browser.msie and parseInt($.browser.version, 10) in [8, 9]
|
130
|
+
# jQuery.ajaxTransport (s) ->
|
131
|
+
# if s.crossDomain and s.async
|
132
|
+
# if s.timeout
|
133
|
+
# s.xdrTimeout = s.timeout
|
134
|
+
# delete s.timeout
|
135
|
+
# xdr = undefined
|
136
|
+
# return {
|
137
|
+
# send: (_, complete) ->
|
138
|
+
# callback = (status, statusText, responses, responseHeaders) ->
|
139
|
+
# xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
|
140
|
+
# xdr = undefined
|
141
|
+
# complete status, statusText, responses, responseHeaders
|
142
|
+
# xdr = new XDomainRequest()
|
143
|
+
# xdr.open s.type, s.url
|
144
|
+
# xdr.onload = ->
|
145
|
+
# headers = "Content-Type: #{xdr.contentType}"
|
146
|
+
# callback 200, "OK", {text: xdr.responseText}, headers
|
147
|
+
# xdr.onerror = -> callback 404, "Not Found"
|
148
|
+
# if s.xdrTimeout?
|
149
|
+
# xdr.ontimeout = -> callback 0, "timeout"
|
150
|
+
# xdr.timeout = s.xdrTimeout
|
151
|
+
# xdr.send (s.hasContent and s.data) or null
|
152
|
+
# abort: ->
|
153
|
+
# if xdr?
|
154
|
+
# xdr.onerror = jQuery.noop()
|
155
|
+
# xdr.abort()
|
156
|
+
# }
|
@@ -11,23 +11,21 @@ class Firehose.Transport
|
|
11
11
|
|
12
12
|
# Lets rock'n'roll! Connect to the server.
|
13
13
|
connect: (delay = 0) =>
|
14
|
-
setTimeout
|
15
|
-
@_request()
|
16
|
-
, delay
|
14
|
+
setTimeout @_request, delay
|
17
15
|
this
|
18
16
|
|
19
|
-
#
|
20
|
-
|
17
|
+
# Hey subclasses:
|
18
|
+
name: -> throw 'not implemented in base Transport' # implement this to identify transport type
|
19
|
+
stop: -> throw 'not implemented in base Transport' # implement this to stop receiving messages
|
20
|
+
_request: -> throw 'not implemented in base Transport' # implement this to handle requests
|
21
21
|
|
22
22
|
# Default error handler
|
23
23
|
_error: (event) =>
|
24
|
-
|
25
|
-
# Fail peremantly if the error happens on the first connection.
|
26
|
-
@config.failed(this)
|
27
|
-
else
|
24
|
+
if @_succeeded
|
28
25
|
# Lets try to connect again with delay
|
29
26
|
@config.disconnected()
|
30
27
|
@connect(@_retryDelay)
|
28
|
+
else @config.failed @
|
31
29
|
|
32
30
|
# Default connection established handler
|
33
31
|
_open: (event) =>
|
@@ -3,6 +3,8 @@
|
|
3
3
|
window.WEB_SOCKET_SWF_LOCATION = '/assets/firehose/WebSocketMain.swf' if !window.WEB_SOCKET_SWF_LOCATION
|
4
4
|
|
5
5
|
class Firehose.WebSocket extends Firehose.Transport
|
6
|
+
name: -> 'WebSocket'
|
7
|
+
|
6
8
|
@supported: =>
|
7
9
|
# Compatibility reference: http://caniuse.com/websockets
|
8
10
|
# We don't need to explicitly check for Flash web socket or MozWebSocket
|
@@ -18,11 +20,31 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
18
20
|
@config.webSocket.url ||= "ws:#{@config.uri}?#{$.param(@config.params)}"
|
19
21
|
|
20
22
|
_request: =>
|
21
|
-
@socket = new window.WebSocket
|
22
|
-
@socket.onopen
|
23
|
-
@socket.onclose
|
24
|
-
@socket.onerror
|
25
|
-
@socket.onmessage = @
|
23
|
+
@socket = new window.WebSocket @config.webSocket.url
|
24
|
+
@socket.onopen = @_open
|
25
|
+
@socket.onclose = @_close
|
26
|
+
@socket.onerror = @_error
|
27
|
+
@socket.onmessage = @_waitForPong
|
28
|
+
|
29
|
+
stop: =>
|
30
|
+
@cleanUp()
|
31
|
+
|
32
|
+
_open: =>
|
33
|
+
# TODO: include JSON client-side script for less awesome browsers
|
34
|
+
@socket.send JSON.stringify ping: 'PING'
|
35
|
+
# TODO: consider making this timeout configurable somehow...
|
36
|
+
@pingTimeout = setTimeout @_error, 2000
|
37
|
+
|
38
|
+
_waitForPong: (event) =>
|
39
|
+
o = try JSON.parse event.data catch e then {}
|
40
|
+
if o.pong is 'PONG'
|
41
|
+
# Not quite sure why this doesn't work in IE8:
|
42
|
+
# (Throws "TypeError: Array or arguments object expected")
|
43
|
+
# Firehose.Transport::_open.apply @, event
|
44
|
+
@_succeeded = true
|
45
|
+
@config.connected @
|
46
|
+
clearTimeout @pingTimeout
|
47
|
+
@socket.onmessage = @_message
|
26
48
|
|
27
49
|
_message: (event) =>
|
28
50
|
@config.message(@config.parse(event.data))
|
@@ -34,13 +56,14 @@ class Firehose.WebSocket extends Firehose.Transport
|
|
34
56
|
@_error(event)
|
35
57
|
|
36
58
|
_error: (event) =>
|
37
|
-
|
59
|
+
@cleanUp()
|
60
|
+
super
|
61
|
+
|
62
|
+
cleanUp: ->
|
38
63
|
if @socket
|
39
|
-
@socket.onopen
|
40
|
-
@socket.onclose
|
41
|
-
@socket.onerror
|
64
|
+
@socket.onopen = null
|
65
|
+
@socket.onclose = null
|
66
|
+
@socket.onerror = null
|
42
67
|
@socket.onmessage = null
|
43
68
|
@socket.close()
|
44
69
|
delete(@socket)
|
45
|
-
|
46
|
-
super
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'faye/websocket'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module Firehose
|
4
5
|
module Rack
|
@@ -47,7 +48,8 @@ module Firehose
|
|
47
48
|
env['async.callback'].call response(400, "Header '#{LAST_MESSAGE_SEQUENCE_HEADER}' may not be less than zero", response_headers(env))
|
48
49
|
else
|
49
50
|
Channel.new(path).next_message(last_sequence, :timeout => TIMEOUT).callback do |message, sequence|
|
50
|
-
|
51
|
+
combined_headers = response_headers(env).merge(LAST_MESSAGE_SEQUENCE_HEADER => sequence.to_s)
|
52
|
+
env['async.callback'].call response(200, message, combined_headers)
|
51
53
|
end.errback do |e|
|
52
54
|
if e == :timeout
|
53
55
|
env['async.callback'].call response(204, '', response_headers(env))
|
@@ -84,8 +86,8 @@ module Firehose
|
|
84
86
|
def cors_headers(env)
|
85
87
|
# TODO seperate out CORS logic as an async middleware with a Goliath web server.
|
86
88
|
{
|
87
|
-
'Access-Control-Allow-Origin'
|
88
|
-
'Access-Control-Expose-Headers'
|
89
|
+
'Access-Control-Allow-Origin' => cors_origin(env),
|
90
|
+
'Access-Control-Expose-Headers' => LAST_MESSAGE_SEQUENCE_HEADER
|
89
91
|
}
|
90
92
|
end
|
91
93
|
end
|
@@ -117,9 +119,10 @@ module Firehose
|
|
117
119
|
subscribe.call nil
|
118
120
|
end
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
122
|
+
ws.onmessage = lambda do |event|
|
123
|
+
o = JSON.parse(event.data, :symbolize_names => true) rescue {}
|
124
|
+
ws.send(JSON.generate(:pong => 'PONG')) if o[:ping] == 'PING'
|
125
|
+
end
|
123
126
|
|
124
127
|
ws.onclose = lambda do |event|
|
125
128
|
if @deferrable
|
data/lib/firehose/rack.rb
CHANGED
@@ -10,7 +10,7 @@ module Firehose
|
|
10
10
|
# Normally we'd want to use a custom header to reduce the likelihood of some
|
11
11
|
# HTTP middleware clobbering the value. But Safari seems to ignore our CORS
|
12
12
|
# header instructions, so we are using 'pragma' because it is always allowed.
|
13
|
-
LAST_MESSAGE_SEQUENCE_HEADER = '
|
13
|
+
LAST_MESSAGE_SEQUENCE_HEADER = 'Pragma'
|
14
14
|
RACK_LAST_MESSAGE_SEQUENCE_HEADER = "HTTP_#{LAST_MESSAGE_SEQUENCE_HEADER.upcase.gsub('-', '_')}"
|
15
15
|
# Don't cache in development mode
|
16
16
|
CORS_OPTIONS_MAX_AGE = ENV['RACK_ENV'] == 'development' ? '1' : '1728000'
|
data/lib/firehose/version.rb
CHANGED
metadata
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firehose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.alpha.
|
4
|
+
version: 0.2.alpha.6
|
5
5
|
prerelease: 4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brad Gessler
|
9
9
|
- Steel Fu
|
10
10
|
- Paul Cortens
|
11
|
+
- Zach Zolton
|
11
12
|
autorequire:
|
12
13
|
bindir: bin
|
13
14
|
cert_chain: []
|
14
|
-
date: 2012-08-
|
15
|
+
date: 2012-08-28 00:00:00.000000000 Z
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
18
|
name: eventmachine
|
18
|
-
requirement: &
|
19
|
+
requirement: &70256291491020 !ruby/object:Gem::Requirement
|
19
20
|
none: false
|
20
21
|
requirements:
|
21
22
|
- - ! '>='
|
@@ -23,10 +24,10 @@ dependencies:
|
|
23
24
|
version: 1.0.0.rc
|
24
25
|
type: :runtime
|
25
26
|
prerelease: false
|
26
|
-
version_requirements: *
|
27
|
+
version_requirements: *70256291491020
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
29
|
name: em-hiredis
|
29
|
-
requirement: &
|
30
|
+
requirement: &70256291490580 !ruby/object:Gem::Requirement
|
30
31
|
none: false
|
31
32
|
requirements:
|
32
33
|
- - ! '>='
|
@@ -34,10 +35,10 @@ dependencies:
|
|
34
35
|
version: '0'
|
35
36
|
type: :runtime
|
36
37
|
prerelease: false
|
37
|
-
version_requirements: *
|
38
|
+
version_requirements: *70256291490580
|
38
39
|
- !ruby/object:Gem::Dependency
|
39
40
|
name: thor
|
40
|
-
requirement: &
|
41
|
+
requirement: &70256291490080 !ruby/object:Gem::Requirement
|
41
42
|
none: false
|
42
43
|
requirements:
|
43
44
|
- - ! '>='
|
@@ -45,10 +46,10 @@ dependencies:
|
|
45
46
|
version: '0'
|
46
47
|
type: :runtime
|
47
48
|
prerelease: false
|
48
|
-
version_requirements: *
|
49
|
+
version_requirements: *70256291490080
|
49
50
|
- !ruby/object:Gem::Dependency
|
50
51
|
name: faraday
|
51
|
-
requirement: &
|
52
|
+
requirement: &70256291489660 !ruby/object:Gem::Requirement
|
52
53
|
none: false
|
53
54
|
requirements:
|
54
55
|
- - ! '>='
|
@@ -56,10 +57,10 @@ dependencies:
|
|
56
57
|
version: '0'
|
57
58
|
type: :runtime
|
58
59
|
prerelease: false
|
59
|
-
version_requirements: *
|
60
|
+
version_requirements: *70256291489660
|
60
61
|
- !ruby/object:Gem::Dependency
|
61
62
|
name: faye-websocket
|
62
|
-
requirement: &
|
63
|
+
requirement: &70256291489240 !ruby/object:Gem::Requirement
|
63
64
|
none: false
|
64
65
|
requirements:
|
65
66
|
- - ! '>='
|
@@ -67,10 +68,10 @@ dependencies:
|
|
67
68
|
version: '0'
|
68
69
|
type: :runtime
|
69
70
|
prerelease: false
|
70
|
-
version_requirements: *
|
71
|
+
version_requirements: *70256291489240
|
71
72
|
- !ruby/object:Gem::Dependency
|
72
73
|
name: em-http-request
|
73
|
-
requirement: &
|
74
|
+
requirement: &70256291488740 !ruby/object:Gem::Requirement
|
74
75
|
none: false
|
75
76
|
requirements:
|
76
77
|
- - ~>
|
@@ -78,10 +79,21 @@ dependencies:
|
|
78
79
|
version: 1.0.0
|
79
80
|
type: :runtime
|
80
81
|
prerelease: false
|
81
|
-
version_requirements: *
|
82
|
+
version_requirements: *70256291488740
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: json
|
85
|
+
requirement: &70256291488320 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: *70256291488320
|
82
94
|
- !ruby/object:Gem::Dependency
|
83
95
|
name: rspec
|
84
|
-
requirement: &
|
96
|
+
requirement: &70256291487860 !ruby/object:Gem::Requirement
|
85
97
|
none: false
|
86
98
|
requirements:
|
87
99
|
- - ! '>='
|
@@ -89,10 +101,10 @@ dependencies:
|
|
89
101
|
version: '0'
|
90
102
|
type: :development
|
91
103
|
prerelease: false
|
92
|
-
version_requirements: *
|
104
|
+
version_requirements: *70256291487860
|
93
105
|
- !ruby/object:Gem::Dependency
|
94
106
|
name: webmock
|
95
|
-
requirement: &
|
107
|
+
requirement: &70256291459520 !ruby/object:Gem::Requirement
|
96
108
|
none: false
|
97
109
|
requirements:
|
98
110
|
- - ! '>='
|
@@ -100,10 +112,10 @@ dependencies:
|
|
100
112
|
version: '0'
|
101
113
|
type: :development
|
102
114
|
prerelease: false
|
103
|
-
version_requirements: *
|
115
|
+
version_requirements: *70256291459520
|
104
116
|
- !ruby/object:Gem::Dependency
|
105
117
|
name: guard-rspec
|
106
|
-
requirement: &
|
118
|
+
requirement: &70256291459080 !ruby/object:Gem::Requirement
|
107
119
|
none: false
|
108
120
|
requirements:
|
109
121
|
- - ! '>='
|
@@ -111,10 +123,10 @@ dependencies:
|
|
111
123
|
version: '0'
|
112
124
|
type: :development
|
113
125
|
prerelease: false
|
114
|
-
version_requirements: *
|
126
|
+
version_requirements: *70256291459080
|
115
127
|
- !ruby/object:Gem::Dependency
|
116
128
|
name: guard-bundler
|
117
|
-
requirement: &
|
129
|
+
requirement: &70256291458660 !ruby/object:Gem::Requirement
|
118
130
|
none: false
|
119
131
|
requirements:
|
120
132
|
- - ! '>='
|
@@ -122,10 +134,10 @@ dependencies:
|
|
122
134
|
version: '0'
|
123
135
|
type: :development
|
124
136
|
prerelease: false
|
125
|
-
version_requirements: *
|
137
|
+
version_requirements: *70256291458660
|
126
138
|
- !ruby/object:Gem::Dependency
|
127
139
|
name: guard-coffeescript
|
128
|
-
requirement: &
|
140
|
+
requirement: &70256291458220 !ruby/object:Gem::Requirement
|
129
141
|
none: false
|
130
142
|
requirements:
|
131
143
|
- - ! '>='
|
@@ -133,10 +145,10 @@ dependencies:
|
|
133
145
|
version: '0'
|
134
146
|
type: :development
|
135
147
|
prerelease: false
|
136
|
-
version_requirements: *
|
148
|
+
version_requirements: *70256291458220
|
137
149
|
- !ruby/object:Gem::Dependency
|
138
150
|
name: rainbows
|
139
|
-
requirement: &
|
151
|
+
requirement: &70256291457800 !ruby/object:Gem::Requirement
|
140
152
|
none: false
|
141
153
|
requirements:
|
142
154
|
- - ! '>='
|
@@ -144,10 +156,10 @@ dependencies:
|
|
144
156
|
version: '0'
|
145
157
|
type: :development
|
146
158
|
prerelease: false
|
147
|
-
version_requirements: *
|
159
|
+
version_requirements: *70256291457800
|
148
160
|
- !ruby/object:Gem::Dependency
|
149
161
|
name: thin
|
150
|
-
requirement: &
|
162
|
+
requirement: &70256291457380 !ruby/object:Gem::Requirement
|
151
163
|
none: false
|
152
164
|
requirements:
|
153
165
|
- - ! '>='
|
@@ -155,10 +167,10 @@ dependencies:
|
|
155
167
|
version: '0'
|
156
168
|
type: :development
|
157
169
|
prerelease: false
|
158
|
-
version_requirements: *
|
170
|
+
version_requirements: *70256291457380
|
159
171
|
- !ruby/object:Gem::Dependency
|
160
172
|
name: rack-test
|
161
|
-
requirement: &
|
173
|
+
requirement: &70256291456960 !ruby/object:Gem::Requirement
|
162
174
|
none: false
|
163
175
|
requirements:
|
164
176
|
- - ! '>='
|
@@ -166,10 +178,10 @@ dependencies:
|
|
166
178
|
version: '0'
|
167
179
|
type: :development
|
168
180
|
prerelease: false
|
169
|
-
version_requirements: *
|
181
|
+
version_requirements: *70256291456960
|
170
182
|
- !ruby/object:Gem::Dependency
|
171
183
|
name: async_rack_test
|
172
|
-
requirement: &
|
184
|
+
requirement: &70256291456520 !ruby/object:Gem::Requirement
|
173
185
|
none: false
|
174
186
|
requirements:
|
175
187
|
- - ! '>='
|
@@ -177,10 +189,10 @@ dependencies:
|
|
177
189
|
version: '0'
|
178
190
|
type: :development
|
179
191
|
prerelease: false
|
180
|
-
version_requirements: *
|
192
|
+
version_requirements: *70256291456520
|
181
193
|
- !ruby/object:Gem::Dependency
|
182
194
|
name: foreman
|
183
|
-
requirement: &
|
195
|
+
requirement: &70256291456040 !ruby/object:Gem::Requirement
|
184
196
|
none: false
|
185
197
|
requirements:
|
186
198
|
- - ! '>='
|
@@ -188,10 +200,10 @@ dependencies:
|
|
188
200
|
version: '0'
|
189
201
|
type: :development
|
190
202
|
prerelease: false
|
191
|
-
version_requirements: *
|
203
|
+
version_requirements: *70256291456040
|
192
204
|
- !ruby/object:Gem::Dependency
|
193
205
|
name: sprockets
|
194
|
-
requirement: &
|
206
|
+
requirement: &70256291455540 !ruby/object:Gem::Requirement
|
195
207
|
none: false
|
196
208
|
requirements:
|
197
209
|
- - ! '>='
|
@@ -199,13 +211,14 @@ dependencies:
|
|
199
211
|
version: '0'
|
200
212
|
type: :development
|
201
213
|
prerelease: false
|
202
|
-
version_requirements: *
|
214
|
+
version_requirements: *70256291455540
|
203
215
|
description: Firehose is a realtime web application toolkit for building realtime
|
204
216
|
Ruby web applications.
|
205
217
|
email:
|
206
218
|
- brad@polleverywhere.com
|
207
219
|
- steel@polleverywhere.com
|
208
220
|
- paul@polleverywhere.com
|
221
|
+
- zach@polleverywhere.com
|
209
222
|
executables:
|
210
223
|
- firehose
|
211
224
|
extensions: []
|
@@ -282,6 +295,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
282
295
|
- - ! '>='
|
283
296
|
- !ruby/object:Gem::Version
|
284
297
|
version: '0'
|
298
|
+
segments:
|
299
|
+
- 0
|
300
|
+
hash: 1005777279219714638
|
285
301
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
286
302
|
none: false
|
287
303
|
requirements:
|