firehose 0.0.3 → 0.0.4

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 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: []