action_channels 0.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.
- checksums.yaml +7 -0
- data/.editorconfig +21 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.txt +21 -0
- data/README.md +4 -0
- data/Rakefile +6 -0
- data/action_channels.gemspec +29 -0
- data/lib/action_channels.rb +19 -0
- data/lib/action_channels/channel.rb +76 -0
- data/lib/action_channels/channel_repository.rb +37 -0
- data/lib/action_channels/errors.rb +6 -0
- data/lib/action_channels/message.rb +43 -0
- data/lib/action_channels/message_senders.rb +8 -0
- data/lib/action_channels/message_senders/base.rb +8 -0
- data/lib/action_channels/message_senders/buffer.rb +13 -0
- data/lib/action_channels/message_senders/websocket.rb +9 -0
- data/lib/action_channels/server.rb +43 -0
- data/lib/action_channels/version.rb +3 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2a86a68b94af2eef6fcb59c71974a5906905c130
|
4
|
+
data.tar.gz: f24eb7b22f1eeafe33183e2686f591a427bf0801
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 65665230caaa80943f0d94cd0e561403fb71cbec5b9aae2bfb8bf3446109595c2e91e07bae8c59300eb1eacb557cad59a8c1ac180ee34ff6a637919c042d4dfb
|
7
|
+
data.tar.gz: 3f74d7b19fe79a651d50cfb57e8f437f7bf1f140dbadf0e605dddb9def7eec3ff19342cd711331d5d4a842d54797f0b7501f4e42da3ce1f86fbd66edb30e29f0
|
data/.editorconfig
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# EditorConfig helps developers define and maintain consistent
|
2
|
+
# coding styles between different editors and IDEs
|
3
|
+
# editorconfig.org
|
4
|
+
|
5
|
+
root = true
|
6
|
+
|
7
|
+
|
8
|
+
[*]
|
9
|
+
|
10
|
+
# Change these settings to your own preference
|
11
|
+
indent_style = space
|
12
|
+
indent_size = 2
|
13
|
+
|
14
|
+
# We recommend you to keep these unchanged
|
15
|
+
end_of_line = lf
|
16
|
+
charset = utf-8
|
17
|
+
trim_trailing_whitespace = true
|
18
|
+
insert_final_newline = true
|
19
|
+
|
20
|
+
[*.md]
|
21
|
+
trim_trailing_whitespace = false
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.4.2
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
action_channels (0.0.1)
|
5
|
+
nio4r-websocket
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.3)
|
11
|
+
nio4r (2.2.0)
|
12
|
+
nio4r-websocket (0.6.2)
|
13
|
+
nio4r (>= 1.2.1, < 3.0)
|
14
|
+
websocket-driver (~> 0.7)
|
15
|
+
rake (10.5.0)
|
16
|
+
rspec (3.7.0)
|
17
|
+
rspec-core (~> 3.7.0)
|
18
|
+
rspec-expectations (~> 3.7.0)
|
19
|
+
rspec-mocks (~> 3.7.0)
|
20
|
+
rspec-core (3.7.1)
|
21
|
+
rspec-support (~> 3.7.0)
|
22
|
+
rspec-expectations (3.7.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.7.0)
|
25
|
+
rspec-mocks (3.7.0)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.7.0)
|
28
|
+
rspec-support (3.7.1)
|
29
|
+
websocket-driver (0.7.0)
|
30
|
+
websocket-extensions (>= 0.1.0)
|
31
|
+
websocket-extensions (0.1.3)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
action_channels!
|
38
|
+
bundler (~> 1.16)
|
39
|
+
rake (~> 10.0)
|
40
|
+
rspec (~> 3.0)
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.16.0
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 German Antsiferov
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "action_channels/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "action_channels"
|
8
|
+
spec.version = ActionChannels::VERSION
|
9
|
+
spec.authors = ["German Antsiferov"]
|
10
|
+
spec.email = ["dxdy@bk.ru"]
|
11
|
+
|
12
|
+
spec.summary = %q{Easy server on websockets.}
|
13
|
+
spec.description = %q{Easy server on websockets.}
|
14
|
+
spec.homepage = "https://github.com/mr-dxdy/action_channels"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'nio4r-websocket'
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
require "action_channels/version"
|
4
|
+
require "action_channels/errors"
|
5
|
+
require "action_channels/message"
|
6
|
+
require "action_channels/message_senders"
|
7
|
+
require "action_channels/channel"
|
8
|
+
require "action_channels/channel_repository"
|
9
|
+
require "action_channels/server"
|
10
|
+
|
11
|
+
module ActionChannels
|
12
|
+
class << self
|
13
|
+
attr_accessor :logger
|
14
|
+
|
15
|
+
def configure(&block)
|
16
|
+
yield(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,76 @@
|
|
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(name:, message_sender: MessageSenders::WebSocket.new)
|
15
|
+
@name = name
|
16
|
+
@clients = Set.new
|
17
|
+
@message_sender = message_sender
|
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
|
+
ActionChannels.logger.info "The channel ##{self.name} added a client"
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_client(client)
|
30
|
+
clients.delete client
|
31
|
+
ActionChannels.logger.info "The channel ##{self.name} removed a client"
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify_all(message)
|
35
|
+
clients.each do |client|
|
36
|
+
send_message client, message
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_message(message)
|
41
|
+
ActionChannels.logger.debug "The channel ##{self.name} received a message #{message.inspect}"
|
42
|
+
|
43
|
+
case message.type
|
44
|
+
when 'subscribe'
|
45
|
+
on_subscribe message.author, message.details
|
46
|
+
when 'unsubscribe'
|
47
|
+
on_unsubscribe message.author, message.details
|
48
|
+
when 'publish'
|
49
|
+
on_publish message.author, message.details
|
50
|
+
else
|
51
|
+
on_unknown_type_message message.author, message.type
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def on_subscribe(subscriber, details)
|
58
|
+
add_client(subscriber)
|
59
|
+
send_message subscriber, Message.new(channel: name, type: 'subscribed')
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_unsubscribe(subscriber, details)
|
63
|
+
remove_client(subscriber)
|
64
|
+
send_message subscriber, Message.new(channel: name, type: 'unsubscribed')
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_publish(speaker, details)
|
68
|
+
notify_all Message.new(channel: name, type: 'news', details: details)
|
69
|
+
send_message speaker, Message.new(channel: name, type: 'published')
|
70
|
+
end
|
71
|
+
|
72
|
+
def on_unknown_type_message(user, type)
|
73
|
+
send_message user, Message.new(channel: name, type: 'invalid_message', details: { error: "Unknown type of message: #{type}" })
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActionChannels
|
2
|
+
class ChannelRepository
|
3
|
+
def initialize(channels = [])
|
4
|
+
@channels = Set.new channels
|
5
|
+
end
|
6
|
+
|
7
|
+
def all
|
8
|
+
channels.to_a
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_by_name(channel_name)
|
12
|
+
channels.find { |channel| channel.name.eql? channel_name }
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_by_name_or_create(channel_name)
|
16
|
+
find_by_name(channel_name) || create(channel_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(channel)
|
20
|
+
channels.add channel
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(channel)
|
24
|
+
channels.delete channel
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :channels
|
30
|
+
|
31
|
+
def create(channel_name)
|
32
|
+
channel = Channel.new name: channel_name
|
33
|
+
channels << channel
|
34
|
+
channel
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module ActionChannels
|
4
|
+
class Message
|
5
|
+
attr_accessor(
|
6
|
+
:channel, # [String]
|
7
|
+
:type, # [String]
|
8
|
+
:author, # [WebSocket::Driver]
|
9
|
+
:details # Hash
|
10
|
+
)
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def parse(message_raw)
|
14
|
+
command_json = JSON.parse message_raw
|
15
|
+
|
16
|
+
new(
|
17
|
+
channel: command_json['channel'],
|
18
|
+
type: command_json['type'],
|
19
|
+
details: command_json.fetch('details', {})
|
20
|
+
)
|
21
|
+
rescue JSON::ParserError => exp
|
22
|
+
raise Errors::NotParseMessage, exp.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_and_setup_author(message_raw, author)
|
26
|
+
command = parse(message_raw)
|
27
|
+
command.author = author
|
28
|
+
command
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(channel:, type:, author: nil, details: {})
|
33
|
+
@channel = channel
|
34
|
+
@type = type
|
35
|
+
@author = author
|
36
|
+
@details = details
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_raw
|
40
|
+
JSON.generate(channel: channel, type: type, details: details)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'nio/websocket'
|
2
|
+
|
3
|
+
module ActionChannels
|
4
|
+
class Server
|
5
|
+
attr_reader :port
|
6
|
+
|
7
|
+
def initialize(port:)
|
8
|
+
@port = port
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
NIO::WebSocket.listen port: port do |client|
|
13
|
+
on_message = lambda do |event|
|
14
|
+
ActionChannels.logger.info "Received new message: #{event.data}"
|
15
|
+
|
16
|
+
message = Message.parse_and_setup_author event.data, client
|
17
|
+
process_message message
|
18
|
+
end
|
19
|
+
|
20
|
+
on_close = lambda do |event|
|
21
|
+
ActionChannels.logger.info "Client closed connection."
|
22
|
+
channel_repository.all.each { |channel| channel.remove_client(client) }
|
23
|
+
end
|
24
|
+
|
25
|
+
client.on :message, on_message
|
26
|
+
client.on :close, on_close
|
27
|
+
end
|
28
|
+
|
29
|
+
ActionChannels.logger.info "Server started on port: #{port}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def channel_repository
|
33
|
+
@channel_repository ||= ChannelRepository.new
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def process_message(message)
|
39
|
+
channel = channel_repository.find_by_name_or_create message.channel
|
40
|
+
channel.process_message message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: action_channels
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- German Antsiferov
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nio4r-websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: Easy server on websockets.
|
70
|
+
email:
|
71
|
+
- dxdy@bk.ru
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".editorconfig"
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".ruby-version"
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- action_channels.gemspec
|
86
|
+
- lib/action_channels.rb
|
87
|
+
- lib/action_channels/channel.rb
|
88
|
+
- lib/action_channels/channel_repository.rb
|
89
|
+
- lib/action_channels/errors.rb
|
90
|
+
- lib/action_channels/message.rb
|
91
|
+
- lib/action_channels/message_senders.rb
|
92
|
+
- lib/action_channels/message_senders/base.rb
|
93
|
+
- lib/action_channels/message_senders/buffer.rb
|
94
|
+
- lib/action_channels/message_senders/websocket.rb
|
95
|
+
- lib/action_channels/server.rb
|
96
|
+
- lib/action_channels/version.rb
|
97
|
+
homepage: https://github.com/mr-dxdy/action_channels
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.13
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Easy server on websockets.
|
121
|
+
test_files: []
|