firehose 1.2.20 → 2.1.0

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.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +36 -0
  3. data/{LICENSE.txt → LICENSE} +6 -6
  4. data/README.md +389 -139
  5. data/app/assets/javascripts/firehose.js +258 -0
  6. data/config/importmap.rb +1 -0
  7. data/db/migrate/20260214185244_create_firehose_events.rb +23 -0
  8. data/lib/commands/firehose_command.rb +119 -0
  9. data/lib/firehose/channel.rb +40 -0
  10. data/lib/firehose/cleanup_job.rb +21 -0
  11. data/lib/firehose/engine.rb +36 -0
  12. data/lib/firehose/helper.rb +40 -0
  13. data/lib/firehose/models/channel.rb +12 -0
  14. data/lib/firehose/models/message.rb +11 -0
  15. data/lib/firehose/queue.rb +44 -0
  16. data/lib/firehose/server.rb +931 -18
  17. data/lib/firehose/sse.rb +187 -0
  18. data/lib/firehose/stream.rb +32 -0
  19. data/lib/firehose/streamable.rb +25 -0
  20. data/lib/firehose/websocket.rb +242 -0
  21. data/lib/firehose.rb +67 -15
  22. data/lib/generators/firehose/install/install_generator.rb +43 -0
  23. data/lib/generators/firehose/install/templates/firehose_controller.rb +16 -0
  24. metadata +40 -428
  25. data/.env.sample +0 -17
  26. data/.gitignore +0 -12
  27. data/.rbenv-version +0 -1
  28. data/.rspec +0 -1
  29. data/.travis.yml +0 -16
  30. data/Gemfile +0 -4
  31. data/Guardfile +0 -31
  32. data/Procfile +0 -1
  33. data/Procfile.dev +0 -4
  34. data/Rakefile +0 -53
  35. data/bin/firehose +0 -9
  36. data/config/evergreen.rb +0 -9
  37. data/config/rainbows.rb +0 -17
  38. data/firehose.gemspec +0 -49
  39. data/lib/assets/javascripts/firehose/base.js.coffee +0 -4
  40. data/lib/assets/javascripts/firehose/consumer.js.coffee +0 -70
  41. data/lib/assets/javascripts/firehose/core.js.coffee +0 -5
  42. data/lib/assets/javascripts/firehose/long_poll.js.coffee +0 -149
  43. data/lib/assets/javascripts/firehose/transport.js.coffee +0 -42
  44. data/lib/assets/javascripts/firehose/version.js.coffee.erb +0 -2
  45. data/lib/assets/javascripts/firehose/web_socket.js.coffee +0 -102
  46. data/lib/assets/javascripts/firehose.js.coffee +0 -3
  47. data/lib/assets/javascripts/vendor/json2.js +0 -487
  48. data/lib/firehose/assets.rb +0 -56
  49. data/lib/firehose/cli.rb +0 -78
  50. data/lib/firehose/client/consumer.rb +0 -94
  51. data/lib/firehose/client/producer.rb +0 -108
  52. data/lib/firehose/client.rb +0 -10
  53. data/lib/firehose/logging.rb +0 -32
  54. data/lib/firehose/rack/app.rb +0 -49
  55. data/lib/firehose/rack/consumer/http_long_poll.rb +0 -86
  56. data/lib/firehose/rack/consumer/web_socket.rb +0 -95
  57. data/lib/firehose/rack/consumer.rb +0 -39
  58. data/lib/firehose/rack/ping.rb +0 -88
  59. data/lib/firehose/rack/publisher.rb +0 -53
  60. data/lib/firehose/rack.rb +0 -32
  61. data/lib/firehose/rails.rb +0 -7
  62. data/lib/firehose/server/app.rb +0 -52
  63. data/lib/firehose/server/channel.rb +0 -80
  64. data/lib/firehose/server/publisher.rb +0 -134
  65. data/lib/firehose/server/subscriber.rb +0 -51
  66. data/lib/firehose/version.rb +0 -4
  67. data/package.json +0 -5
  68. data/spec/integrations/integration_test_helper.rb +0 -22
  69. data/spec/integrations/rainbows_spec.rb +0 -7
  70. data/spec/integrations/shared_examples.rb +0 -120
  71. data/spec/integrations/thin_spec.rb +0 -7
  72. data/spec/javascripts/firehose/base_spec.coffee +0 -2
  73. data/spec/javascripts/firehose/consumer_spec.coffee +0 -45
  74. data/spec/javascripts/firehose/long_poll_spec.coffee +0 -44
  75. data/spec/javascripts/firehose/transport_spec.coffee +0 -32
  76. data/spec/javascripts/firehose/websocket_spec.coffee +0 -25
  77. data/spec/javascripts/helpers/spec_helper.js +0 -30
  78. data/spec/javascripts/support/sinon-1.7.3.js +0 -4290
  79. data/spec/lib/assets_spec.rb +0 -35
  80. data/spec/lib/client/consumer_spec.rb +0 -13
  81. data/spec/lib/client/producer_spec.rb +0 -71
  82. data/spec/lib/firehose_spec.rb +0 -7
  83. data/spec/lib/rack/consumer/http_long_poll_spec.rb +0 -12
  84. data/spec/lib/rack/consumer_spec.rb +0 -19
  85. data/spec/lib/rack/ping_spec.rb +0 -28
  86. data/spec/lib/rack/publisher_spec.rb +0 -34
  87. data/spec/lib/server/app_spec.rb +0 -15
  88. data/spec/lib/server/channel_spec.rb +0 -164
  89. data/spec/lib/server/publisher_spec.rb +0 -84
  90. data/spec/lib/server/subscriber_spec.rb +0 -69
  91. data/spec/spec_helper.rb +0 -77
