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 CHANGED
@@ -9,26 +9,26 @@
9
9
 
10
10
  # What is Firehose?
11
11
 
12
- Firehose is both a Rack application and JavasSript library that makes building scalable real-time web applications possible.
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, firehose attempts to solve data consistency issues and authentication by encourage the use of proxying to the web application.
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 Consumer
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::Transport::Dispatcher.new do |config|
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 Producer
48
+ ## The Publisher
63
49
 
64
- Lets test the producer! Open two terminal windows. In one window, curl the consumer server:
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 script in another terminal:
56
+ Then run the following in the other terminal:
71
57
 
72
- ```ruby
73
- require 'rubygems'
74
- require 'firehose'
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
- Firehose::Producer.new.publish('hi there!').to('/greetings')
64
+ ```sh
65
+ Greetings fellow human being...
77
66
  ```
78
67
 
79
- ## JavaScript Client
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
- Then in your browser create a new Firehose Client object as such:
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://some_websocket_url.com',
87
- longpoll: 'http://some_longpoll_url.com'
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
- alert(msg); // Fires when a message is received from the server.
91
+ console.log(msg); // Fires when a message is received from the server.
97
92
  })
98
93
  .connected(function(){
99
- alert('Howdy friend!');
94
+ console.log('Howdy friend!');
100
95
  })
101
96
  .disconnected(function(){
102
- alert('Bu bye');
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@bradgessler.com"]
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
 
@@ -0,0 +1,5 @@
1
+ #= require firehose/base
2
+ #= require firehose/transport
3
+ #= require firehose/long_poll
4
+ #= require firehose/web_socket
5
+ #= require firehose/client
@@ -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
@@ -1,3 +1,4 @@
1
1
  module Firehose
2
- VERSION = "0.0.3"
3
- end
2
+ VERSION = "0.0.4"
3
+ CODENAME = "Trickle"
4
+ end
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.3
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: &70282339770020 !ruby/object:Gem::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: *70282339770020
25
+ version_requirements: *70267687581480
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: amqp
27
- requirement: &70282339769280 !ruby/object:Gem::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: *70282339769280
36
+ version_requirements: *70267687580600
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: thin
38
- requirement: &70282339768720 !ruby/object:Gem::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: *70282339768720
47
+ version_requirements: *70267687580200
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: websocket-rack
49
- requirement: &70282339768120 !ruby/object:Gem::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: *70282339768120
58
+ version_requirements: *70267687579660
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: rspec
60
- requirement: &70282339767660 !ruby/object:Gem::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: *70282339767660
69
+ version_requirements: *70267687579160
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: rack-test
71
- requirement: &70282339767140 !ruby/object:Gem::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: *70282339767140
80
+ version_requirements: *70267687578500
80
81
  - !ruby/object:Gem::Dependency
81
82
  name: guard-rspec
82
- requirement: &70282339766600 !ruby/object:Gem::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: *70282339766600
91
+ version_requirements: *70267687338120
91
92
  - !ruby/object:Gem::Dependency
92
93
  name: guard-bundler
93
- requirement: &70282339765920 !ruby/object:Gem::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: *70282339765920
102
+ version_requirements: *70267687337440
102
103
  - !ruby/object:Gem::Dependency
103
104
  name: thin
104
- requirement: &70282339752660 !ruby/object:Gem::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: *70282339752660
113
+ version_requirements: *70267687336400
113
114
  - !ruby/object:Gem::Dependency
114
115
  name: em-http-request
115
- requirement: &70282339751820 !ruby/object:Gem::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: *70282339751820
124
+ version_requirements: *70267687335300
124
125
  - !ruby/object:Gem::Dependency
125
126
  name: guard-coffeescript
126
- requirement: &70282339750760 !ruby/object:Gem::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: *70282339750760
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@bradgessler.com
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: []