firehose 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +30 -35
- data/firehose.gemspec +3 -3
- data/lib/assets/flash/{WebSocketMain.swf → firehose/WebSocketMain.swf} +0 -0
- data/lib/assets/javascripts/firehose.js.coffee +5 -0
- data/lib/assets/javascripts/firehose/base.js.coffee +1 -0
- data/lib/assets/javascripts/firehose/client.js.coffee +9 -0
- data/lib/assets/javascripts/firehose/long_poll.js.coffee +79 -0
- data/lib/assets/javascripts/firehose/transport.js.coffee +40 -0
- data/lib/assets/javascripts/firehose/web_socket.js.coffee +48 -0
- data/lib/firehose/version.rb +3 -2
- metadata +34 -26
data/README.md
CHANGED
@@ -9,26 +9,26 @@
|
|
9
9
|
|
10
10
|
# What is Firehose?
|
11
11
|
|
12
|
-
Firehose is both a Rack application and
|
12
|
+
Firehose is both a Rack application and JavasScript library that makes building scalable real-time web applications possible.
|
13
13
|
|
14
14
|
# How is it different from socket.io?
|
15
15
|
|
16
16
|
socket.io attempts to store connection state per node instance. Firehose makes no attempt to store connection state.
|
17
17
|
|
18
|
-
Also, socket.io attempts to abstract a low-latency full-duplex port. Firehose assumes that its impossible to simulate this in older web browsers that don't support WebSockets. As such, Firehose focuses on low-latency server-to-client connections and encourages the use of HTTP transports for client-to-server communications.
|
18
|
+
Also, socket.io attempts to abstract a low-latency full-duplex port. Firehose assumes that its impossible to simulate this in older web browsers that don't support WebSockets. As such, Firehose focuses on low-latency server-to-client connections and encourages the use of existing HTTP transports, like POST and PUT, for client-to-server communications.
|
19
19
|
|
20
|
-
Finally,
|
20
|
+
Finally, Firehose attempts to solve data consistency issues and authentication by encourage the use of proxying to the web application.
|
21
21
|
|
22
22
|
# Getting Started
|
23
23
|
|
24
24
|
First, you'll need to install and run RabbitMQ.
|
25
25
|
|
26
|
-
```
|
26
|
+
```sh
|
27
27
|
apt-get install rabbitmq # Install on Ubuntu
|
28
28
|
brew install rabbitmq # Install on Mac Homebrew
|
29
29
|
```
|
30
30
|
|
31
|
-
## The
|
31
|
+
## The Server
|
32
32
|
|
33
33
|
The consumer is the web server that your client connects to for real-time updates. Create a config.ru file with the following:
|
34
34
|
|
@@ -36,21 +36,7 @@ The consumer is the web server that your client connects to for real-time update
|
|
36
36
|
require 'rubygems'
|
37
37
|
require 'firehose'
|
38
38
|
|
39
|
-
run Firehose::
|
40
|
-
config.timeout = 20
|
41
|
-
|
42
|
-
# Extract the consumer ID from the HTTP session. This could be a cookie
|
43
|
-
# query param, or whatever.
|
44
|
-
config.consumer = Proc.new do |env|
|
45
|
-
Firehose::Consumer.new(env['HTTP_CONSUMER_ID'])
|
46
|
-
end
|
47
|
-
|
48
|
-
# Use the /url/path for the queue channel. You could change this to a query
|
49
|
-
# param, or whatever
|
50
|
-
config.channel = Proc.new do |env|
|
51
|
-
env['PATH_INFO']
|
52
|
-
end
|
53
|
-
end
|
39
|
+
run Firehose::Rack::App.new
|
54
40
|
```
|
55
41
|
|
56
42
|
Now run the config.ru file in a server that supports async Rack callbacks (like thin or rainbows)
|
@@ -59,32 +45,41 @@ Now run the config.ru file in a server that supports async Rack callbacks (like
|
|
59
45
|
thin -R config.ru -p 4000 start
|
60
46
|
```
|
61
47
|
|
62
|
-
## The
|
48
|
+
## The Publisher
|
63
49
|
|
64
|
-
Lets test
|
50
|
+
Lets test it out! Open two terminal windows. In one window, curl:
|
65
51
|
|
66
52
|
```sh
|
67
|
-
curl "http://localhost:4000/"
|
53
|
+
curl "http://localhost:4000/hello"
|
68
54
|
```
|
69
55
|
|
70
|
-
Then run the following
|
56
|
+
Then run the following in the other terminal:
|
71
57
|
|
72
|
-
```
|
73
|
-
|
74
|
-
|
58
|
+
```sh
|
59
|
+
curl -X PUT -d "Greetings fellow human being..." "http://localhost:4000/hello"
|
60
|
+
```
|
61
|
+
|
62
|
+
and you should see the message in the other terminal.
|
75
63
|
|
76
|
-
|
64
|
+
```sh
|
65
|
+
Greetings fellow human being...
|
77
66
|
```
|
78
67
|
|
79
|
-
##
|
68
|
+
## Yeah, so?
|
69
|
+
|
70
|
+
You have a dirt simple HTTP pub-sub feed. You could setup an `after_commit` hook on ActiveRecord to push JSON to an end-point. On the subscription side, you could have a Backbone.js application that picks up the changes and updates the client-side UI.
|
71
|
+
|
72
|
+
Holy mackerel! Its a nice, clean, RESTful way to build real-time web applications.
|
73
|
+
|
74
|
+
# The JavaScript Client
|
80
75
|
|
81
|
-
|
76
|
+
Firehose doesn't just stop at curl; it has a full-featured JavaScript client that lets you subscribe to channels for live updates.
|
82
77
|
|
83
78
|
```javascript
|
84
79
|
new Firehose.Client()
|
85
80
|
.url({
|
86
|
-
websocket: 'ws://
|
87
|
-
longpoll: 'http://
|
81
|
+
websocket: 'ws://localhost:5100',
|
82
|
+
longpoll: 'http://localhost:5100'
|
88
83
|
})
|
89
84
|
.params({
|
90
85
|
cid: '024023948234'
|
@@ -93,13 +88,13 @@ new Firehose.Client()
|
|
93
88
|
timeout: 5000
|
94
89
|
})
|
95
90
|
.message(function(msg){
|
96
|
-
|
91
|
+
console.log(msg); // Fires when a message is received from the server.
|
97
92
|
})
|
98
93
|
.connected(function(){
|
99
|
-
|
94
|
+
console.log('Howdy friend!');
|
100
95
|
})
|
101
96
|
.disconnected(function(){
|
102
|
-
|
97
|
+
console.log('Bu bye');
|
103
98
|
})
|
104
99
|
.connect()
|
105
100
|
```
|
data/firehose.gemspec
CHANGED
@@ -5,9 +5,9 @@ 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"]
|
9
|
-
s.email = ["brad@
|
10
|
-
s.homepage = ""
|
8
|
+
s.authors = ["Brad Gessler", "Steel Fu"]
|
9
|
+
s.email = ["brad@polleverywhere.com", "steel@polleverywhere.com"]
|
10
|
+
s.homepage = "http://github.com/polleverywhere/firehose"
|
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.}
|
13
13
|
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
window.Firehose = {}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class Firehose.Client
|
2
|
+
constructor: (args) ->
|
3
|
+
@transports = ['WebSocket', 'LongPoll']
|
4
|
+
|
5
|
+
# Detect the first supported transport give it back
|
6
|
+
transport = _.detect @transports, (transport) ->
|
7
|
+
Firehose[transport].supported()
|
8
|
+
|
9
|
+
return new Firehose[transport](args)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class Firehose.LongPoll extends Firehose.Transport
|
2
|
+
# CORS is supported in IE 8+
|
3
|
+
@ieSupported: =>
|
4
|
+
$.browser.msie and parseInt($.browser.version) > 7 and window.XDomainRequest
|
5
|
+
|
6
|
+
@supported: =>
|
7
|
+
# IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
|
8
|
+
$.support.cors || LongPoll.ieSupported()
|
9
|
+
|
10
|
+
constructor: (args) ->
|
11
|
+
super args
|
12
|
+
|
13
|
+
# We use the lag time to make the client live longer than the server.
|
14
|
+
@_lagTime = 5000
|
15
|
+
@_timeout = @options.timeout + @_lagTime
|
16
|
+
@_dataType = "json"
|
17
|
+
@_offlineTimer
|
18
|
+
@_okInterval = 0
|
19
|
+
|
20
|
+
@registerIETransport()
|
21
|
+
|
22
|
+
registerIETransport: =>
|
23
|
+
if LongPoll.ieSupported()
|
24
|
+
$.ajaxTransport 'json', (options, orignalOptions, jqXhr) ->
|
25
|
+
xdr = null
|
26
|
+
send: (_, callback) ->
|
27
|
+
xdr = new XDomainRequest()
|
28
|
+
xdr.onload = ->
|
29
|
+
statusCode = if xdr.responseText.length > 0 then 200 else 204
|
30
|
+
callback(statusCode, 'success', text: xdr.responseText)
|
31
|
+
|
32
|
+
xdr.onerror = xdr.ontimeout = ->
|
33
|
+
callback(400, 'failed', text: xdr.responseText)
|
34
|
+
|
35
|
+
xdr.open(options.type, options.url)
|
36
|
+
xdr.send(options.data)
|
37
|
+
|
38
|
+
abort: ->
|
39
|
+
if xdr
|
40
|
+
xdr.onerror = $.noop()
|
41
|
+
xdr.abort()
|
42
|
+
|
43
|
+
# also, override the support check
|
44
|
+
$.support.cors = true;
|
45
|
+
|
46
|
+
connect: (delay = 0) =>
|
47
|
+
@onConnected()
|
48
|
+
super(delay)
|
49
|
+
|
50
|
+
_request: =>
|
51
|
+
$.ajax @url["longpoll"],
|
52
|
+
crossDomain: true
|
53
|
+
cache: false
|
54
|
+
dataType: @_dataType
|
55
|
+
data: @params
|
56
|
+
timeout: @_timeout
|
57
|
+
success: @_success
|
58
|
+
error: @_error
|
59
|
+
|
60
|
+
_success: (data, status, jqXhr) =>
|
61
|
+
if jqXhr.status == 204
|
62
|
+
# If we get a 204 back, that means the server timed-out and sent back a 204 with a
|
63
|
+
# X-Http-Next-Request header
|
64
|
+
#
|
65
|
+
# Why did we use a 204 and not a 408? Because FireFox is really stupid about 400 level error
|
66
|
+
# codes and would claims its a 0 error code, which we use for something else. Firefox is IE
|
67
|
+
# in thise case
|
68
|
+
@connect(@_okInterval)
|
69
|
+
else
|
70
|
+
@onMessage(data)
|
71
|
+
@connect(@_okInterval)
|
72
|
+
|
73
|
+
# We need this custom handler to have the connection status
|
74
|
+
# properly displayed
|
75
|
+
_error: (jqXhr, status, error) =>
|
76
|
+
clearTimeout(@_offlineTimer)
|
77
|
+
@onDisconnected()
|
78
|
+
@_offlineTimer = setTimeout(@onConnected, @_errorInterval + @_lagTime)
|
79
|
+
@connect(@_errorInterval)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Firehose.Transport
|
2
|
+
# Class method to determine whether transport is supported by the current browser
|
3
|
+
@supported: =>
|
4
|
+
false
|
5
|
+
|
6
|
+
constructor: (args) ->
|
7
|
+
@_errorInterval = 5000
|
8
|
+
|
9
|
+
# Chainable config
|
10
|
+
connected: (@onConnected) -> this
|
11
|
+
disconnected: (@onDisconnected) -> this
|
12
|
+
message: (@onMessage) -> this
|
13
|
+
error: (@onError) -> this
|
14
|
+
url: (@url) -> this
|
15
|
+
params: (@params) -> this
|
16
|
+
options: (@options) -> this
|
17
|
+
|
18
|
+
# Lets rock'n'roll
|
19
|
+
connect: (delay = 0) =>
|
20
|
+
setTimeout =>
|
21
|
+
@_request()
|
22
|
+
, delay
|
23
|
+
this
|
24
|
+
|
25
|
+
# Sub classes need to implement this method to handle requests
|
26
|
+
_request: =>
|
27
|
+
|
28
|
+
# Default error handler
|
29
|
+
_error: (event) =>
|
30
|
+
# Lets try to connect again with delay
|
31
|
+
@onDisconnected()
|
32
|
+
@connect(@_errorInterval)
|
33
|
+
|
34
|
+
# Default connection established handler
|
35
|
+
_open: (event) =>
|
36
|
+
@onConnected()
|
37
|
+
|
38
|
+
# Default connection closed handler
|
39
|
+
_close: (event) =>
|
40
|
+
@onDisconnected()
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Firehose.WebSocket extends Firehose.Transport
|
2
|
+
@flashSupported: =>
|
3
|
+
$.browser.msie
|
4
|
+
|
5
|
+
@supported: =>
|
6
|
+
# Compatibility reference: http://caniuse.com/websockets
|
7
|
+
# Native websocket support + Flash web socket
|
8
|
+
window.WebSocket || (window["MozWebSocket"] and window.MozWebSocket) || WebSocket.flashSupported()
|
9
|
+
|
10
|
+
constructor: (args) ->
|
11
|
+
super args
|
12
|
+
|
13
|
+
# Set flash socket path.
|
14
|
+
WebSocket.__swfLocation = "/flash/firehose/WebSocketMain.swf"
|
15
|
+
|
16
|
+
# Mozilla decided to have their own implementation of Web Sockets so detect for that
|
17
|
+
window.WebSocket = window.MozWebSocket if window["MozWebSocket"] and window.MozWebSocket
|
18
|
+
|
19
|
+
_request: =>
|
20
|
+
@socket = new window.WebSocket(@url["websocket"] + "?" + $.param(@params))
|
21
|
+
@socket.onopen = @_open
|
22
|
+
@socket.onclose = @_close
|
23
|
+
@socket.onerror = @_error
|
24
|
+
@socket.onmessage = @_message
|
25
|
+
|
26
|
+
_message: (event) =>
|
27
|
+
try
|
28
|
+
@onMessage($.parseJSON(event.data))
|
29
|
+
catch e # If JSON parsing doesn't work, send the rest of it on through
|
30
|
+
@onMessage(event.data)
|
31
|
+
|
32
|
+
_close: (event) =>
|
33
|
+
if !event || (event and !event.wasClean)
|
34
|
+
# This was not a clean disconnect. An error occurred somewhere
|
35
|
+
# Lets try to reconnect
|
36
|
+
@_error(event)
|
37
|
+
|
38
|
+
_error: (event) =>
|
39
|
+
# Cleanup the current connection
|
40
|
+
if @socket
|
41
|
+
@socket.onopen = null
|
42
|
+
@socket.onclose = null
|
43
|
+
@socket.onerror = null
|
44
|
+
@socket.onmessage = null
|
45
|
+
@socket.close()
|
46
|
+
delete(@socket)
|
47
|
+
|
48
|
+
super
|
data/lib/firehose/version.rb
CHANGED
metadata
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firehose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brad Gessler
|
9
|
+
- Steel Fu
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
@@ -13,7 +14,7 @@ date: 2012-04-16 00:00:00.000000000 Z
|
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: eventmachine
|
16
|
-
requirement: &
|
17
|
+
requirement: &70267687581480 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,10 +22,10 @@ dependencies:
|
|
21
22
|
version: 1.0.0.beta
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *70267687581480
|
25
26
|
- !ruby/object:Gem::Dependency
|
26
27
|
name: amqp
|
27
|
-
requirement: &
|
28
|
+
requirement: &70267687580600 !ruby/object:Gem::Requirement
|
28
29
|
none: false
|
29
30
|
requirements:
|
30
31
|
- - ! '>='
|
@@ -32,10 +33,10 @@ dependencies:
|
|
32
33
|
version: 0.9.4
|
33
34
|
type: :runtime
|
34
35
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
+
version_requirements: *70267687580600
|
36
37
|
- !ruby/object:Gem::Dependency
|
37
38
|
name: thin
|
38
|
-
requirement: &
|
39
|
+
requirement: &70267687580200 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
41
|
requirements:
|
41
42
|
- - ! '>='
|
@@ -43,10 +44,10 @@ dependencies:
|
|
43
44
|
version: '0'
|
44
45
|
type: :runtime
|
45
46
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
+
version_requirements: *70267687580200
|
47
48
|
- !ruby/object:Gem::Dependency
|
48
49
|
name: websocket-rack
|
49
|
-
requirement: &
|
50
|
+
requirement: &70267687579660 !ruby/object:Gem::Requirement
|
50
51
|
none: false
|
51
52
|
requirements:
|
52
53
|
- - ! '>='
|
@@ -54,10 +55,10 @@ dependencies:
|
|
54
55
|
version: '0'
|
55
56
|
type: :runtime
|
56
57
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
+
version_requirements: *70267687579660
|
58
59
|
- !ruby/object:Gem::Dependency
|
59
60
|
name: rspec
|
60
|
-
requirement: &
|
61
|
+
requirement: &70267687579160 !ruby/object:Gem::Requirement
|
61
62
|
none: false
|
62
63
|
requirements:
|
63
64
|
- - ! '>='
|
@@ -65,10 +66,10 @@ dependencies:
|
|
65
66
|
version: '0'
|
66
67
|
type: :development
|
67
68
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
+
version_requirements: *70267687579160
|
69
70
|
- !ruby/object:Gem::Dependency
|
70
71
|
name: rack-test
|
71
|
-
requirement: &
|
72
|
+
requirement: &70267687578500 !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
74
|
requirements:
|
74
75
|
- - ! '>='
|
@@ -76,10 +77,10 @@ dependencies:
|
|
76
77
|
version: '0'
|
77
78
|
type: :development
|
78
79
|
prerelease: false
|
79
|
-
version_requirements: *
|
80
|
+
version_requirements: *70267687578500
|
80
81
|
- !ruby/object:Gem::Dependency
|
81
82
|
name: guard-rspec
|
82
|
-
requirement: &
|
83
|
+
requirement: &70267687338120 !ruby/object:Gem::Requirement
|
83
84
|
none: false
|
84
85
|
requirements:
|
85
86
|
- - ! '>='
|
@@ -87,10 +88,10 @@ dependencies:
|
|
87
88
|
version: '0'
|
88
89
|
type: :development
|
89
90
|
prerelease: false
|
90
|
-
version_requirements: *
|
91
|
+
version_requirements: *70267687338120
|
91
92
|
- !ruby/object:Gem::Dependency
|
92
93
|
name: guard-bundler
|
93
|
-
requirement: &
|
94
|
+
requirement: &70267687337440 !ruby/object:Gem::Requirement
|
94
95
|
none: false
|
95
96
|
requirements:
|
96
97
|
- - ! '>='
|
@@ -98,10 +99,10 @@ dependencies:
|
|
98
99
|
version: '0'
|
99
100
|
type: :development
|
100
101
|
prerelease: false
|
101
|
-
version_requirements: *
|
102
|
+
version_requirements: *70267687337440
|
102
103
|
- !ruby/object:Gem::Dependency
|
103
104
|
name: thin
|
104
|
-
requirement: &
|
105
|
+
requirement: &70267687336400 !ruby/object:Gem::Requirement
|
105
106
|
none: false
|
106
107
|
requirements:
|
107
108
|
- - ! '>='
|
@@ -109,10 +110,10 @@ dependencies:
|
|
109
110
|
version: '0'
|
110
111
|
type: :development
|
111
112
|
prerelease: false
|
112
|
-
version_requirements: *
|
113
|
+
version_requirements: *70267687336400
|
113
114
|
- !ruby/object:Gem::Dependency
|
114
115
|
name: em-http-request
|
115
|
-
requirement: &
|
116
|
+
requirement: &70267687335300 !ruby/object:Gem::Requirement
|
116
117
|
none: false
|
117
118
|
requirements:
|
118
119
|
- - ~>
|
@@ -120,10 +121,10 @@ dependencies:
|
|
120
121
|
version: 0.3.0
|
121
122
|
type: :development
|
122
123
|
prerelease: false
|
123
|
-
version_requirements: *
|
124
|
+
version_requirements: *70267687335300
|
124
125
|
- !ruby/object:Gem::Dependency
|
125
126
|
name: guard-coffeescript
|
126
|
-
requirement: &
|
127
|
+
requirement: &70267687334360 !ruby/object:Gem::Requirement
|
127
128
|
none: false
|
128
129
|
requirements:
|
129
130
|
- - ! '>='
|
@@ -131,11 +132,12 @@ dependencies:
|
|
131
132
|
version: '0'
|
132
133
|
type: :development
|
133
134
|
prerelease: false
|
134
|
-
version_requirements: *
|
135
|
+
version_requirements: *70267687334360
|
135
136
|
description: Firehose is a realtime web application toolkit for building realtime
|
136
137
|
Ruby web applications.
|
137
138
|
email:
|
138
|
-
- brad@
|
139
|
+
- brad@polleverywhere.com
|
140
|
+
- steel@polleverywhere.com
|
139
141
|
executables:
|
140
142
|
- firehose
|
141
143
|
- firehose-test
|
@@ -153,7 +155,13 @@ files:
|
|
153
155
|
- bin/firehose-test
|
154
156
|
- config.ru
|
155
157
|
- firehose.gemspec
|
156
|
-
- lib/assets/flash/WebSocketMain.swf
|
158
|
+
- lib/assets/flash/firehose/WebSocketMain.swf
|
159
|
+
- lib/assets/javascripts/firehose.js.coffee
|
160
|
+
- lib/assets/javascripts/firehose/base.js.coffee
|
161
|
+
- lib/assets/javascripts/firehose/client.js.coffee
|
162
|
+
- lib/assets/javascripts/firehose/long_poll.js.coffee
|
163
|
+
- lib/assets/javascripts/firehose/transport.js.coffee
|
164
|
+
- lib/assets/javascripts/firehose/web_socket.js.coffee
|
157
165
|
- lib/firehose.rb
|
158
166
|
- lib/firehose/goliath.rb
|
159
167
|
- lib/firehose/http_publisher.rb
|
@@ -166,7 +174,7 @@ files:
|
|
166
174
|
- spec/integrations/goliath_spec.rb
|
167
175
|
- spec/integrations/thin_spec.rb
|
168
176
|
- spec/spec_helper.rb
|
169
|
-
homepage:
|
177
|
+
homepage: http://github.com/polleverywhere/firehose
|
170
178
|
licenses: []
|
171
179
|
post_install_message:
|
172
180
|
rdoc_options: []
|