data/config/evergreen.rb DELETED
@@ -1,9 +0,0 @@
1
- require 'capybara-webkit'
2
-
3
-
4
- Evergreen.configure do |config|
5
- config.driver = :webkit
6
- config.public_dir = 'public'
7
- # config.template_dir = 'spec/javascripts/templates'
8
- config.spec_dir = 'spec/javascripts'
9
- end
data/config/rainbows.rb DELETED
@@ -1,17 +0,0 @@
1
- # Configuration from http://rainbows.rubyforge.org/Rainbows/Configurator.html. Don't juse
2
- # blindly copy and paste this configuration! Be sure you have read and understand the Rainbows
3
- # configuration documentation for your environment.
4
-
5
- Rainbows! do
6
- use :EventMachine # concurrency model
7
- worker_connections 400
8
- keepalive_timeout 0 # disables keepalives
9
- keepalive_requests 666 # default:100
10
- client_max_body_size 5 * 1024 * 1024 # 5 megabytes
11
- client_header_buffer_size 2 * 1024 # 2 kilobytes
12
- end
13
-
14
- # the rest of the Unicorn configuration...
15
- worker_processes [ENV['WORKER_PROCESSES'].to_i, 1].max # Default to 1
16
- working_directory ENV['WORKING_DIRECTORY'] if ENV['WORKING_DIRECTORY']
17
- logger Firehose.logger
data/firehose.gemspec DELETED
@@ -1,49 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "firehose/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "firehose"
7
- s.version = Firehose::VERSION
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
- s.homepage = "http://firehose.io/"
11
- s.summary = %q{Build realtime Ruby web applications}
12
- s.description = %q{Firehose is a realtime web application toolkit for building realtime Ruby web applications.}
13
- s.license = "MIT"
14
-
15
- s.rubyforge_project = "firehose"
16
-
17
- s.files = `git ls-files`.split("\n")
18
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib"]
21
-
22
- # specify any dependencies here; for example:
23
- s.add_runtime_dependency "eventmachine", ">= 1.0.0"
24
- s.add_runtime_dependency "em-hiredis", ">= 0.2.0"
25
- s.add_runtime_dependency "thor"
26
- s.add_runtime_dependency "faraday"
27
- s.add_runtime_dependency "faye-websocket"
28
- s.add_runtime_dependency "em-http-request", ">= 1.0.0"
29
- s.add_runtime_dependency "json"
30
- s.add_runtime_dependency "rack"
31
-
32
- s.add_development_dependency "rspec", "~> 2"
33
- s.add_development_dependency "webmock"
34
- s.add_development_dependency "coffee-script"
35
- s.add_development_dependency "guard-rspec"
36
- s.add_development_dependency "guard-bundler"
37
- s.add_development_dependency "guard-coffeescript"
38
- s.add_development_dependency "guard-copy"
39
- s.add_development_dependency "rainbows", "~> 4.4.3"
40
- s.add_development_dependency "thin"
41
- s.add_development_dependency "rack-test"
42
- s.add_development_dependency "async_rack_test"
43
- s.add_development_dependency "foreman"
44
- s.add_development_dependency "sprockets"
45
- s.add_development_dependency "rake"
46
- s.add_development_dependency "evergreen"
47
- s.add_development_dependency "capybara-webkit"
48
- s.add_development_dependency "therubyracer"
49
- end
@@ -1,4 +0,0 @@
1
- if module?.exports?
2
- global.Firehose ||= {}
3
- else
4
- window.Firehose ||= {}
@@ -1,70 +0,0 @@
1
- class Firehose.Consumer
2
- constructor: (@config = {}) ->
3
- # Empty handler for messages.
4
- @config.message ||= ->
5
- # Empty handler for error handling.
6
- @config.error ||= ->
7
- # Empty handler for when we establish a connection.
8
- @config.connected ||= ->
9
- # Empty handler for when we're disconnected.
10
- @config.disconnected ||= ->
11
- # The initial connection failed. This is probably triggered when a
12
- # transport, like WebSockets is supported by the browser, but for whatever
13
- # reason it can't connect (probably a firewall)
14
- @config.failed ||= ->
15
- throw "Could not connect"
16
- # Params that we'll tack on to the URL.
17
- @config.params ||= {}
18
- # Do stuff before we send the message into config.message. The sensible
19
- # default on the webs is to parse JSON.
20
- @config.parse ||= JSON.parse
21
- # Make sure we return ourself out of the constructor so we can chain.
22
- this
23
-
24
- connect: (delay=0) =>
25
- promise = @_connectPromise()
26
-
27
- @config.connectionVerified = @_upgradeTransport
28
- if Firehose.WebSocket.supported()
29
- @upgradeTimeout = setTimeout =>
30
- ws = new Firehose.WebSocket @config
31
- ws.connect delay
32
- , 500
33
- @transport = new Firehose.LongPoll @config
34
- @transport.connect delay
35
-
36
- promise
37
-
38
- stop: =>
39
- if @upgradeTimeout?
40
- clearTimeout @upgradeTimeout
41
- @upgradeTimeout = null
42
- @transport.stop()
43
- return
44
-
45
- _upgradeTransport: (ws) =>
46
- @transport.stop()
47
- ws.sendStartingMessageSequence @transport.getLastMessageSequence()
48
- @transport = ws
49
- return
50
-
51
- # Return a promise that will succeed/fail depending on whether or not the
52
- # initial connection succeeds.
53
- _connectPromise: =>
54
- deferred = $.Deferred()
55
-
56
- origConnected = @config.connected
57
- @config.connected = =>
58
- deferred.resolve()
59
- if origConnected
60
- @config.connected = origConnected
61
- origConnected()
62
-
63
- # origDisconnected = @config.disconnected
64
- # @config.disconnected = =>
65
- # deferred.reject()
66
- # if origDisconnected
67
- # @config.disconnected = origDisconnected
68
- # origDisconnected()
69
-
70
- deferred.promise()
@@ -1,5 +0,0 @@
1
- #= require ./base
2
- #= require ./transport
3
- #= require ./long_poll
4
- #= require ./web_socket
5
- #= require ./consumer
@@ -1,149 +0,0 @@
1
- class Firehose.LongPoll extends Firehose.Transport
2
- messageSequenceHeader: 'Pragma'
3
- name: -> 'LongPoll'
4
-
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
- # $.browser.msie and parseInt($.browser.version) >= 8 # DEPRECATED
9
- @ieSupported: -> (document.documentMode || 10) >= 8
10
-
11
- @supported: ->
12
- # IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
13
- if xhr = $.ajaxSettings.xhr()
14
- "withCredentials" of xhr || Firehose.LongPoll.ieSupported()
15
-
16
- constructor: (args) ->
17
- super args
18
-
19
- @config.ssl ?= false
20
-
21
- # Configrations specifically for long polling
22
- @config.longPoll ||= {}
23
- @config.longPoll.url ||= "#{@_protocol()}:#{@config.uri}"
24
- # How many ms should we wait before timing out the AJAX connection?
25
- @config.longPoll.timeout ||= 25000
26
- # TODO - What is @_lagTime for? Can't we just use the @_timeout value?
27
- # We use the lag time to make the client live longer than the server.
28
- @_lagTime = 5000
29
- @_timeout = @config.longPoll.timeout + @_lagTime
30
- @_okInterval = @config.okInterval || 0
31
- @_stopRequestLoop = false
32
-
33
- # Protocol schema we should use for talking to firehose server.
34
- _protocol: =>
35
- if @config.ssl then "https" else "http"
36
-
37
- _request: =>
38
- return if @_stopRequestLoop
39
- # Set the Last Message Sequence in a query string.
40
- # Ideally we'd use an HTTP header, but android devices don't let us
41
- # set any HTTP headers for CORS requests.
42
- data = @config.params
43
- data.last_message_sequence = @_lastMessageSequence
44
- # TODO: Some of these options will be deprecated in jQuery 1.8
45
- # See: http://api.jquery.com/jQuery.ajax/#jqXHR
46
- @_lastRequest = $.ajax
47
- url: @config.longPoll.url
48
- firehose: true
49
- crossDomain: true
50
- data: data
51
- timeout: @_timeout
52
- success: @_success
53
- error: @_error
54
- cache: false
55
-
56
- stop: =>
57
- @_stopRequestLoop = true
58
- if @_lastRequest?
59
- try @_lastRequest.abort() catch e
60
- delete @_lastRequest
61
- if @_lastPingRequest?
62
- try @_lastPingRequest.abort() catch e
63
- delete @_lastPingRequest
64
-
65
- _success: (data, status, jqXhr) =>
66
- if @_needToNotifyOfReconnect or not @_succeeded
67
- @_needToNotifyOfReconnect = false
68
- @_open data
69
- return if @_stopRequestLoop
70
- if jqXhr.status is 200
71
- # Of course, IE's XDomainRequest doesn't support non-200 success codes.
72
- try
73
- {message, last_sequence} = JSON.parse jqXhr.responseText
74
- @_lastMessageSequence = last_sequence
75
- @config.message @config.parse message
76
- catch e
77
- @connect @_okInterval
78
-
79
- _ping: =>
80
- # Ping long poll server to verify internet connectivity
81
- # jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
82
- # directly so we can't manually set a timeout.
83
- @_lastPingRequest = $.ajax
84
- url: @config.longPoll.url
85
- method: 'HEAD'
86
- crossDomain: true
87
- firehose: true
88
- data: @config.params
89
- success: =>
90
- if @_needToNotifyOfReconnect
91
- @_needToNotifyOfReconnect = false
92
- @config.connected @
93
-
94
- # We need this custom handler to have the connection status
95
- # properly displayed
96
- _error: (jqXhr, status, error) =>
97
- unless @_needToNotifyOfReconnect or @_stopRequestLoop
98
- @_needToNotifyOfReconnect = true
99
- @config.disconnected()
100
- unless @_stopRequestLoop
101
- # Ping the server to make sure this isn't a network connectivity error
102
- setTimeout @_ping, @_retryDelay + @_lagTime
103
- # Reconnect with delay
104
- setTimeout @_request, @_retryDelay
105
-
106
- # Let's try to hack in support for IE8-9 via the XDomainRequest object!
107
- # This was adapted from code shamelessly stolen from:
108
- # https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
109
- if $?.browser?.msie and parseInt($.browser.version, 10) in [8, 9]
110
- jQuery.ajaxTransport (s) ->
111
- if s.crossDomain and s.async and s.firehose
112
- if s.timeout
113
- s.xdrTimeout = s.timeout
114
- delete s.timeout
115
- xdr = undefined
116
- return {
117
- send: (_, complete) ->
118
- callback = (status, statusText, responses, responseHeaders) ->
119
- xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
120
- xdr = undefined
121
- complete status, statusText, responses, responseHeaders
122
-
123
- xdr = new XDomainRequest()
124
-
125
- xdr.open s.type, s.url
126
-
127
- xdr.onload = ->
128
- headers = "Content-Type: #{xdr.contentType}"
129
- callback 200, "OK", {text: xdr.responseText}, headers
130
-
131
- xdr.onerror = -> callback 404, "Not Found"
132
-
133
- # This is critical for long poll to work in IE9.
134
- # Without it, the initial request will work but
135
- # subsequent requests will fail silently.
136
- # http://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
137
- xdr.onprogress = -> {}
138
-
139
- if s.xdrTimeout?
140
- xdr.ontimeout = -> callback 0, "timeout"
141
- xdr.timeout = s.xdrTimeout
142
-
143
- xdr.send (s.hasContent and s.data) or null
144
-
145
- abort: ->
146
- if xdr?
147
- xdr.onerror = jQuery.noop()
148
- xdr.abort()
149
- }
@@ -1,42 +0,0 @@
1
- class Firehose.Transport
2
- # Class method to determine whether transport is supported by the current browser. Note that while
3
- # the transport may be supported by the browser, its possible that the network connection won't
4
- # succeed. That should be accounted for during the initial connecting to the server.
5
- @supported: =>
6
- false
7
-
8
- constructor: (config={}) ->
9
- @config = config
10
- @_retryDelay = 3000
11
- @
12
-
13
- # Lets rock'n'roll! Connect to the server.
14
- connect: (delay = 0) =>
15
- setTimeout @_request, delay
16
- this
17
-
18
- # Hey subclasses:
19
- name: -> throw 'not implemented in base Transport' # implement this to identify transport type
20
- stop: -> throw 'not implemented in base Transport' # implement this to stop receiving messages
21
- _request: -> throw 'not implemented in base Transport' # implement this to handle requests
22
-
23
- # Default error handler
24
- _error: (event) =>
25
- if @_succeeded
26
- # Lets try to connect again with delay
27
- @config.disconnected()
28
- @connect(@_retryDelay)
29
- else @config.failed @
30
-
31
- # Default connection established handler
32
- _open: (event) =>
33
- @_succeeded = true
34
- @config.connected(@)
35
-
36
- # Default connection closed handler
37
- _close: (event) =>
38
- @config.disconnected()
39
-
40
- # Useful for reconnecting after any networking hiccups
41
- getLastMessageSequence: =>
42
- @_lastMessageSequence or 0
@@ -1,2 +0,0 @@
1
- Firehose.version = "<%= Firehose::VERSION %>"
2
- Firehose.codeName = "<%= Firehose::CODENAME %>"
@@ -1,102 +0,0 @@
1
- INITIAL_PING_TIMEOUT = 2000
2
- KEEPALIVE_PING_TIMEOUT = 20000
3
-
4
- class Firehose.WebSocket extends Firehose.Transport
5
- name: -> 'WebSocket'
6
-
7
- @ieSupported:-> (document.documentMode || 10) > 9
8
- @supported :-> (if module?.exports? then global else window).WebSocket? # Check if WebSocket is an object in the window.
9
-
10
- constructor: (args) ->
11
- super args
12
- # Configrations specifically for web sockets
13
- @config.webSocket ||= {}
14
- @config.webSocket.connectionVerified = @config.connectionVerified
15
-
16
- _request: =>
17
- # Run this is a try/catch block because IE10 inside of a .NET control
18
- # complains about security zones.
19
- try
20
- @socket = new (if module?.exports? then global else window).WebSocket "#{@_protocol()}:#{@config.uri}?#{$.param @config.params}"
21
- @socket.onopen = @_open
22
- @socket.onclose = @_close
23
- @socket.onerror = @_error
24
- @socket.onmessage = @_lookForInitialPong
25
- catch err
26
- console?.log(err)
27
-
28
- # Protocol schema we should use for talking to firehose server.
29
- _protocol: =>
30
- if @config.ssl then "wss" else "ws"
31
-
32
- _open: =>
33
- sendPing @socket
34
-
35
- _lookForInitialPong: (event) =>
36
- @_restartKeepAlive()
37
- if isPong(try JSON.parse event.data catch e then {})
38
- if @_lastMessageSequence?
39
- # don't callback to connectionVerified on subsequent reconnects
40
- @sendStartingMessageSequence @_lastMessageSequence
41
- else @config.webSocket.connectionVerified @
42
-
43
- sendStartingMessageSequence: (message_sequence) =>
44
- @_lastMessageSequence = message_sequence
45
- @socket.onmessage = @_message
46
- @socket.send JSON.stringify {message_sequence}
47
- @_needToNotifyOfDisconnect = true
48
- Firehose.Transport::_open.call @
49
-
50
- stop: =>
51
- @_cleanUp()
52
-
53
- _message: (event) =>
54
- frame = @config.parse event.data
55
- @_restartKeepAlive()
56
- unless isPong frame
57
- try
58
- @_lastMessageSequence = frame.last_sequence
59
- @config.message @config.parse frame.message
60
- catch e
61
-
62
- _close: (event) =>
63
- if event?.wasClean then @_cleanUp()
64
- else @_error event
65
-
66
- _error: (event) =>
67
- @_cleanUp()
68
- if @_needToNotifyOfDisconnect
69
- @_needToNotifyOfDisconnect = false
70
- @config.disconnected()
71
- if @_succeeded then @connect @_retryDelay
72
- else @config.failed @
73
-
74
- _cleanUp: =>
75
- @_clearKeepalive()
76
- if @socket?
77
- @socket.onopen = null
78
- @socket.onclose = null
79
- @socket.onerror = null
80
- @socket.onmessage = null
81
- @socket.close()
82
- delete @socket
83
-
84
- _restartKeepAlive: =>
85
- doPing = =>
86
- sendPing @socket
87
- setNextKeepAlive()
88
- setNextKeepAlive = =>
89
- @keepaliveTimeout = setTimeout doPing, KEEPALIVE_PING_TIMEOUT
90
- @_clearKeepalive()
91
- setNextKeepAlive()
92
-
93
- _clearKeepalive: =>
94
- if @keepaliveTimeout?
95
- clearTimeout @keepaliveTimeout
96
- @keepaliveTimeout = null
97
-
98
- sendPing = (socket) ->
99
- socket.send JSON.stringify ping: 'PING'
100
-
101
- isPong = (o) ->
102
- o.pong is 'PONG'
@@ -1,3 +0,0 @@
1
- #= require firehose/core
2
- #= require firehose/version
3
- #= require vendor/json2