action_cable_client 1.3.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -9
- data/LICENSE +21 -0
- data/README.md +39 -0
- data/lib/action_cable_client.rb +47 -78
- data/lib/action_cable_client/errors.rb +7 -0
- data/lib/action_cable_client/message.rb +1 -0
- data/lib/action_cable_client/message_factory.rb +15 -10
- data/lib/action_cable_client/version.rb +2 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4efe4e0d3cbf2308ee5deebf64db87704a4518c
|
4
|
+
data.tar.gz: f72fa641ad54554816874825eb0f924eaa033cef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8ccddc722216d3ac5533734f4ac42e6541d74eb4c7c668ac0774f702674d813057cd39679a1ba0e11b14a7baa7f185a213f0c3a171b889f29ae8384b5567f62
|
7
|
+
data.tar.gz: 6fe25aeb39115aebed3e2caaa76b796e6ec766af521fd323e98cf770168de83a5edf1219e3ca0868fe1590c69b4be49ddff620b1a54bd09c0a6f5cc9b9e7a3d4
|
data/CHANGELOG.md
CHANGED
@@ -1,26 +1,41 @@
|
|
1
|
+
## 2.0 - Unreleased
|
2
|
+
|
3
|
+
**General**
|
4
|
+
|
5
|
+
* [#18](https://github.com/NullVoxPopuli/action_cable_client/pull/18) Added the ability to reconnect (@NullVoxPopuli)
|
6
|
+
* [#19](https://github.com/NullVoxPopuli/action_cable_client/pull/19) Allow for additional params via the identifier (@mcary, @NullVoxPopuli)
|
7
|
+
* Support ruby-2.4.x
|
8
|
+
* [#20](https://github.com/NullVoxPopuli/action_cable_client/pull/20) Change underlying websocket gem to [websocket-eventmachine-client](https://github.com/imanel/websocket-eventmachine-client)
|
9
|
+
* enables SSL
|
10
|
+
* allows header usage on handshake
|
11
|
+
|
12
|
+
**Breaking**
|
13
|
+
* [#19](https://github.com/NullVoxPopuli/action_cable_client/pull/19) Removed queued_send in initializer - this allows for a action_cable_client to be simpler, and stay an true to real-time communication as possible -- also it wasn't being used. (@NullVoxPopuli)
|
14
|
+
* Drop Support for ruby-2.2.x
|
15
|
+
|
1
16
|
## 1.3.4
|
2
17
|
* [#7](https://github.com/NullVoxPopuli/action_cable_client/pull/7) Avoid crashing on empty JSON data (@MikeAski)
|
3
18
|
|
4
19
|
## 1.3.2
|
5
|
-
* Getting disconnected from the server will now set the result of subscribed? to false
|
20
|
+
* Getting disconnected from the server will now set the result of subscribed? to false (@NullVoxPopuli)
|
6
21
|
|
7
22
|
## 1.3.0
|
8
|
-
* subscribed now is a callback instead of a boolean
|
9
|
-
* subscribed? tells whether or not the client is subscribed to the channel
|
10
|
-
* added subscribed callback which signifies when the client can start sending messages on the channel
|
23
|
+
* subscribed now is a callback instead of a boolean (@NullVoxPopuli)
|
24
|
+
* subscribed? tells whether or not the client is subscribed to the channel (@NullVoxPopuli)
|
25
|
+
* added subscribed callback which signifies when the client can start sending messages on the channel (@NullVoxPopuli)
|
11
26
|
|
12
27
|
## 1.2.4
|
13
28
|
* [#3](https://github.com/NullVoxPopuli/action_cable_client/pull/3) Support Ruby 2.2.2 (@NullVoxPopuli)
|
14
29
|
|
15
30
|
## 1.2.3
|
16
|
-
* The ping message received from the action cable server changed from being identity: _ping to type: ping
|
17
|
-
* Fixed an issue where subscribing sometimes didn't work.
|
31
|
+
* The ping message received from the action cable server changed from being identity: \_ping to type: ping (@NullVoxPopuli)
|
32
|
+
* Fixed an issue where subscribing sometimes didn't work. (@NullVoxPopuli)
|
18
33
|
|
19
34
|
## 1.2.0
|
20
|
-
* Made the handling of received messages not all happen in one method. This allows for easier overriding of what is yielded, in case someone wants to also yield the URL for example.
|
35
|
+
* Made the handling of received messages not all happen in one method. This allows for easier overriding of what is yielded, in case someone wants to also yield the URL for example. (@NullVoxPopuli)
|
21
36
|
|
22
37
|
## 1.1.0
|
23
|
-
* Made message queuing optional, off by default. This allows for near-instant message sending
|
38
|
+
* Made message queuing optional, off by default. This allows for near-instant message sending (@NullVoxPopuli)
|
24
39
|
|
25
40
|
## 1.0
|
26
|
-
* Initial Work
|
41
|
+
* Initial Work (@NullVoxPopuli)
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 L. Preston Sego III
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -46,6 +46,45 @@ The available hooks to tie in to are:
|
|
46
46
|
- `errored { |msg| }`
|
47
47
|
- `received { |msg }`
|
48
48
|
|
49
|
+
|
50
|
+
#### Connecting on initialization is also configurable.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
client = ActionCableClient.new(uri, 'RoomChannel', connect_on_start: false)
|
54
|
+
client.connect!
|
55
|
+
```
|
56
|
+
|
57
|
+
this way if you also enable ping receiving via
|
58
|
+
```ruby
|
59
|
+
client.received(false) do |json|
|
60
|
+
# now pings will be here as well, because skip_pings is set to false
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
you could track the time since you last received a ping, if you haven't received one in a while, it could be that your client is disconnected.
|
65
|
+
|
66
|
+
To reconnect,
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
client.connect!
|
70
|
+
```
|
71
|
+
|
72
|
+
#### Sending additional params
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
params = { channel: 'RoomChannel', favorite_color: 'blue' }
|
76
|
+
client = ActionCableClient.new(uri, params)
|
77
|
+
```
|
78
|
+
|
79
|
+
then on the server end, in your Channel, `params` will give you:
|
80
|
+
```
|
81
|
+
{
|
82
|
+
"channel" => "RoomChannel",
|
83
|
+
"favorite_color" => "blue"
|
84
|
+
}
|
85
|
+
```
|
86
|
+
|
87
|
+
|
49
88
|
## Demo
|
50
89
|
|
51
90
|
[![Live Demo](http://img.youtube.com/vi/x9D1wWsVHMY/mqdefault.jpg)](http://www.youtube.com/watch?v=x9D1wWsVHMY&hd=1)
|
data/lib/action_cable_client.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# required gems
|
3
|
-
require '
|
4
|
+
require 'websocket-eventmachine-client'
|
4
5
|
require 'forwardable'
|
5
6
|
require 'active_support/core_ext/string'
|
6
7
|
require 'json'
|
7
8
|
|
8
9
|
# local files
|
10
|
+
require 'action_cable_client/errors'
|
9
11
|
require 'action_cable_client/message_factory'
|
10
12
|
require 'action_cable_client/message'
|
11
13
|
|
@@ -17,44 +19,52 @@ class ActionCableClient
|
|
17
19
|
MESSAGE = 'message'
|
18
20
|
end
|
19
21
|
|
20
|
-
attr_reader :_websocket_client, :_uri
|
22
|
+
attr_reader :_websocket_client, :_uri
|
21
23
|
attr_reader :_message_factory
|
22
24
|
# The queue should store entries in the format:
|
23
25
|
# [ action, data ]
|
24
26
|
attr_accessor :message_queue, :_subscribed, :_subscribed_callaback
|
25
27
|
|
26
|
-
def_delegator :_websocket_client, :
|
27
|
-
def_delegator :_websocket_client, :
|
28
|
+
def_delegator :_websocket_client, :onerror, :errored
|
29
|
+
def_delegator :_websocket_client, :send, :send_msg
|
28
30
|
|
29
31
|
# @param [String] uri - e.g.: ws://domain:port
|
30
|
-
# @param [String]
|
32
|
+
# @param [String] params - the name of the channel on the Rails server
|
33
|
+
# or params. This gets sent with every request.
|
31
34
|
# e.g.: RoomChannel
|
32
|
-
# @param [Boolean]
|
33
|
-
#
|
34
|
-
|
35
|
-
|
35
|
+
# @param [Boolean] connect_on_start - connects on init when true
|
36
|
+
# - otherwise manually call `connect!`
|
37
|
+
# @param [Hash] headers - HTTP headers to use in the handshake
|
38
|
+
def initialize(uri, params = '', connect_on_start = true, headers = {})
|
36
39
|
@_uri = uri
|
37
|
-
@_queued_send = queued_send
|
38
40
|
@message_queue = []
|
39
41
|
@_subscribed = false
|
40
42
|
|
41
|
-
@_message_factory = MessageFactory.new(
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
@_message_factory = MessageFactory.new(params)
|
44
|
+
|
45
|
+
connect!(headers) if connect_on_start
|
46
|
+
end
|
47
|
+
|
48
|
+
def connect!(headers = {})
|
49
|
+
# Quick Reference for WebSocket::EM::Client's api
|
50
|
+
# - onopen - called after successfully connecting
|
51
|
+
# - onclose - called after closing connection
|
52
|
+
# - onmessage - called when client recives a message. on `message do |msg, type (text or binary)|``
|
53
|
+
# - also called when a ping is received
|
54
|
+
# - onerror - called when client encounters an error
|
55
|
+
# - onping - called when client receives a ping from the server
|
56
|
+
# - onpong - called when client receives a pong from the server
|
57
|
+
# - send - sends a message to the server (and also disables any metaprogramming shenanigans :-/)
|
58
|
+
# - close - closes the connection and optionally sends close frame to server. `close(code, data)`
|
59
|
+
# - ping - sends a ping
|
60
|
+
# - pong - sends a pong
|
61
|
+
@_websocket_client = WebSocket::EventMachine::Client.connect(uri: @_uri, headers: headers)
|
48
62
|
end
|
49
63
|
|
50
64
|
# @param [String] action - how the message is being sent
|
51
65
|
# @param [Hash] data - the message to be sent to the channel
|
52
66
|
def perform(action, data)
|
53
|
-
|
54
|
-
message_queue.push([action, data])
|
55
|
-
else
|
56
|
-
dispatch_message(action, data)
|
57
|
-
end
|
67
|
+
dispatch_message(action, data)
|
58
68
|
end
|
59
69
|
|
60
70
|
# callback for received messages as well as
|
@@ -70,7 +80,7 @@ class ActionCableClient
|
|
70
80
|
# puts message
|
71
81
|
# end
|
72
82
|
def received(skip_pings = true)
|
73
|
-
_websocket_client.
|
83
|
+
_websocket_client.onmessage do |message, _type|
|
74
84
|
handle_received_message(message, skip_pings) do |json|
|
75
85
|
yield(json)
|
76
86
|
end
|
@@ -85,7 +95,7 @@ class ActionCableClient
|
|
85
95
|
# # do things after the client is connected to the server
|
86
96
|
# end
|
87
97
|
def connected
|
88
|
-
_websocket_client.
|
98
|
+
_websocket_client.onopen do
|
89
99
|
subscribe
|
90
100
|
yield
|
91
101
|
end
|
@@ -122,7 +132,7 @@ class ActionCableClient
|
|
122
132
|
# # cleanup after the server disconnects from the client
|
123
133
|
# end
|
124
134
|
def disconnected
|
125
|
-
_websocket_client.
|
135
|
+
_websocket_client.onclose do
|
126
136
|
self._subscribed = false
|
127
137
|
yield
|
128
138
|
end
|
@@ -130,40 +140,12 @@ class ActionCableClient
|
|
130
140
|
|
131
141
|
private
|
132
142
|
|
133
|
-
# @param [
|
134
|
-
# This object is from the websocket-ruby gem:
|
135
|
-
# https://github.com/imanel/websocket-ruby/blob/master/lib/websocket/frame/incoming/client.rb
|
136
|
-
#
|
137
|
-
# [9] pry(#<ActionCableClient>)> ap message.methods - Object.instance_methods
|
138
|
-
#
|
139
|
-
# [ 0] <<(data) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Incoming)
|
140
|
-
# [ 1] code() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
141
|
-
# [ 2] code=(arg1) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
142
|
-
# [ 3] data() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
143
|
-
# [ 4] data=(arg1) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
144
|
-
# [ 5] decoded?() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Incoming)
|
145
|
-
# [ 6] error() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
146
|
-
# [ 7] error=(arg1) WebSocket::Frame::Incoming::Client (WebSocket::ExceptionHandler)
|
147
|
-
# [ 8] error?() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
148
|
-
# [ 9] incoming_masking?() WebSocket::Frame::Incoming::Client
|
149
|
-
# [10] initialize_with_rescue(*args) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
150
|
-
# [11] next(*args) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Incoming)
|
151
|
-
# [12] next_with_rescue(*args) WebSocket::Frame::Incoming::Client (WebSocket::Frame::Incoming)
|
152
|
-
# [13] next_without_rescue() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Incoming)
|
153
|
-
# [14] outgoing_masking?() WebSocket::Frame::Incoming::Client
|
154
|
-
# [15] support_type?() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
155
|
-
# [16] supported_frames() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
156
|
-
# [17] type() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
157
|
-
# [18] version() WebSocket::Frame::Incoming::Client (WebSocket::Frame::Base)
|
158
|
-
#
|
159
|
-
# None of this really seems that importont, other than `data`
|
160
|
-
#
|
143
|
+
# @param [String] message - the websockt message object
|
161
144
|
# @param [Boolean] skip_pings - by default, messages
|
162
145
|
# with the identifier '_ping' are skipped
|
163
146
|
def handle_received_message(message, skip_pings = true)
|
164
|
-
|
165
|
-
|
166
|
-
json = JSON.parse(string)
|
147
|
+
return if message.empty?
|
148
|
+
json = JSON.parse(message)
|
167
149
|
|
168
150
|
if is_ping?(json)
|
169
151
|
yield(json) unless skip_pings
|
@@ -174,17 +156,15 @@ class ActionCableClient
|
|
174
156
|
# maybe just make it extensible?
|
175
157
|
yield(json)
|
176
158
|
end
|
177
|
-
|
178
|
-
deplete_queue if _queued_send
|
179
159
|
end
|
180
160
|
|
181
161
|
# {"identifier" => "_ping","type" => "confirm_subscription"}
|
182
162
|
def check_for_subscribe_confirmation(message)
|
183
163
|
message_type = message[Message::TYPE_KEY]
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
164
|
+
return unless Message::TYPE_CONFIRM_SUBSCRIPTION == message_type
|
165
|
+
|
166
|
+
self._subscribed = true
|
167
|
+
_subscribed_callaback&.call
|
188
168
|
end
|
189
169
|
|
190
170
|
# {"identifier" => "_ping","message" => 1460201942}
|
@@ -199,23 +179,12 @@ class ActionCableClient
|
|
199
179
|
send_msg(msg.to_json)
|
200
180
|
end
|
201
181
|
|
202
|
-
def deplete_queue
|
203
|
-
# if we haven't yet subscribed, don't deplete the queue
|
204
|
-
if subscribed?
|
205
|
-
# only try to send if we have messages to send
|
206
|
-
until message_queue.empty?
|
207
|
-
action, data = message_queue.pop
|
208
|
-
dispatch_message(action, data)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
182
|
def dispatch_message(action, data)
|
214
183
|
# can't send messages if we aren't subscribed
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
184
|
+
return unless subscribed?
|
185
|
+
|
186
|
+
msg = _message_factory.create(Commands::MESSAGE, action, data)
|
187
|
+
json = msg.to_json
|
188
|
+
send_msg(json)
|
220
189
|
end
|
221
190
|
end
|
@@ -1,11 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class ActionCableClient
|
3
4
|
class MessageFactory
|
4
|
-
attr_reader :
|
5
|
+
attr_reader :channel, :identifier
|
5
6
|
|
6
|
-
# @param [String] channel - the name of the subscribed channel
|
7
|
+
# @param [String or Hash] channel - the name of the subscribed channel, or
|
8
|
+
# a hash that includes the :channel key and any other params to send.
|
7
9
|
def initialize(channel)
|
8
|
-
|
10
|
+
# the ending result should look like
|
11
|
+
# "{"channel":"RoomChannel"}" but that's up to
|
12
|
+
# the Mesage to format it
|
13
|
+
@channel = channel
|
14
|
+
@identifier =
|
15
|
+
case channel
|
16
|
+
when String then { channel: channel }
|
17
|
+
when Hash then channel
|
18
|
+
else
|
19
|
+
raise ActionCableClient::Errors::ChannelNotSpecified, 'channel is invalid'
|
20
|
+
end
|
9
21
|
end
|
10
22
|
|
11
23
|
# @param [String] command - the type of message that this is
|
@@ -22,12 +34,5 @@ class ActionCableClient
|
|
22
34
|
def build_data(action, message)
|
23
35
|
message.merge(action: action) if message.is_a?(Hash)
|
24
36
|
end
|
25
|
-
|
26
|
-
# the ending result should look like
|
27
|
-
# "{"channel":"RoomChannel"}" but that's up to
|
28
|
-
# the Mesage to format it
|
29
|
-
def identifier
|
30
|
-
{ channel: _channel }
|
31
|
-
end
|
32
37
|
end
|
33
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_cable_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- L. Preston Sego III
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 5.0.0.beta4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: websocket-eventmachine-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.2.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.2.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,8 +101,10 @@ extensions: []
|
|
101
101
|
extra_rdoc_files: []
|
102
102
|
files:
|
103
103
|
- CHANGELOG.md
|
104
|
+
- LICENSE
|
104
105
|
- README.md
|
105
106
|
- lib/action_cable_client.rb
|
107
|
+
- lib/action_cable_client/errors.rb
|
106
108
|
- lib/action_cable_client/message.rb
|
107
109
|
- lib/action_cable_client/message_factory.rb
|
108
110
|
- lib/action_cable_client/version.rb
|
@@ -126,8 +128,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
128
|
version: '0'
|
127
129
|
requirements: []
|
128
130
|
rubyforge_project:
|
129
|
-
rubygems_version: 2.
|
131
|
+
rubygems_version: 2.5.1
|
130
132
|
signing_key:
|
131
133
|
specification_version: 4
|
132
|
-
summary: ActionCableClient-
|
134
|
+
summary: ActionCableClient-2.0.0
|
133
135
|
test_files: []
|
136
|
+
has_rdoc:
|