sock-drawer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4aed78bab3749fc12dac0bab025f9677595d9300
4
+ data.tar.gz: 1ef7a4f58fd5810ee7a9abe707ca5919cae8f882
5
+ SHA512:
6
+ metadata.gz: c5c7a1b748211288e7b9c265c78baec327483cddbbbc8c40ea81f5e96af6e6187c6f2c962af923b0e89642b5473bd0fe30f83bb0a31f9c43b4b6a628aa8191e4
7
+ data.tar.gz: 5f90cb1049216f6e32fb69653c807a51e29761d7a08a9604d050f78f87c75439243c183243f64f6ca092f9f6c223caa4b9a1e89ef530fb5d19d69b3721513996
data/.gitignore ADDED
@@ -0,0 +1,43 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ *.gem
15
+ *.rbc
16
+ /.config
17
+ /coverage/
18
+ /InstalledFiles
19
+ /pkg/
20
+ /spec/reports/
21
+ /test/tmp/
22
+ /test/version_tmp/
23
+ /tmp/
24
+
25
+ ## Documentation cache and generated files:
26
+ /.yardoc/
27
+ /_yardoc/
28
+ /doc/
29
+ /rdoc/
30
+
31
+ ## Environment normalisation:
32
+ /.bundle/
33
+ /vendor/bundle
34
+ /lib/bundler/man/
35
+
36
+ # for a library or gem, you might want to ignore these files since the code is
37
+ # intended to run in multiple environments; otherwise, check them in:
38
+ # Gemfile.lock
39
+ # .ruby-version
40
+ # .ruby-gemset
41
+
42
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
43
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sock-drawer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Adam Hess
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Sock::Drawer
2
+
3
+ This gem allows message async message calls to subscribed listeners.
4
+ Messages can be fired between ruby objects or to listening websocket connections.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'sock-drawer'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install sock-drawer
21
+
22
+ ## Usage
23
+
24
+ Initialize a instance of the sock client
25
+
26
+ ```Ruby
27
+ sock = Sock::Client.new(logger: Rails.logger, redis: redis)
28
+ ```
29
+
30
+ Publish an event on a channel,
31
+
32
+ ```Ruby
33
+ sock.pub("my message", postfix: "my-channel")
34
+ ```
35
+
36
+ Subscribe to events fired from a specific sock server,
37
+
38
+ ```Ruby
39
+ sock.sub(server, "my-channel") do |message|
40
+ puts message
41
+ end
42
+ ```
43
+
44
+ To capture the event in Javascript use something like,
45
+
46
+ ```javascript
47
+ var webSocket = new WebSocket("ws://" + location.hostname + ":8081/" + "my-channel");
48
+
49
+ webSocket.onmessage = function(event) {
50
+ console.log(event.data);
51
+ }
52
+ ```
53
+
54
+ If you wish to start the sock server in another thread you can use
55
+
56
+ $ rake sock:server
57
+
58
+ And you are good to go!
59
+
60
+ ## Contributing
61
+
62
+ 1. Fork it ( https://github.com/[my-github-username]/sock-drawer/fork )
63
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
64
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
65
+ 4. Push to the branch (`git push origin my-new-feature`)
66
+ 5. Create a new Pull Request
67
+
68
+ ## Running the tests
69
+
70
+ if you are going to contribute, I hope you run the tests at least once -- hopefully many times.
71
+ to run the tests, you must have redis-server running in the background with default configuration.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'sock/drawer'
3
+
4
+ namespace :sock do
5
+ desc 'start the sock-drawer server to manage socket connections'
6
+ task :server do
7
+ Sock::Server.new.start!
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module Sock
2
+ # Client is the interface for publishing events to Drawer
3
+ class Client
4
+ def initialize(name: DEFAULT_NAME,
5
+ logger: Logger.new(STDOUT),
6
+ redis: Redis.new)
7
+ @logger = logger
8
+ @name = name
9
+ @redis = redis
10
+ end
11
+
12
+ # send a message to all subscribed listeners.
13
+ def pub(msg, postfix: '')
14
+ @logger.info "sending #{msg} on channel: #{channel_name(postfix)}"
15
+ @redis.publish(channel_name(postfix), msg)
16
+ end
17
+
18
+ # subscribe to all events fired on a given channel
19
+ def sub(server, channel, &block)
20
+ @logger.info "subscribing to #{channel}"
21
+ server.channel(channel_name(channel)).subscribe { |msg| block.call(msg) }
22
+ end
23
+
24
+ private
25
+
26
+ def channel_name(postfix)
27
+ "#{@name}/#{postfix}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module Sock
2
+ # supply the version number
3
+ class Drawer
4
+ # The current version of sock-drawer
5
+ VERSION = '0.0.2'
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ require 'sock/drawer/version'
2
+ require 'em-websocket'
3
+ require 'em-hiredis'
4
+ require 'redis'
5
+ require 'logger'
6
+ require 'sock/client'
7
+ require 'sock/server'
8
+
9
+ # Sock is the top level module for sock objects
10
+ module Sock
11
+ # the default prefix of events listened to on redis
12
+ DEFAULT_NAME = 'sock-hook'
13
+ # the default host to run the websocket server on
14
+ HOST = '0.0.0.0'
15
+ # the default port to run the websocket server on.
16
+ PORT = 8020
17
+ end
@@ -0,0 +1,79 @@
1
+ module Sock
2
+ # server provides a EM process that will manage websocket connections
3
+ class Server
4
+ attr_reader :channels, :logger, :name
5
+ def initialize(name: DEFAULT_NAME,
6
+ logger: Logger.new(STDOUT),
7
+ socket_params: { host: HOST, port: PORT },
8
+ mode: 'default')
9
+ @name = name
10
+ @socket_params = socket_params
11
+ @channels = {}
12
+ @logger = logger
13
+ @mode = mode
14
+ end
15
+
16
+ # utility method used to subscribe on the default name and start the socket server
17
+ def start!
18
+ EM.run do
19
+ subscribe(@name)
20
+ socket_start_listening
21
+ end
22
+ end
23
+
24
+ # subscribe fires a event on a EM channel whenever a message is fired on a pattern matching
25
+ # @name (default: "sock-hook/") + '*'
26
+ def subscribe(subscription)
27
+ @logger.info "Subscribing to: #{subscription + '*'}"
28
+ pubsub.psubscribe(subscription + '*') do |chan, msg|
29
+ @logger.info "pushing c: #{chan} msg: #{msg}"
30
+ channel(chan).push(msg)
31
+ end
32
+ end
33
+
34
+ # channel will find or create a EM channel.
35
+ # channels can have new events pushed on them or subscribe to events from them.
36
+ def channel(channel_name)
37
+ @logger.info "creating channel #{channel_name}" unless @channels[channel_name]
38
+ @channels[channel_name] ||= EM::Channel.new
39
+ end
40
+
41
+
42
+ # starts the websocket server
43
+ # on open this server will find a new channel based on the path
44
+ # on message it will fire an event to the default 'incoming-hook' channel.
45
+ # on close, we do nothing. (future direction: would be nice to unsubscribe the websocket event if we know it is the only one listening to that channel)
46
+ def socket_start_listening
47
+ EventMachine::WebSocket.start(@socket_params) do |ws|
48
+ handle_open(ws)
49
+ handle_message(ws)
50
+ handle_close(ws)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def handle_open(ws)
57
+ ws.onopen do |handshake|
58
+ @logger.info "sock opened on #{handshake.path}"
59
+ channel(@name + handshake.path).subscribe { |msg| ws.send(msg) }
60
+ end
61
+ end
62
+
63
+ def handle_message(ws)
64
+ ws.onmessage { |msg| channel('incoming-hook').push(msg) }
65
+ end
66
+
67
+ def handle_close(ws)
68
+ # TODO: how can I know the sid to remove this subscription?
69
+ ws.onclose {
70
+ @logger.info "connection closed"
71
+ # channel(@name + handshake.path).unsubscribe(sid)
72
+ }
73
+ end
74
+
75
+ def pubsub
76
+ @pubsub ||= EM::Hiredis.connect.pubsub
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sock/drawer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'sock-drawer'
8
+ spec.version = Sock::Drawer::VERSION
9
+ spec.authors = ['Adam Hess']
10
+ spec.email = ['adamhess1991@gmail.com']
11
+ spec.summary = 'Super simple websocket manager.'
12
+ spec.description = 'Provide a really simple interface'\
13
+ 'to manage events in ruby and via websockets using ruby and event machine.'
14
+ spec.homepage = 'http://www.thisisadam.me'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'redis'
23
+ spec.add_dependency 'eventmachine'
24
+ spec.add_dependency 'em-hiredis'
25
+ spec.add_dependency 'em-websocket'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.7'
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'pry'
31
+ spec.add_development_dependency 'em-websocket-client'
32
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'logger'
3
+
4
+ RSpec.describe Sock::Drawer do
5
+ let(:redis) { Redis.new }
6
+ let(:sock) { Sock::Client.new(redis: redis, logger: Logger.new(nil)) }
7
+
8
+ context '#pub' do
9
+ it 'subscribes to events from redis with that name' do
10
+ expect(redis).to receive(:publish).with('sock-hook/new_channel', 'hi')
11
+ sock.pub('hi', postfix: 'new_channel')
12
+ end
13
+ end
14
+
15
+ context '#sub' do
16
+ let(:server) { Sock::Server.new(logger: Logger.new(nil)) }
17
+ let(:hi_redis) { EM::Hiredis.connect }
18
+
19
+ it 'can register a callback to be run when a event comes through redis' do
20
+ steps :fire, :received
21
+ sock.sub(server, 'hi') { |msg|
22
+ expect(msg).to eq('hi there')
23
+ complete :received
24
+ }
25
+ event_block do
26
+ server.subscribe(server.name).callback do
27
+ hi_redis.publish('sock-hook/hi', 'hi there').callback do
28
+ complete :fire
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+ require 'em-websocket-client'
3
+
4
+ # TODO rename to server spec
5
+
6
+ RSpec.describe Sock::Server do
7
+ let(:server) { Sock::Server.new(logger: Logger.new(nil)) }
8
+ let(:redis) { Redis.new }
9
+ let(:hi_redis) { EM::Hiredis.connect }
10
+ let(:sock) { Sock::Client.new(redis: redis, logger: Logger.new(nil)) }
11
+
12
+ context '#subscribe' do
13
+ it 'creates a channel for redis events to fire on' do
14
+ steps :subscribe
15
+ event_block do
16
+ df = server.subscribe('my-sub')
17
+ df.callback { |subs|
18
+ expect(subs).to eq(1)
19
+ complete :subscribe
20
+ }
21
+ end
22
+ end
23
+
24
+ it 'pushes an event onto a channel when a redis event is fired' do
25
+ steps :fire
26
+ event_block do
27
+ server.subscribe('my-sub')
28
+ df = hi_redis.publish('my-sub', 'hello')
29
+ df.callback {
30
+ expect(server.channels.keys).to eq(['my-sub'])
31
+ complete :fire
32
+ }
33
+ end
34
+ end
35
+
36
+ it 'will not create another channel if one already exists' do
37
+ steps :fire_1, :fire_2
38
+ event_block do
39
+ server.subscribe('my-sub')
40
+ hi_redis.publish('my-sub', 'hello').callback {
41
+ expect(server.channels.keys).to eq(['my-sub'])
42
+ complete :fire_1
43
+
44
+ hi_redis.publish('my-sub', 'goodbye').callback {
45
+ expect(server.channels.keys).to eq(['my-sub'])
46
+ complete :fire_2
47
+ }
48
+ }
49
+ end
50
+ end
51
+ end
52
+
53
+ context '#channel' do
54
+ it 'finds or creates a channel' do
55
+ server.channel('heyo')
56
+ expect(server.channels.keys).to eq(['heyo'])
57
+ end
58
+
59
+ it 'prevents duplicates' do
60
+ server.channel('heyo')
61
+ server.channel('whatup')
62
+ expect(server.channels.keys.sort).to eq(['heyo', 'whatup'].sort)
63
+ end
64
+
65
+ it 'can have events attached to it' do
66
+ steps :subscribe
67
+ event_block do
68
+ server.channel('9 news').subscribe { |msg|
69
+ expect(msg).to eq('hello there')
70
+ complete :subscribe
71
+ }
72
+ server.channel('9 news').push("hello there")
73
+ end
74
+ end
75
+ end
76
+
77
+ context '#socket_start_listening' do
78
+ let(:conn) { EventMachine::WebSocketClient.connect("ws://0.0.0.0:8020/") }
79
+
80
+ it 'subscribes to a channel when a connection opens' do
81
+ steps :open
82
+ event_block do
83
+ server.socket_start_listening
84
+ EventMachine::WebSocketClient.connect("ws://0.0.0.0:8020/").callback do
85
+ expect(server.channels.keys).to eq(['sock-hook/'])
86
+ complete :open
87
+ end
88
+ end
89
+ end
90
+
91
+ it 'opens different channels for different routes' do
92
+ steps :open
93
+ event_block do
94
+ server.socket_start_listening
95
+ EventMachine::WebSocketClient.connect("ws://0.0.0.0:8020/hi").callback do
96
+ expect(server.channels.keys).to eq(['sock-hook/hi'])
97
+ complete :open
98
+ end
99
+ end
100
+ end
101
+
102
+ it 'sends whatever message is sent across the channel' do
103
+ steps :open, :receive
104
+ event_block do
105
+ server.socket_start_listening
106
+ conn.callback do
107
+ server.channels['sock-hook/'].push('What up')
108
+ complete :open
109
+ end
110
+
111
+ conn.stream do |msg|
112
+ expect(msg.data).to eq('What up')
113
+ complete :receive
114
+ end
115
+ end
116
+ end
117
+
118
+ it 'will send incoming webhooks to a channel' do
119
+ steps :open, :receive
120
+ event_block do
121
+ server.socket_start_listening
122
+ conn.callback do
123
+ conn.send_msg('LGTM')
124
+ complete :open
125
+ server.channel('incoming-hook').subscribe { |msg|
126
+ expect(msg).to eq('LGTM')
127
+ complete :receive
128
+ }
129
+ end
130
+ end
131
+ end
132
+
133
+ it 'does nothing on close yet' do
134
+ # TODO: unsub sid
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
+
3
+ require 'sock/drawer'
4
+ require 'pry'
5
+
6
+ def event_block
7
+ Timeout::timeout(5) do
8
+ EM.run do
9
+ EM.epoll
10
+ yield
11
+ end
12
+ end
13
+ rescue Timeout::Error
14
+ fail "Eventmachine was not stopped with #{@_em_steps} left"
15
+ end
16
+
17
+ def steps(*steps)
18
+ @_em_steps = *steps
19
+ end
20
+
21
+ def complete(step)
22
+ @_em_steps.delete(step)
23
+ EM.stop if @_em_steps.empty?
24
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sock-drawer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Adam Hess
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
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: eventmachine
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: em-hiredis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: em-websocket
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: em-websocket-client
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Provide a really simple interfaceto manage events in ruby and via websockets
140
+ using ruby and event machine.
141
+ email:
142
+ - adamhess1991@gmail.com
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - Gemfile
149
+ - LICENSE.txt
150
+ - README.md
151
+ - Rakefile
152
+ - lib/sock/client.rb
153
+ - lib/sock/drawer.rb
154
+ - lib/sock/drawer/version.rb
155
+ - lib/sock/server.rb
156
+ - sock-drawer.gemspec
157
+ - spec/client_spec.rb
158
+ - spec/server_spec.rb
159
+ - spec/spec_helper.rb
160
+ homepage: http://www.thisisadam.me
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.2.2
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Super simple websocket manager.
184
+ test_files:
185
+ - spec/client_spec.rb
186
+ - spec/server_spec.rb
187
+ - spec/spec_helper.rb