action_channels 0.1.3 → 0.2.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.
- checksums.yaml +8 -8
- data/.gitignore +1 -0
- data/README.md +25 -2
- data/lib/action_channels.rb +10 -2
- data/lib/action_channels/channel_repository.rb +1 -1
- data/lib/action_channels/channels.rb +7 -0
- data/lib/action_channels/channels/base.rb +89 -0
- data/lib/action_channels/channels/news_channel.rb +21 -0
- data/lib/action_channels/client.rb +77 -0
- data/lib/action_channels/driver.rb +9 -0
- data/lib/action_channels/errors.rb +1 -0
- data/lib/action_channels/message.rb +9 -5
- data/lib/action_channels/server.rb +4 -9
- data/lib/action_channels/version.rb +1 -1
- metadata +7 -3
- data/lib/action_channels/channel.rb +0 -80
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDNlYzcxNjljYTNiNWJmNjk4ZDkyYjU1ZWU5ZGVhYTczNTA2MmVmYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzlkYzg3MWNlZGZjNjNlOGY4YTIyZTRlNWQ5N2Y2ZTdiZWFiYTYyMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDBlOWUyZjEyYTkzOTE2ZDk2YWEzZjU4MDZlNzYwNDRkMWQzYTRiZTJiODY2
|
10
|
+
M2RhZGZmMTkyOTNjNWUwZDBjN2NiNzc2MWExMWQyODMwNWY2YWZiNmIzNGY1
|
11
|
+
NzZlOTljNTU0MzM3NWM5ZDlmYTkzZWMwMmQ0YThkYTEyMDcxZWU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTM4MDFiMTlmNjNkNTU1MDVjN2Q2MzUyMzgxMDk1OGJlMTIxZTM3ZDIwMmY2
|
14
|
+
MmEyMTUxMTA5NWZjZDY0ZjUxYzNjNDc2YTUwYTQzZDQyMmYxNWI0OTU1YmM4
|
15
|
+
YTFlMTI2OGMwMjI5NGY0MGI1MjUzYThjNmUwOWE2NGYwMTdmZDg=
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
# Action channels
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
## Example
|
4
|
+
|
5
|
+
``` ruby
|
6
|
+
require 'action_channels'
|
7
|
+
|
8
|
+
class PingPongChannel < ActionChannels::Channels::Base
|
9
|
+
def process_custom_message(message)
|
10
|
+
case message.type
|
11
|
+
when 'ping'
|
12
|
+
send_message message.author, ActionChannels::Message.new(channel: name, type: 'pong')
|
13
|
+
when 'pong'
|
14
|
+
send_message message.author, ActionChannels::Message.new(channel: name, type: 'ping')
|
15
|
+
else
|
16
|
+
on_unknown_type_message message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
server = ActionChannels::Server.new(
|
22
|
+
port: 3050,
|
23
|
+
channels: [PingPongChannel.new(name: 'ping-pong')]
|
24
|
+
)
|
25
|
+
|
26
|
+
server.run
|
27
|
+
```
|
data/lib/action_channels.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'logger'
|
3
|
+
require 'nio/websocket'
|
2
4
|
|
3
5
|
require "action_channels/version"
|
4
6
|
require "action_channels/errors"
|
7
|
+
require "action_channels/driver"
|
5
8
|
require "action_channels/message"
|
6
9
|
require "action_channels/message_senders"
|
7
|
-
require "action_channels/
|
10
|
+
require "action_channels/client"
|
11
|
+
require "action_channels/channels"
|
8
12
|
require "action_channels/channel_repository"
|
9
13
|
require "action_channels/server"
|
10
14
|
|
11
15
|
module ActionChannels
|
12
16
|
class << self
|
13
|
-
|
17
|
+
attr_writer :logger
|
14
18
|
|
15
19
|
def configure(&block)
|
16
20
|
yield(self)
|
17
21
|
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
@logger ||= Logger.new('action_channels.log')
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module ActionChannels
|
2
|
+
module Channels
|
3
|
+
class Base
|
4
|
+
attr_reader :name, :clients
|
5
|
+
attr_accessor :message_sender
|
6
|
+
|
7
|
+
def initialize(attrs)
|
8
|
+
@name = attrs.fetch(:name)
|
9
|
+
@clients = Set.new
|
10
|
+
@message_sender = attrs.fetch :message_sender, MessageSenders::WebSocket.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_message(receiver, message)
|
14
|
+
message_sender.do_send(receiver, message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_client(client)
|
18
|
+
clients << client
|
19
|
+
|
20
|
+
ActionChannels.logger.info "The channel ##{self.name} added a client"
|
21
|
+
ActionChannels.logger.debug "Count of client of channel ##{self.name} is #{self.clients.count}."
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_client(client)
|
25
|
+
clients.delete client
|
26
|
+
|
27
|
+
ActionChannels.logger.info "The channel ##{self.name} removed a client"
|
28
|
+
ActionChannels.logger.debug "Count of client of channel ##{self.name} is #{self.clients.count}."
|
29
|
+
end
|
30
|
+
|
31
|
+
def notify_all(message)
|
32
|
+
clients.each do |client|
|
33
|
+
send_message client, message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_message(message)
|
38
|
+
ActionChannels.logger.debug "The channel ##{self.name} received a message #{message.inspect}"
|
39
|
+
|
40
|
+
if message.systemic?
|
41
|
+
process_system_message message
|
42
|
+
else
|
43
|
+
process_custom_message message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_system_message(message)
|
48
|
+
case message.type
|
49
|
+
when 'subscribe'
|
50
|
+
on_subscribe message.author, message.details
|
51
|
+
when 'unsubscribe'
|
52
|
+
on_unsubscribe message.author, message.details
|
53
|
+
else
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_custom_message(message)
|
58
|
+
# nothing
|
59
|
+
end
|
60
|
+
|
61
|
+
def after_message_subscribe(subscriber, details)
|
62
|
+
# nothing
|
63
|
+
end
|
64
|
+
|
65
|
+
def after_message_unsubscribe(subscriber, details)
|
66
|
+
# nothing
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def on_subscribe(subscriber, details)
|
72
|
+
add_client(subscriber)
|
73
|
+
send_message subscriber, Message.new(channel: name, type: 'subscribed')
|
74
|
+
after_message_subscribe subscriber, details
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_unsubscribe(subscriber, details)
|
78
|
+
remove_client(subscriber)
|
79
|
+
send_message subscriber, Message.new(channel: name, type: 'unsubscribed')
|
80
|
+
after_message_unsubscribe subscriber, details
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_unknown_type_message(message)
|
84
|
+
user, type = message.author, message.type
|
85
|
+
send_message user, Message.new(channel: name, type: 'invalid_message', details: { error: "Unknown type of message: #{type}" })
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActionChannels
|
2
|
+
module Channels
|
3
|
+
class NewsChannel < Base
|
4
|
+
def process_custom_message(message)
|
5
|
+
case message.type
|
6
|
+
when 'publish'
|
7
|
+
on_publish message.author, message.details
|
8
|
+
else
|
9
|
+
on_unknown_type_message message
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def on_publish(speaker, details)
|
16
|
+
notify_all Message.new(channel: name, type: 'news', details: details)
|
17
|
+
send_message speaker, Message.new(channel: name, type: 'published')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module ActionChannels
|
4
|
+
class Client
|
5
|
+
class << self
|
6
|
+
def send_message(url, message, options = {})
|
7
|
+
client = new(url, options)
|
8
|
+
client.start
|
9
|
+
client.send_message(message)
|
10
|
+
client.stop
|
11
|
+
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :url, :message_sender
|
17
|
+
|
18
|
+
def initialize(url, options = {})
|
19
|
+
@url = url
|
20
|
+
self.is_connected = false
|
21
|
+
@message_sender = options.fetch :message_sender, MessageSenders::WebSocket.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def start(waits_at = 20)
|
25
|
+
return true if connected?
|
26
|
+
|
27
|
+
do_connect
|
28
|
+
|
29
|
+
Timeout::timeout(waits_at, Errors::NotConnected) do
|
30
|
+
until connected? do
|
31
|
+
sleep 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
if connected?
|
40
|
+
connect.close
|
41
|
+
connect = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_message(message)
|
48
|
+
return false unless connected?
|
49
|
+
|
50
|
+
message_sender.do_send(connect, message)
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def connected?
|
56
|
+
is_connected
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
attr_accessor :is_connected, :connect
|
62
|
+
|
63
|
+
def do_connect
|
64
|
+
client = self
|
65
|
+
|
66
|
+
::NIO::WebSocket.connect(url) do |websocket|
|
67
|
+
on_open = lambda do |_|
|
68
|
+
client.send 'connect=', websocket
|
69
|
+
client.send 'is_connected=', true
|
70
|
+
end
|
71
|
+
|
72
|
+
websocket.on :open, on_open
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -5,18 +5,18 @@ module ActionChannels
|
|
5
5
|
attr_accessor(
|
6
6
|
:channel, # [String]
|
7
7
|
:type, # [String]
|
8
|
-
:author, # [
|
8
|
+
:author, # [Driver]
|
9
9
|
:details # Hash
|
10
10
|
)
|
11
11
|
|
12
12
|
class << self
|
13
13
|
def parse(message_raw)
|
14
|
-
command_json = JSON.parse message_raw
|
14
|
+
command_json = JSON.parse message_raw, symbolize_names: true
|
15
15
|
|
16
16
|
new(
|
17
|
-
channel: command_json[
|
18
|
-
type: command_json[
|
19
|
-
details: command_json.fetch(
|
17
|
+
channel: command_json[:channel],
|
18
|
+
type: command_json[:type],
|
19
|
+
details: command_json.fetch(:details, {})
|
20
20
|
)
|
21
21
|
rescue JSON::ParserError => exp
|
22
22
|
raise Errors::NotParseMessage, exp.message
|
@@ -39,5 +39,9 @@ module ActionChannels
|
|
39
39
|
def to_raw
|
40
40
|
JSON.generate(channel: channel, type: type, details: details)
|
41
41
|
end
|
42
|
+
|
43
|
+
def systemic?
|
44
|
+
%w(subscribe unsubscribe).include? type
|
45
|
+
end
|
42
46
|
end
|
43
47
|
end
|
@@ -1,25 +1,20 @@
|
|
1
|
-
require 'nio/websocket'
|
2
|
-
|
3
1
|
module ActionChannels
|
4
2
|
class Server
|
5
|
-
attr_reader :port
|
3
|
+
attr_reader :port, :channel_repository
|
6
4
|
|
7
5
|
def initialize(options)
|
8
6
|
@port = options.fetch(:port)
|
7
|
+
@channel_repository = ChannelRepository.new options.fetch(:channels, [])
|
9
8
|
end
|
10
9
|
|
11
10
|
def run
|
12
|
-
NIO::WebSocket.listen port: port do |
|
13
|
-
process_client
|
11
|
+
NIO::WebSocket.listen port: port do |driver|
|
12
|
+
process_client Driver.new(driver)
|
14
13
|
end
|
15
14
|
|
16
15
|
ActionChannels.logger.info "Server started on port: #{port}"
|
17
16
|
end
|
18
17
|
|
19
|
-
def channel_repository
|
20
|
-
@channel_repository ||= ChannelRepository.new
|
21
|
-
end
|
22
|
-
|
23
18
|
def process_client(client)
|
24
19
|
client.on :message, callback_on_message(client)
|
25
20
|
client.on :close, callback_on_close(client)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_channels
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- German Antsiferov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -97,8 +97,12 @@ files:
|
|
97
97
|
- Rakefile
|
98
98
|
- action_channels.gemspec
|
99
99
|
- lib/action_channels.rb
|
100
|
-
- lib/action_channels/channel.rb
|
101
100
|
- lib/action_channels/channel_repository.rb
|
101
|
+
- lib/action_channels/channels.rb
|
102
|
+
- lib/action_channels/channels/base.rb
|
103
|
+
- lib/action_channels/channels/news_channel.rb
|
104
|
+
- lib/action_channels/client.rb
|
105
|
+
- lib/action_channels/driver.rb
|
102
106
|
- lib/action_channels/errors.rb
|
103
107
|
- lib/action_channels/message.rb
|
104
108
|
- lib/action_channels/message_senders.rb
|
@@ -1,80 +0,0 @@
|
|
1
|
-
module ActionChannels
|
2
|
-
class Channel
|
3
|
-
def self.input_message_types
|
4
|
-
%w(subscribe unsubscribe publish)
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.output_message_types
|
8
|
-
%w(subscribed unsubscribed published news invalid_message)
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :name, :clients
|
12
|
-
attr_accessor :message_sender
|
13
|
-
|
14
|
-
def initialize(attrs)
|
15
|
-
@name = attrs.fetch(:name)
|
16
|
-
@clients = Set.new
|
17
|
-
@message_sender = attrs.fetch :message_sender, MessageSenders::WebSocket.new
|
18
|
-
end
|
19
|
-
|
20
|
-
def send_message(receiver, message)
|
21
|
-
message_sender.do_send(receiver, message)
|
22
|
-
end
|
23
|
-
|
24
|
-
def add_client(client)
|
25
|
-
clients << client
|
26
|
-
|
27
|
-
ActionChannels.logger.info "The channel ##{self.name} added a client"
|
28
|
-
ActionChannels.logger.debug "Count of client of channel ##{self.name} is #{self.clients.count}."
|
29
|
-
end
|
30
|
-
|
31
|
-
def remove_client(client)
|
32
|
-
clients.delete client
|
33
|
-
|
34
|
-
ActionChannels.logger.info "The channel ##{self.name} removed a client"
|
35
|
-
ActionChannels.logger.debug "Count of client of channel ##{self.name} is #{self.clients.count}."
|
36
|
-
end
|
37
|
-
|
38
|
-
def notify_all(message)
|
39
|
-
clients.each do |client|
|
40
|
-
send_message client, message
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def process_message(message)
|
45
|
-
ActionChannels.logger.debug "The channel ##{self.name} received a message #{message.inspect}"
|
46
|
-
|
47
|
-
case message.type
|
48
|
-
when 'subscribe'
|
49
|
-
on_subscribe message.author, message.details
|
50
|
-
when 'unsubscribe'
|
51
|
-
on_unsubscribe message.author, message.details
|
52
|
-
when 'publish'
|
53
|
-
on_publish message.author, message.details
|
54
|
-
else
|
55
|
-
on_unknown_type_message message.author, message.type
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def on_subscribe(subscriber, details)
|
62
|
-
add_client(subscriber)
|
63
|
-
send_message subscriber, Message.new(channel: name, type: 'subscribed')
|
64
|
-
end
|
65
|
-
|
66
|
-
def on_unsubscribe(subscriber, details)
|
67
|
-
remove_client(subscriber)
|
68
|
-
send_message subscriber, Message.new(channel: name, type: 'unsubscribed')
|
69
|
-
end
|
70
|
-
|
71
|
-
def on_publish(speaker, details)
|
72
|
-
notify_all Message.new(channel: name, type: 'news', details: details)
|
73
|
-
send_message speaker, Message.new(channel: name, type: 'published')
|
74
|
-
end
|
75
|
-
|
76
|
-
def on_unknown_type_message(user, type)
|
77
|
-
send_message user, Message.new(channel: name, type: 'invalid_message', details: { error: "Unknown type of message: #{type}" })
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|