wamp 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +76 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +30 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/demo/app.rb +34 -0
- data/demo/client.html +15 -0
- data/demo/client.js +66 -0
- data/demo/config.ru +3 -0
- data/lib/wamp.rb +26 -0
- data/lib/wamp/message_type.rb +29 -0
- data/lib/wamp/server.rb +146 -0
- data/lib/wamp/socket.rb +36 -0
- data/lib/wamp/topic.rb +9 -0
- data/spec/lib/wamp/message_type_spec.rb +28 -0
- data/spec/lib/wamp/server_spec.rb +33 -0
- data/spec/lib/wamp/socket_spec.rb +35 -0
- data/spec/lib/wamp/topic_spec.rb +10 -0
- data/spec/lib/wamp_spec.rb +19 -0
- data/spec/spec_helper.rb +13 -0
- data/wamp.gemspec +91 -0
- metadata +236 -0
data/.document
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem 'thin'
|
6
|
+
gem 'faye-websocket'
|
7
|
+
|
8
|
+
# Add dependencies to develop your gem here.
|
9
|
+
# Include everything needed to run rake, tests, features, etc.
|
10
|
+
group :development do
|
11
|
+
gem "rspec"
|
12
|
+
gem 'guard-rspec'
|
13
|
+
gem 'rb-fsevent'
|
14
|
+
gem 'rb-inotify'
|
15
|
+
gem 'fuubar'
|
16
|
+
gem "rdoc", "~> 3.12"
|
17
|
+
gem "bundler"
|
18
|
+
gem "jeweler", "~> 1.8.4"
|
19
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
coderay (1.0.9)
|
5
|
+
daemons (1.1.9)
|
6
|
+
diff-lcs (1.2.1)
|
7
|
+
eventmachine (1.0.3)
|
8
|
+
faye-websocket (0.4.7)
|
9
|
+
eventmachine (>= 0.12.0)
|
10
|
+
ffi (1.6.0)
|
11
|
+
formatador (0.2.4)
|
12
|
+
fuubar (1.1.0)
|
13
|
+
rspec (~> 2.0)
|
14
|
+
rspec-instafail (~> 0.2.0)
|
15
|
+
ruby-progressbar (~> 1.0.0)
|
16
|
+
git (1.2.5)
|
17
|
+
guard (1.7.0)
|
18
|
+
formatador (>= 0.2.4)
|
19
|
+
listen (>= 0.6.0)
|
20
|
+
lumberjack (>= 1.0.2)
|
21
|
+
pry (>= 0.9.10)
|
22
|
+
thor (>= 0.14.6)
|
23
|
+
guard-rspec (2.5.1)
|
24
|
+
guard (>= 1.1)
|
25
|
+
rspec (~> 2.11)
|
26
|
+
jeweler (1.8.4)
|
27
|
+
bundler (~> 1.0)
|
28
|
+
git (>= 1.2.5)
|
29
|
+
rake
|
30
|
+
rdoc
|
31
|
+
json (1.7.7)
|
32
|
+
listen (0.7.3)
|
33
|
+
lumberjack (1.0.3)
|
34
|
+
method_source (0.8.1)
|
35
|
+
pry (0.9.12)
|
36
|
+
coderay (~> 1.0.5)
|
37
|
+
method_source (~> 0.8)
|
38
|
+
slop (~> 3.4)
|
39
|
+
rack (1.5.2)
|
40
|
+
rake (10.0.4)
|
41
|
+
rb-fsevent (0.9.3)
|
42
|
+
rb-inotify (0.9.0)
|
43
|
+
ffi (>= 0.5.0)
|
44
|
+
rdoc (3.12.2)
|
45
|
+
json (~> 1.4)
|
46
|
+
rspec (2.13.0)
|
47
|
+
rspec-core (~> 2.13.0)
|
48
|
+
rspec-expectations (~> 2.13.0)
|
49
|
+
rspec-mocks (~> 2.13.0)
|
50
|
+
rspec-core (2.13.1)
|
51
|
+
rspec-expectations (2.13.0)
|
52
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
53
|
+
rspec-instafail (0.2.4)
|
54
|
+
rspec-mocks (2.13.0)
|
55
|
+
ruby-progressbar (1.0.2)
|
56
|
+
slop (3.4.4)
|
57
|
+
thin (1.5.1)
|
58
|
+
daemons (>= 1.0.9)
|
59
|
+
eventmachine (>= 0.12.6)
|
60
|
+
rack (>= 1.0.0)
|
61
|
+
thor (0.18.0)
|
62
|
+
|
63
|
+
PLATFORMS
|
64
|
+
ruby
|
65
|
+
|
66
|
+
DEPENDENCIES
|
67
|
+
bundler
|
68
|
+
faye-websocket
|
69
|
+
fuubar
|
70
|
+
guard-rspec
|
71
|
+
jeweler (~> 1.8.4)
|
72
|
+
rb-fsevent
|
73
|
+
rb-inotify
|
74
|
+
rdoc (~> 3.12)
|
75
|
+
rspec
|
76
|
+
thin
|
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Brady Love
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
## WAMP Ruby
|
2
|
+
|
3
|
+
This is an implementation of the WAMP (WebSocket Application Messaging Protocol http://www.wamp.ws) in Ruby. This implementation of the WAMP protocol is sitting on top of of the Faye WebSockets (https://github.com/faye/faye-websocket-ruby) gem.
|
4
|
+
|
5
|
+
Development has just started and is very incomplete. Currently only server side of the protocol, client will be coming soon. If you are interested in helping out please feel free to fork and send pull requests :).
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
Coming soon! For now please check out the demo directory.
|
9
|
+
|
10
|
+
## License
|
11
|
+
Copyright (c) 2013 Brady Love
|
12
|
+
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
14
|
+
a copy of this software and associated documentation files (the
|
15
|
+
"Software"), to deal in the Software without restriction, including
|
16
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
17
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
18
|
+
permit persons to whom the Software is furnished to do so, subject to
|
19
|
+
the following conditions:
|
20
|
+
|
21
|
+
The above copyright notice and this permission notice shall be
|
22
|
+
included in all copies or substantial portions of the Software.
|
23
|
+
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
25
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
26
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
27
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
28
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
29
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
30
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require './lib/wamp'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Bundler.setup(:default, :development)
|
9
|
+
rescue Bundler::BundlerError => e
|
10
|
+
$stderr.puts e.message
|
11
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
+
exit e.status_code
|
13
|
+
end
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "wamp"
|
20
|
+
gem.homepage = "http://github.com/bradylove/wamp-ruby"
|
21
|
+
gem.license = "MIT"
|
22
|
+
gem.summary = %Q{A Ruby implementation of the WAMP WebSocket subprotocol}
|
23
|
+
gem.description = %Q{A Ruby implementation of the WAMP (Web Application Messaging Protocol) WebSocket subprotocol}
|
24
|
+
gem.email = "love.brady@gmail.com"
|
25
|
+
gem.authors = ["Brady Love"]
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rspec/core'
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
33
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rdoc/task'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = WAMP.version
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "WAMP #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/demo/app.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require '../lib/wamp'
|
2
|
+
require 'json'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
App = WAMP::Server.new
|
6
|
+
|
7
|
+
def log(text)
|
8
|
+
puts "[#{Time.now}] #{text}"
|
9
|
+
end
|
10
|
+
|
11
|
+
App.bind(:connect) do |client|
|
12
|
+
log "#{client.id} connected"
|
13
|
+
end
|
14
|
+
|
15
|
+
App.bind(:prefix) do |client, prefix, uri|
|
16
|
+
log "#{client.id} negotiated #{prefix} as #{uri}"
|
17
|
+
log "#{client.id} prefixes: #{client.prefixes.to_s}"
|
18
|
+
end
|
19
|
+
|
20
|
+
App.bind(:subscribe) do |client, topic|
|
21
|
+
log "#{client.id} subscribed to #{topic}"
|
22
|
+
end
|
23
|
+
|
24
|
+
App.bind(:unsubscribe) do |client, topic|
|
25
|
+
log "#{client.id} unsubscribed from #{topic}"
|
26
|
+
end
|
27
|
+
|
28
|
+
App.bind(:publish) do |client, topic, data|
|
29
|
+
log "#{client.id} published #{data} to #{topic}"
|
30
|
+
end
|
31
|
+
|
32
|
+
App.bind(:disconnect) do |client|
|
33
|
+
log "#{client.id} disconnected"
|
34
|
+
end
|
data/demo/client.html
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<script src="http://autobahn.s3.amazonaws.com/js/autobahn.js"></script>
|
5
|
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
6
|
+
<script src="client.js"></script>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<div id="debug-data"></div>
|
10
|
+
<button onclick="connect();">Connect</button>
|
11
|
+
<input type="text" id="prefix-curie"></input><button onclick="registerPrefix()">Register Prefix</button>
|
12
|
+
<input type="text" id="topic-name"></input><button onclick="subscribe()">Subscribe</button><button onclick="unsubscribe()">Unsubscribe</button>
|
13
|
+
<input type="text" id="simple-msg"></input><button onclick="sendSimpleMsg()">Send</button>
|
14
|
+
</abody>
|
15
|
+
</html>
|
data/demo/client.js
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
var wamp, topicName, topicPath, prefix;
|
2
|
+
var wsuri = "ws://localhost:9292";
|
3
|
+
|
4
|
+
debugData = function(msg){
|
5
|
+
$("#debug-data").append(msg + '</br>')
|
6
|
+
}
|
7
|
+
|
8
|
+
connect = function(){
|
9
|
+
ab.connect(wsuri,
|
10
|
+
// WAMP session was established
|
11
|
+
function (session) {
|
12
|
+
debugData("Session established.");
|
13
|
+
wamp = session;
|
14
|
+
},
|
15
|
+
|
16
|
+
// WAMP session is gone
|
17
|
+
function (code, reason) {
|
18
|
+
switch (code) {
|
19
|
+
case ab.CONNECTION_CLOSED:
|
20
|
+
debugData("Connection was closed properly - done.");
|
21
|
+
break;
|
22
|
+
case ab.CONNECTION_UNREACHABLE:
|
23
|
+
debugData("Connection could not be established.");
|
24
|
+
break;
|
25
|
+
case ab.CONNECTION_UNSUPPORTED:
|
26
|
+
debugData("Browser does not support WebSocket.");
|
27
|
+
break;
|
28
|
+
case ab.CONNECTION_LOST:
|
29
|
+
debugData("Connection lost - reconnecting ...");
|
30
|
+
break;
|
31
|
+
}
|
32
|
+
|
33
|
+
debugData("Session closed, code " + code + ", reason:" + reason)
|
34
|
+
}
|
35
|
+
);
|
36
|
+
};
|
37
|
+
|
38
|
+
subscribe = function(){
|
39
|
+
wamp.subscribe(prefix, onEvent);
|
40
|
+
}
|
41
|
+
|
42
|
+
unsubscribe = function(){
|
43
|
+
wamp.unsubscribe(prefix, onEvent);
|
44
|
+
topicPath = null;
|
45
|
+
}
|
46
|
+
|
47
|
+
registerPrefix = function(){
|
48
|
+
prefix = $("#prefix-curie").val()
|
49
|
+
topicName = $("#topic-name").val();
|
50
|
+
topicPath = wsuri + topicName;
|
51
|
+
|
52
|
+
wamp.prefix(prefix, topicPath);
|
53
|
+
}
|
54
|
+
|
55
|
+
function onEvent(topicUri, event) {
|
56
|
+
debugData(topicUri);
|
57
|
+
debugData(event);
|
58
|
+
}
|
59
|
+
|
60
|
+
function sendSimpleMsg()
|
61
|
+
{
|
62
|
+
msg = $("#simple-msg").val();
|
63
|
+
$("#simple-msg").val("");
|
64
|
+
|
65
|
+
wamp.publish(prefix, msg);
|
66
|
+
}
|
data/demo/config.ru
ADDED
data/lib/wamp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module WAMP
|
2
|
+
MAJOR = 0
|
3
|
+
MINOR = 0
|
4
|
+
PATCH = 1
|
5
|
+
|
6
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
autoload :Server, File.join(ROOT, "wamp", "server")
|
9
|
+
autoload :Socket, File.join(ROOT, "wamp", "socket")
|
10
|
+
autoload :Topic, File.join(ROOT, "wamp", "topic")
|
11
|
+
autoload :MessageType, File.join(ROOT, "wamp", "message_type")
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def version
|
15
|
+
"#{MAJOR}.#{MINOR}.#{PATCH}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def identity
|
19
|
+
"WAMP Ruby/#{self.version}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def protocol_version
|
23
|
+
1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module WAMP::MessageType
|
2
|
+
# Message types with ID per the WAMP Specification located at
|
3
|
+
# http://wamp.ws/spec#message_types
|
4
|
+
TYPES = {
|
5
|
+
WELCOME: 0,
|
6
|
+
PREFIX: 1,
|
7
|
+
CALL: 2,
|
8
|
+
CALLRESULT: 3,
|
9
|
+
CALLERROR: 4,
|
10
|
+
SUBSCRIBE: 5,
|
11
|
+
UNSUBSCRIBE: 6,
|
12
|
+
PUBLISH: 7,
|
13
|
+
EVENT: 8
|
14
|
+
}
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# Get MessageType ID with symbolized name, or get symbolized name with an ID
|
18
|
+
# Usage:
|
19
|
+
# WAMP::MessageType[:WELCOME] #=> 0
|
20
|
+
# WAMP::MessageType[0] #=> :WELCOME
|
21
|
+
def [](id)
|
22
|
+
if id.is_a? Integer
|
23
|
+
TYPES.key(id)
|
24
|
+
else
|
25
|
+
TYPES[id]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/wamp/server.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'faye/websocket'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module WAMP
|
5
|
+
class Server
|
6
|
+
attr_accessor :options, :sockets, :topics
|
7
|
+
|
8
|
+
CALLBACKS = [:subscribe, :unsubscribe, :publish, :call, :prefix, :connect, :disconnect]
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = options
|
12
|
+
@sockets = {}
|
13
|
+
@topics = {}
|
14
|
+
|
15
|
+
@callbacks = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
lambda do |env|
|
20
|
+
# Faye::WebSocket.load_adapter('thin')
|
21
|
+
if Faye::WebSocket.websocket?(env)
|
22
|
+
ws = Faye::WebSocket.new(env, ['wamp'], ping: 25)
|
23
|
+
|
24
|
+
ws.onopen = lambda { |event| handle_open(ws, event) }
|
25
|
+
ws.onmessage = lambda { |event| handle_message(ws, event) }
|
26
|
+
ws.onclose = lambda { |event| handle_close(ws, event) }
|
27
|
+
|
28
|
+
ws.rack_response
|
29
|
+
else
|
30
|
+
# Normal HTTP request
|
31
|
+
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def bind(name, &callback)
|
37
|
+
raise "Invalid callback name: #{name}" unless CALLBACKS.include? name
|
38
|
+
@callbacks[name] = callback
|
39
|
+
end
|
40
|
+
|
41
|
+
def trigger(name, *args)
|
42
|
+
@callbacks[name].call *args if @callbacks[name]
|
43
|
+
end
|
44
|
+
|
45
|
+
def send_event_to_all
|
46
|
+
msg = [8, 'ws://localhost:9292/', "Here I am, Rock you like a hurricane"]
|
47
|
+
@sockets.each_pair do |s, c|
|
48
|
+
s.send msg.to_json
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def handle_open(websocket, event)
|
55
|
+
socket = @sockets[websocket] = WAMP::Socket.new(websocket)
|
56
|
+
|
57
|
+
trigger(:connect, socket)
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_message(websocket, event)
|
61
|
+
socket = @sockets[websocket]
|
62
|
+
|
63
|
+
parsed_msg = JSON.parse(event.data)
|
64
|
+
msg_type = parsed_msg[0]
|
65
|
+
|
66
|
+
case WAMP::MessageType[msg_type]
|
67
|
+
when :PREFIX
|
68
|
+
prefix = parsed_msg[1]
|
69
|
+
uri = parsed_msg[2]
|
70
|
+
|
71
|
+
socket.add_prefix(prefix, uri)
|
72
|
+
|
73
|
+
trigger(:prefix, socket, prefix, uri)
|
74
|
+
when :CALL
|
75
|
+
# TODO handle RPC Call
|
76
|
+
when :SUBSCRIBE
|
77
|
+
handle_subscribe(socket, parsed_msg)
|
78
|
+
when :UNSUBSCRIBE
|
79
|
+
topic_name = parsed_msg[1]
|
80
|
+
topic = @topics[topic_name]
|
81
|
+
|
82
|
+
trigger(:unsubscribe, socket, topic.name)
|
83
|
+
when :PUBLISH
|
84
|
+
handle_publish(socket, parsed_msg)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Handle a subscribe message from a client
|
89
|
+
# SUBSCRIBE data structure [5, TOPIC]
|
90
|
+
def handle_subscribe(socket, data)
|
91
|
+
topic_uri = data[1]
|
92
|
+
|
93
|
+
@topics[topic_uri] ||= WAMP::Topic.new(topic_uri)
|
94
|
+
socket.add_topic(@topics[topic_uri])
|
95
|
+
|
96
|
+
trigger(:subscribe, socket, topic.name)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Handle an unsubscribe message from client
|
100
|
+
# UNSUBSCRIBE data structure [6, TOPIC]
|
101
|
+
def handle_unsubscribe(socket, data)
|
102
|
+
topic_uri = data[1]
|
103
|
+
topic = @topics[topic_uri]
|
104
|
+
|
105
|
+
socket.remove_topic(topic) if topic
|
106
|
+
|
107
|
+
trigger(:unsubscribe, socket, topic.name)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Handle a message published by a client
|
111
|
+
# PUBLISH data structure [7, TOPIC, DATA, EXCLUDE, INCLUDE]
|
112
|
+
def handle_publish(socket, data)
|
113
|
+
topic = topics[data[1]]
|
114
|
+
payload = data[2]
|
115
|
+
exclude = data[3]
|
116
|
+
include = data[4]
|
117
|
+
|
118
|
+
if exclude == true
|
119
|
+
exclude = [socket.id]
|
120
|
+
elsif exclude == false
|
121
|
+
exclude = []
|
122
|
+
end
|
123
|
+
|
124
|
+
# Send payload to all sockets subscribed to topic
|
125
|
+
@sockets.each_pair do |k, v|
|
126
|
+
next if exclude.include? k
|
127
|
+
|
128
|
+
if v.topics.include? topic
|
129
|
+
k.send [WAMP::MessageType[:EVENT], topic.name, payload].to_json
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Todo: Filter send with include
|
134
|
+
|
135
|
+
trigger(:publish, socket, topic.name, payload, exclude, include)
|
136
|
+
end
|
137
|
+
|
138
|
+
def handle_close(websocket, event)
|
139
|
+
socket = @sockets.delete(websocket)
|
140
|
+
|
141
|
+
p [socket.id, :close, event.code, event.reason]
|
142
|
+
|
143
|
+
trigger(:disconnect, socket)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/wamp/socket.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module WAMP
|
5
|
+
class Socket
|
6
|
+
attr_accessor :id, :websocket, :topics, :prefixes
|
7
|
+
|
8
|
+
def initialize(websocket)
|
9
|
+
@id = SecureRandom.uuid
|
10
|
+
@topics = []
|
11
|
+
@prefixes = {}
|
12
|
+
@websocket = websocket
|
13
|
+
|
14
|
+
send_welcome_message
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_topic(topic)
|
18
|
+
topics << topic unless topics.include? topic
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_topic(topic)
|
22
|
+
topics.delete(topic)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_prefix(prefix, uri)
|
26
|
+
prefixes[prefix] = uri
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def send_welcome_message
|
32
|
+
welcome_msg = [WAMP::MessageType[:WELCOME], id, WAMP.protocol_version, WAMP.identity]
|
33
|
+
websocket.send welcome_msg.to_json
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/wamp/topic.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WAMP::MessageType do
|
4
|
+
it "should be a module" do
|
5
|
+
expect(WAMP::MessageType.class).to eq Module
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.expect_message_type(type, id)
|
9
|
+
it "should return the message type id" do
|
10
|
+
expect(WAMP::MessageType[type]).to eq id
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return the name if given an integer" do
|
14
|
+
expect(WAMP::MessageType[id]).to eq type
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
self.expect_message_type :WELCOME, 0
|
19
|
+
self.expect_message_type :PREFIX, 1
|
20
|
+
self.expect_message_type :CALL, 2
|
21
|
+
self.expect_message_type :CALLRESULT, 3
|
22
|
+
self.expect_message_type :CALLERROR, 4
|
23
|
+
self.expect_message_type :SUBSCRIBE, 5
|
24
|
+
self.expect_message_type :UNSUBSCRIBE, 6
|
25
|
+
self.expect_message_type :PUBLISH, 7
|
26
|
+
self.expect_message_type :EVENT, 8
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WAMP::Server do
|
4
|
+
let(:server) { WAMP::Server.new(host: "localhost", port: 9292) }
|
5
|
+
|
6
|
+
context "initilization" do
|
7
|
+
it "should accept a hash of options" do
|
8
|
+
expect(server.options).to eq({ host: "localhost", port: 9292 })
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have an empty hash of sockets" do
|
12
|
+
expect(server.sockets).to eq({})
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have an empty hash of topics" do
|
16
|
+
expect(server.topics).to eq({})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "#start" do
|
21
|
+
# How the hell do I test this?
|
22
|
+
end
|
23
|
+
|
24
|
+
context "bind" do
|
25
|
+
it "should bind a subscribe callback do" do
|
26
|
+
expect { server.bind(:subscribe) { |client_id, topic| } }.to_not raise_error ""
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should raise an error if an invalid binding name is given" do
|
30
|
+
expect { server.bind(:invalid) {} }.to raise_error "Invalid callback name: invalid"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummySocket
|
4
|
+
def send(*args); true; end;
|
5
|
+
end
|
6
|
+
|
7
|
+
describe WAMP::Socket do
|
8
|
+
let(:new_socket) { WAMP::Socket.new(DummySocket.new) }
|
9
|
+
|
10
|
+
context "#initialization" do
|
11
|
+
it "should return a client" do
|
12
|
+
expect(new_socket.class).to eq WAMP::Socket
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should create a new id on creation" do
|
16
|
+
expect(new_socket.id).to_not be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should have an array of topics' do
|
20
|
+
expect(new_socket.topics).to eq []
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should send the welcome message" do
|
24
|
+
ds = DummySocket.new
|
25
|
+
ds.should_receive :send
|
26
|
+
WAMP::Socket.new(ds)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should add a new topic to client' do
|
31
|
+
topic = WAMP::Topic.new("ws://localhost:9292/sample_topic")
|
32
|
+
new_socket.add_topic topic
|
33
|
+
expect(new_socket.topics).to eq [topic]
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WAMP do
|
4
|
+
it "should be a module" do
|
5
|
+
expect(WAMP.class).to eq Module
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should have a version" do
|
9
|
+
WAMP.version.should eq "#{WAMP::MAJOR}.#{WAMP::MINOR}.#{WAMP::PATCH}"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have a server identity" do
|
13
|
+
expect(WAMP.identity).to eq "WAMP Ruby/#{WAMP.version}"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have a protocol version" do
|
17
|
+
expect(WAMP.protocol_version).to eq 1
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'wamp'
|
6
|
+
|
7
|
+
# Requires supporting files with custom matchers and macros, etc,
|
8
|
+
# in ./support/ and its subdirectories.
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
|
13
|
+
end
|
data/wamp.gemspec
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "wamp"
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Brady Love"]
|
12
|
+
s.date = "2013-04-01"
|
13
|
+
s.description = "A Ruby implementation of the WAMP (Web Application Messaging Protocol) WebSocket subprotocol"
|
14
|
+
s.email = "love.brady@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"Guardfile",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"demo/app.rb",
|
30
|
+
"demo/client.html",
|
31
|
+
"demo/client.js",
|
32
|
+
"demo/config.ru",
|
33
|
+
"lib/wamp.rb",
|
34
|
+
"lib/wamp/message_type.rb",
|
35
|
+
"lib/wamp/server.rb",
|
36
|
+
"lib/wamp/socket.rb",
|
37
|
+
"lib/wamp/topic.rb",
|
38
|
+
"spec/lib/wamp/message_type_spec.rb",
|
39
|
+
"spec/lib/wamp/server_spec.rb",
|
40
|
+
"spec/lib/wamp/socket_spec.rb",
|
41
|
+
"spec/lib/wamp/topic_spec.rb",
|
42
|
+
"spec/lib/wamp_spec.rb",
|
43
|
+
"spec/spec_helper.rb",
|
44
|
+
"wamp.gemspec"
|
45
|
+
]
|
46
|
+
s.homepage = "http://github.com/bradylove/wamp-ruby"
|
47
|
+
s.licenses = ["MIT"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = "1.8.25"
|
50
|
+
s.summary = "A Ruby implementation of the WAMP WebSocket subprotocol"
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
|
+
s.add_runtime_dependency(%q<thin>, [">= 0"])
|
57
|
+
s.add_runtime_dependency(%q<faye-websocket>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<guard-rspec>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<rb-inotify>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<fuubar>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
64
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<thin>, [">= 0"])
|
68
|
+
s.add_dependency(%q<faye-websocket>, [">= 0"])
|
69
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
70
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
71
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
72
|
+
s.add_dependency(%q<rb-inotify>, [">= 0"])
|
73
|
+
s.add_dependency(%q<fuubar>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
75
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<thin>, [">= 0"])
|
80
|
+
s.add_dependency(%q<faye-websocket>, [">= 0"])
|
81
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
82
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
83
|
+
s.add_dependency(%q<rb-fsevent>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rb-inotify>, [">= 0"])
|
85
|
+
s.add_dependency(%q<fuubar>, [">= 0"])
|
86
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
87
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
88
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
metadata
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wamp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brady Love
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thin
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: faye-websocket
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rb-fsevent
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rb-inotify
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: fuubar
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
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
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rdoc
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '3.12'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '3.12'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: bundler
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: jeweler
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 1.8.4
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.8.4
|
174
|
+
description: A Ruby implementation of the WAMP (Web Application Messaging Protocol)
|
175
|
+
WebSocket subprotocol
|
176
|
+
email: love.brady@gmail.com
|
177
|
+
executables: []
|
178
|
+
extensions: []
|
179
|
+
extra_rdoc_files:
|
180
|
+
- LICENSE.txt
|
181
|
+
- README.md
|
182
|
+
files:
|
183
|
+
- .document
|
184
|
+
- .rspec
|
185
|
+
- Gemfile
|
186
|
+
- Gemfile.lock
|
187
|
+
- Guardfile
|
188
|
+
- LICENSE.txt
|
189
|
+
- README.md
|
190
|
+
- Rakefile
|
191
|
+
- VERSION
|
192
|
+
- demo/app.rb
|
193
|
+
- demo/client.html
|
194
|
+
- demo/client.js
|
195
|
+
- demo/config.ru
|
196
|
+
- lib/wamp.rb
|
197
|
+
- lib/wamp/message_type.rb
|
198
|
+
- lib/wamp/server.rb
|
199
|
+
- lib/wamp/socket.rb
|
200
|
+
- lib/wamp/topic.rb
|
201
|
+
- spec/lib/wamp/message_type_spec.rb
|
202
|
+
- spec/lib/wamp/server_spec.rb
|
203
|
+
- spec/lib/wamp/socket_spec.rb
|
204
|
+
- spec/lib/wamp/topic_spec.rb
|
205
|
+
- spec/lib/wamp_spec.rb
|
206
|
+
- spec/spec_helper.rb
|
207
|
+
- wamp.gemspec
|
208
|
+
homepage: http://github.com/bradylove/wamp-ruby
|
209
|
+
licenses:
|
210
|
+
- MIT
|
211
|
+
post_install_message:
|
212
|
+
rdoc_options: []
|
213
|
+
require_paths:
|
214
|
+
- lib
|
215
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ! '>='
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
segments:
|
222
|
+
- 0
|
223
|
+
hash: 4093671401028789804
|
224
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ! '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
requirements: []
|
231
|
+
rubyforge_project:
|
232
|
+
rubygems_version: 1.8.25
|
233
|
+
signing_key:
|
234
|
+
specification_version: 3
|
235
|
+
summary: A Ruby implementation of the WAMP WebSocket subprotocol
|
236
|
+
test_files: []
|