actioncable 5.0.0.beta1.1 → 5.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -3
- data/MIT-LICENSE +1 -1
- data/README.md +60 -44
- data/lib/action_cable.rb +2 -1
- data/lib/action_cable/channel/base.rb +2 -2
- data/lib/action_cable/channel/periodic_timers.rb +3 -3
- data/lib/action_cable/channel/streams.rb +4 -4
- data/lib/action_cable/connection.rb +4 -1
- data/lib/action_cable/connection/base.rb +22 -21
- data/lib/action_cable/connection/client_socket.rb +150 -0
- data/lib/action_cable/connection/identification.rb +1 -1
- data/lib/action_cable/connection/internal_channel.rb +6 -6
- data/lib/action_cable/connection/stream.rb +59 -0
- data/lib/action_cable/connection/stream_event_loop.rb +94 -0
- data/lib/action_cable/connection/subscriptions.rb +0 -1
- data/lib/action_cable/connection/web_socket.rb +14 -8
- data/lib/action_cable/engine.rb +3 -3
- data/lib/action_cable/gem_version.rb +1 -1
- data/lib/action_cable/remote_connections.rb +1 -1
- data/lib/action_cable/server.rb +0 -4
- data/lib/action_cable/server/base.rb +19 -22
- data/lib/action_cable/server/broadcasting.rb +1 -8
- data/lib/action_cable/server/configuration.rb +25 -5
- data/lib/action_cable/server/connections.rb +3 -5
- data/lib/action_cable/server/worker.rb +42 -13
- data/lib/action_cable/subscription_adapter.rb +8 -0
- data/lib/action_cable/subscription_adapter/async.rb +22 -0
- data/lib/action_cable/subscription_adapter/base.rb +28 -0
- data/lib/action_cable/subscription_adapter/evented_redis.rb +67 -0
- data/lib/action_cable/subscription_adapter/inline.rb +35 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +106 -0
- data/lib/action_cable/subscription_adapter/redis.rb +163 -0
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +53 -0
- data/lib/assets/compiled/action_cable.js +473 -0
- data/lib/rails/generators/channel/channel_generator.rb +6 -1
- metadata +21 -99
- data/lib/action_cable/process/logging.rb +0 -10
- data/lib/assets/javascripts/action_cable.coffee.erb +0 -23
- data/lib/assets/javascripts/action_cable/connection.coffee +0 -84
- data/lib/assets/javascripts/action_cable/connection_monitor.coffee +0 -84
- data/lib/assets/javascripts/action_cable/consumer.coffee +0 -31
- data/lib/assets/javascripts/action_cable/subscription.coffee +0 -68
- data/lib/assets/javascripts/action_cable/subscriptions.coffee +0 -78
@@ -5,11 +5,16 @@ module Rails
|
|
5
5
|
|
6
6
|
argument :actions, type: :array, default: [], banner: "method method"
|
7
7
|
|
8
|
+
class_option :assets, type: :boolean
|
9
|
+
|
8
10
|
check_class_collision suffix: "Channel"
|
9
11
|
|
10
12
|
def create_channel_file
|
11
13
|
template "channel.rb", File.join('app/channels', class_path, "#{file_name}_channel.rb")
|
12
|
-
|
14
|
+
|
15
|
+
if options[:assets]
|
16
|
+
template "assets/channel.coffee", File.join('app/assets/javascripts/channels', class_path, "#{file_name}.coffee")
|
17
|
+
end
|
13
18
|
end
|
14
19
|
|
15
20
|
protected
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actioncable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pratik Naik
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-01
|
12
|
+
date: 2016-02-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -17,42 +17,28 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - '='
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 5.0.0.
|
20
|
+
version: 5.0.0.beta2
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - '='
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 5.0.0.
|
27
|
+
version: 5.0.0.beta2
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: nio4r
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
34
|
+
version: '1.2'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: faye-websocket
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - "~>"
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: 0.10.0
|
49
|
-
type: :runtime
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - "~>"
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: 0.10.0
|
41
|
+
version: '1.2'
|
56
42
|
- !ruby/object:Gem::Dependency
|
57
43
|
name: websocket-driver
|
58
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,76 +53,6 @@ dependencies:
|
|
67
53
|
- - "~>"
|
68
54
|
- !ruby/object:Gem::Version
|
69
55
|
version: 0.6.1
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: celluloid
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - "~>"
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: 0.17.2
|
77
|
-
type: :runtime
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - "~>"
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: 0.17.2
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: em-hiredis
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: 0.3.0
|
91
|
-
type: :runtime
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - "~>"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: 0.3.0
|
98
|
-
- !ruby/object:Gem::Dependency
|
99
|
-
name: redis
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - "~>"
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
version: '3.0'
|
105
|
-
type: :runtime
|
106
|
-
prerelease: false
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - "~>"
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
version: '3.0'
|
112
|
-
- !ruby/object:Gem::Dependency
|
113
|
-
name: puma
|
114
|
-
requirement: !ruby/object:Gem::Requirement
|
115
|
-
requirements:
|
116
|
-
- - ">="
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
version: '0'
|
119
|
-
type: :development
|
120
|
-
prerelease: false
|
121
|
-
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
requirements:
|
123
|
-
- - ">="
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '0'
|
126
|
-
- !ruby/object:Gem::Dependency
|
127
|
-
name: mocha
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
-
requirements:
|
130
|
-
- - ">="
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: '0'
|
133
|
-
type: :development
|
134
|
-
prerelease: false
|
135
|
-
version_requirements: !ruby/object:Gem::Requirement
|
136
|
-
requirements:
|
137
|
-
- - ">="
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
version: '0'
|
140
56
|
description: Structure many real-time application concerns into channels over a single
|
141
57
|
WebSocket connection.
|
142
58
|
email:
|
@@ -160,16 +76,18 @@ files:
|
|
160
76
|
- lib/action_cable/connection.rb
|
161
77
|
- lib/action_cable/connection/authorization.rb
|
162
78
|
- lib/action_cable/connection/base.rb
|
79
|
+
- lib/action_cable/connection/client_socket.rb
|
163
80
|
- lib/action_cable/connection/identification.rb
|
164
81
|
- lib/action_cable/connection/internal_channel.rb
|
165
82
|
- lib/action_cable/connection/message_buffer.rb
|
83
|
+
- lib/action_cable/connection/stream.rb
|
84
|
+
- lib/action_cable/connection/stream_event_loop.rb
|
166
85
|
- lib/action_cable/connection/subscriptions.rb
|
167
86
|
- lib/action_cable/connection/tagged_logger_proxy.rb
|
168
87
|
- lib/action_cable/connection/web_socket.rb
|
169
88
|
- lib/action_cable/engine.rb
|
170
89
|
- lib/action_cable/gem_version.rb
|
171
90
|
- lib/action_cable/helpers/action_cable_helper.rb
|
172
|
-
- lib/action_cable/process/logging.rb
|
173
91
|
- lib/action_cable/remote_connections.rb
|
174
92
|
- lib/action_cable/server.rb
|
175
93
|
- lib/action_cable/server/base.rb
|
@@ -178,13 +96,16 @@ files:
|
|
178
96
|
- lib/action_cable/server/connections.rb
|
179
97
|
- lib/action_cable/server/worker.rb
|
180
98
|
- lib/action_cable/server/worker/active_record_connection_management.rb
|
99
|
+
- lib/action_cable/subscription_adapter.rb
|
100
|
+
- lib/action_cable/subscription_adapter/async.rb
|
101
|
+
- lib/action_cable/subscription_adapter/base.rb
|
102
|
+
- lib/action_cable/subscription_adapter/evented_redis.rb
|
103
|
+
- lib/action_cable/subscription_adapter/inline.rb
|
104
|
+
- lib/action_cable/subscription_adapter/postgresql.rb
|
105
|
+
- lib/action_cable/subscription_adapter/redis.rb
|
106
|
+
- lib/action_cable/subscription_adapter/subscriber_map.rb
|
181
107
|
- lib/action_cable/version.rb
|
182
|
-
- lib/assets/
|
183
|
-
- lib/assets/javascripts/action_cable/connection.coffee
|
184
|
-
- lib/assets/javascripts/action_cable/connection_monitor.coffee
|
185
|
-
- lib/assets/javascripts/action_cable/consumer.coffee
|
186
|
-
- lib/assets/javascripts/action_cable/subscription.coffee
|
187
|
-
- lib/assets/javascripts/action_cable/subscriptions.coffee
|
108
|
+
- lib/assets/compiled/action_cable.js
|
188
109
|
- lib/rails/generators/channel/USAGE
|
189
110
|
- lib/rails/generators/channel/channel_generator.rb
|
190
111
|
- lib/rails/generators/channel/templates/assets/channel.coffee
|
@@ -209,8 +130,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
130
|
version: 1.3.1
|
210
131
|
requirements: []
|
211
132
|
rubyforge_project:
|
212
|
-
rubygems_version: 2.5.
|
133
|
+
rubygems_version: 2.5.2
|
213
134
|
signing_key:
|
214
135
|
specification_version: 4
|
215
136
|
summary: WebSocket framework for Rails.
|
216
137
|
test_files: []
|
138
|
+
has_rdoc:
|
@@ -1,23 +0,0 @@
|
|
1
|
-
#= require_self
|
2
|
-
#= require action_cable/consumer
|
3
|
-
|
4
|
-
@ActionCable =
|
5
|
-
INTERNAL: <%= ActionCable::INTERNAL.to_json %>
|
6
|
-
|
7
|
-
createConsumer: (url = @getConfig("url")) ->
|
8
|
-
new ActionCable.Consumer @createWebSocketURL(url)
|
9
|
-
|
10
|
-
getConfig: (name) ->
|
11
|
-
element = document.head.querySelector("meta[name='action-cable-#{name}']")
|
12
|
-
element?.getAttribute("content")
|
13
|
-
|
14
|
-
createWebSocketURL: (url) ->
|
15
|
-
if url and not /^wss?:/i.test(url)
|
16
|
-
a = document.createElement("a")
|
17
|
-
a.href = url
|
18
|
-
# Fix populating Location properties in IE. Otherwise, protocol will be blank.
|
19
|
-
a.href = a.href
|
20
|
-
a.protocol = a.protocol.replace("http", "ws")
|
21
|
-
a.href
|
22
|
-
else
|
23
|
-
url
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# Encapsulate the cable connection held by the consumer. This is an internal class not intended for direct user manipulation.
|
2
|
-
|
3
|
-
{message_types} = ActionCable.INTERNAL
|
4
|
-
|
5
|
-
class ActionCable.Connection
|
6
|
-
@reopenDelay: 500
|
7
|
-
|
8
|
-
constructor: (@consumer) ->
|
9
|
-
@open()
|
10
|
-
|
11
|
-
send: (data) ->
|
12
|
-
if @isOpen()
|
13
|
-
@webSocket.send(JSON.stringify(data))
|
14
|
-
true
|
15
|
-
else
|
16
|
-
false
|
17
|
-
|
18
|
-
open: =>
|
19
|
-
if @webSocket and not @isState("closed")
|
20
|
-
throw new Error("Existing connection must be closed before opening")
|
21
|
-
else
|
22
|
-
@webSocket = new WebSocket(@consumer.url)
|
23
|
-
@installEventHandlers()
|
24
|
-
true
|
25
|
-
|
26
|
-
close: ->
|
27
|
-
@webSocket?.close()
|
28
|
-
|
29
|
-
reopen: ->
|
30
|
-
if @isState("closed")
|
31
|
-
@open()
|
32
|
-
else
|
33
|
-
try
|
34
|
-
@close()
|
35
|
-
finally
|
36
|
-
setTimeout(@open, @constructor.reopenDelay)
|
37
|
-
|
38
|
-
isOpen: ->
|
39
|
-
@isState("open")
|
40
|
-
|
41
|
-
# Private
|
42
|
-
|
43
|
-
isState: (states...) ->
|
44
|
-
@getState() in states
|
45
|
-
|
46
|
-
getState: ->
|
47
|
-
return state.toLowerCase() for state, value of WebSocket when value is @webSocket?.readyState
|
48
|
-
null
|
49
|
-
|
50
|
-
installEventHandlers: ->
|
51
|
-
for eventName of @events
|
52
|
-
handler = @events[eventName].bind(this)
|
53
|
-
@webSocket["on#{eventName}"] = handler
|
54
|
-
return
|
55
|
-
|
56
|
-
events:
|
57
|
-
message: (event) ->
|
58
|
-
{identifier, message, type} = JSON.parse(event.data)
|
59
|
-
|
60
|
-
switch type
|
61
|
-
when message_types.confirmation
|
62
|
-
@consumer.subscriptions.notify(identifier, "connected")
|
63
|
-
when message_types.rejection
|
64
|
-
@consumer.subscriptions.reject(identifier)
|
65
|
-
else
|
66
|
-
@consumer.subscriptions.notify(identifier, "received", message)
|
67
|
-
|
68
|
-
open: ->
|
69
|
-
@disconnected = false
|
70
|
-
@consumer.subscriptions.reload()
|
71
|
-
|
72
|
-
close: ->
|
73
|
-
@disconnect()
|
74
|
-
|
75
|
-
error: ->
|
76
|
-
@disconnect()
|
77
|
-
|
78
|
-
disconnect: ->
|
79
|
-
return if @disconnected
|
80
|
-
@disconnected = true
|
81
|
-
@consumer.subscriptions.notifyAll("disconnected")
|
82
|
-
|
83
|
-
toJSON: ->
|
84
|
-
state: @getState()
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# Responsible for ensuring the cable connection is in good health by validating the heartbeat pings sent from the server, and attempting
|
2
|
-
# revival reconnections if things go astray. Internal class, not intended for direct user manipulation.
|
3
|
-
class ActionCable.ConnectionMonitor
|
4
|
-
@pollInterval:
|
5
|
-
min: 3
|
6
|
-
max: 30
|
7
|
-
|
8
|
-
@staleThreshold: 6 # Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
|
9
|
-
|
10
|
-
identifier: ActionCable.INTERNAL.identifiers.ping
|
11
|
-
|
12
|
-
constructor: (@consumer) ->
|
13
|
-
@consumer.subscriptions.add(this)
|
14
|
-
@start()
|
15
|
-
|
16
|
-
connected: ->
|
17
|
-
@reset()
|
18
|
-
@pingedAt = now()
|
19
|
-
delete @disconnectedAt
|
20
|
-
|
21
|
-
disconnected: ->
|
22
|
-
@disconnectedAt = now()
|
23
|
-
|
24
|
-
received: ->
|
25
|
-
@pingedAt = now()
|
26
|
-
|
27
|
-
reset: ->
|
28
|
-
@reconnectAttempts = 0
|
29
|
-
|
30
|
-
start: ->
|
31
|
-
@reset()
|
32
|
-
delete @stoppedAt
|
33
|
-
@startedAt = now()
|
34
|
-
@poll()
|
35
|
-
document.addEventListener("visibilitychange", @visibilityDidChange)
|
36
|
-
|
37
|
-
stop: ->
|
38
|
-
@stoppedAt = now()
|
39
|
-
document.removeEventListener("visibilitychange", @visibilityDidChange)
|
40
|
-
|
41
|
-
poll: ->
|
42
|
-
setTimeout =>
|
43
|
-
unless @stoppedAt
|
44
|
-
@reconnectIfStale()
|
45
|
-
@poll()
|
46
|
-
, @getInterval()
|
47
|
-
|
48
|
-
getInterval: ->
|
49
|
-
{min, max} = @constructor.pollInterval
|
50
|
-
interval = 5 * Math.log(@reconnectAttempts + 1)
|
51
|
-
clamp(interval, min, max) * 1000
|
52
|
-
|
53
|
-
reconnectIfStale: ->
|
54
|
-
if @connectionIsStale()
|
55
|
-
@reconnectAttempts++
|
56
|
-
unless @disconnectedRecently()
|
57
|
-
@consumer.connection.reopen()
|
58
|
-
|
59
|
-
connectionIsStale: ->
|
60
|
-
secondsSince(@pingedAt ? @startedAt) > @constructor.staleThreshold
|
61
|
-
|
62
|
-
disconnectedRecently: ->
|
63
|
-
@disconnectedAt and secondsSince(@disconnectedAt) < @constructor.staleThreshold
|
64
|
-
|
65
|
-
visibilityDidChange: =>
|
66
|
-
if document.visibilityState is "visible"
|
67
|
-
setTimeout =>
|
68
|
-
if @connectionIsStale() or not @consumer.connection.isOpen()
|
69
|
-
@consumer.connection.reopen()
|
70
|
-
, 200
|
71
|
-
|
72
|
-
toJSON: ->
|
73
|
-
interval = @getInterval()
|
74
|
-
connectionIsStale = @connectionIsStale()
|
75
|
-
{@startedAt, @stoppedAt, @pingedAt, @reconnectAttempts, connectionIsStale, interval}
|
76
|
-
|
77
|
-
now = ->
|
78
|
-
new Date().getTime()
|
79
|
-
|
80
|
-
secondsSince = (time) ->
|
81
|
-
(now() - time) / 1000
|
82
|
-
|
83
|
-
clamp = (number, min, max) ->
|
84
|
-
Math.max(min, Math.min(max, number))
|
@@ -1,31 +0,0 @@
|
|
1
|
-
#= require action_cable/connection
|
2
|
-
#= require action_cable/connection_monitor
|
3
|
-
#= require action_cable/subscriptions
|
4
|
-
#= require action_cable/subscription
|
5
|
-
|
6
|
-
# The ActionCable.Consumer establishes the connection to a server-side Ruby Connection object. Once established,
|
7
|
-
# the ActionCable.ConnectionMonitor will ensure that its properly maintained through heartbeats and checking for stale updates.
|
8
|
-
# The Consumer instance is also the gateway to establishing subscriptions to desired channels through the #createSubscription
|
9
|
-
# method.
|
10
|
-
#
|
11
|
-
# The following example shows how this can be setup:
|
12
|
-
#
|
13
|
-
# @App = {}
|
14
|
-
# App.cable = ActionCable.createConsumer "ws://example.com/accounts/1"
|
15
|
-
# App.appearance = App.cable.subscriptions.create "AppearanceChannel"
|
16
|
-
#
|
17
|
-
# For more details on how you'd configure an actual channel subscription, see ActionCable.Subscription.
|
18
|
-
class ActionCable.Consumer
|
19
|
-
constructor: (@url) ->
|
20
|
-
@subscriptions = new ActionCable.Subscriptions this
|
21
|
-
@connection = new ActionCable.Connection this
|
22
|
-
@connectionMonitor = new ActionCable.ConnectionMonitor this
|
23
|
-
|
24
|
-
send: (data) ->
|
25
|
-
@connection.send(data)
|
26
|
-
|
27
|
-
inspect: ->
|
28
|
-
JSON.stringify(this, null, 2)
|
29
|
-
|
30
|
-
toJSON: ->
|
31
|
-
{@url, @subscriptions, @connection, @connectionMonitor}
|