slanger 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of slanger might be problematic. Click here for more details.
- data/README.md +45 -9
- data/bin/slanger +4 -4
- data/lib/slanger/api_server.rb +35 -10
- data/lib/slanger/handler.rb +5 -2
- data/lib/slanger/redis.rb +7 -9
- data/slanger.rb +1 -1
- metadata +136 -48
data/README.md
CHANGED
@@ -1,12 +1,47 @@
|
|
1
1
|
# Slanger
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
##Typical usage
|
3
|
+
|
4
|
+
Slanger is a standalone server ruby implementation of the Pusher protocol. It
|
5
|
+
is not designed to run inside a Rails or sinatra app, but it can be easily
|
6
|
+
installed as a gem.
|
7
|
+
|
8
|
+
```
|
9
|
+
#see the section further down for How To Use It
|
10
|
+
gem install slanger
|
11
|
+
```
|
12
|
+
|
13
|
+
##You probably don't mean this:
|
14
|
+
|
15
|
+
Gemfile
|
16
|
+
```
|
17
|
+
gem 'slanger'
|
18
|
+
```
|
19
|
+
|
20
|
+
##About
|
21
|
+
Slanger is an open source server implementation of the Pusher protocol written
|
22
|
+
in Ruby. It is designed to scale horizontally across N nodes and to be agnostic
|
23
|
+
as to which Slanger node a subscriber is connected to, i.e subscribers to the
|
24
|
+
same channel are NOT required to be connected to the same Slanger node.
|
25
|
+
Multiple Slanger nodes can sit behind a load balancer with no special
|
26
|
+
configuration. In essence it was designed to be very easy to scale.
|
27
|
+
|
28
|
+
Presence channel state is shared using Redis. Channels are lazily instantiated
|
29
|
+
internally within a given Slanger node when the first subscriber connects. When
|
30
|
+
a presence channel is instantiated within a Slanger node, it queries Redis for
|
31
|
+
the global state across all nodes within the system for that channel, and then
|
32
|
+
copies that state internally. Afterwards, when subscribers connect or
|
33
|
+
disconnect the node publishes a presence message to all interested nodes, i.e.
|
34
|
+
all nodes with at least one subscriber interested in the given channel.
|
35
|
+
|
36
|
+
Slanger is smart enough to know if a new channel subscription belongs to the
|
37
|
+
same user. It will not send presence messages to subscribers in this case. This
|
38
|
+
happens when the user has multiple browser tabs open for example. Using a chat
|
39
|
+
room backed by presence channels as a real example, one would not want
|
40
|
+
"Barrington" to show up N times in the presence roster because Barrington is a
|
41
|
+
retard and has the chat room open in N browser tabs.
|
42
|
+
|
43
|
+
Slanger was designed to be highly available and partition tolerant with
|
44
|
+
eventual consistency, which in practise is instantaneous.
|
10
45
|
|
11
46
|
# How to use it
|
12
47
|
|
@@ -75,6 +110,7 @@ You will also need to do the same to the Pusher JavaScript client in your client
|
|
75
110
|
|
76
111
|
Pusher.host = 'slanger.example.com'
|
77
112
|
Pusher.ws_port = 8080
|
113
|
+
Pusher.wss_port = 8080
|
78
114
|
|
79
115
|
</script>
|
80
116
|
</pre>
|
@@ -102,7 +138,7 @@ Slanger supports several configuration options, which can be supplied as command
|
|
102
138
|
|
103
139
|
-b or --webhook_url URL for webhooks. This argument is optional, if given webhook callbacks will be made http://pusher.com/docs/webhooks
|
104
140
|
|
105
|
-
-c or --
|
141
|
+
-c or --cert_file Certificate file for SSL support. This argument is optional, if given, SSL will be enabled
|
106
142
|
|
107
143
|
-v or --[no-]verbose This makes Slanger run verbosely, meaning WebSocket frames will be echoed to STDOUT. Useful for debugging
|
108
144
|
</pre>
|
data/bin/slanger
CHANGED
@@ -50,9 +50,9 @@ OptionParser.new do |opts|
|
|
50
50
|
options[:webhook_url] = p
|
51
51
|
end
|
52
52
|
|
53
|
-
opts.on '-c', '--
|
53
|
+
opts.on '-c', '--cert_file FILE', "Certificate file for SSL transport" do |p|
|
54
54
|
options[:tls_options] ||= {}
|
55
|
-
options[:tls_options][:
|
55
|
+
options[:tls_options][:cert_file] = p
|
56
56
|
end
|
57
57
|
|
58
58
|
opts.on "-v", "--[no-]verbose", "Run verbosely" do |v|
|
@@ -75,9 +75,9 @@ end
|
|
75
75
|
|
76
76
|
|
77
77
|
if options[:tls_options]
|
78
|
-
[:
|
78
|
+
[:cert_file, :private_key_file].each do |param|
|
79
79
|
raise RuntimeError.new "--#{param} does not exist at `#{options[:tls_options][param]}`" unless File.exists? options[:tls_options][param]
|
80
|
-
raise RuntimeError.new "Both --
|
80
|
+
raise RuntimeError.new "Both --cert_file and --private_key_file need to be specified" unless options[:tls_options][param]
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
data/lib/slanger/api_server.rb
CHANGED
@@ -18,31 +18,56 @@ module Slanger
|
|
18
18
|
# Respond with HTTP 401 Unauthorized if request cannot be authenticated.
|
19
19
|
error(Signature::AuthenticationError) { |c| halt 401, "401 UNAUTHORIZED\n" }
|
20
20
|
|
21
|
+
post '/apps/:app_id/events' do
|
22
|
+
authenticate
|
23
|
+
|
24
|
+
# Event and channel data are now serialized in the JSON data
|
25
|
+
# So, extract and use it
|
26
|
+
data = JSON.parse(request.body.read.tap{ |s| s.force_encoding('utf-8')})
|
27
|
+
|
28
|
+
# Send event to each channel
|
29
|
+
data["channels"].each { |channel| publish(channel, data['name'], data['data']) }
|
30
|
+
|
31
|
+
return {}.to_json
|
32
|
+
end
|
33
|
+
|
21
34
|
post '/apps/:app_id/channels/:channel_id/events' do
|
35
|
+
authenticate
|
36
|
+
|
37
|
+
publish(params[:channel_id], params['name'], request.body.read.tap{ |s| s.force_encoding('utf-8') })
|
38
|
+
|
39
|
+
return {}.to_json
|
40
|
+
end
|
41
|
+
|
42
|
+
def payload(channel, event, data)
|
43
|
+
{
|
44
|
+
event: event,
|
45
|
+
data: data,
|
46
|
+
channel: channel,
|
47
|
+
socket_id: params[:socket_id]
|
48
|
+
}.select { |_,v| v }.to_json
|
49
|
+
end
|
50
|
+
|
51
|
+
def authenticate
|
22
52
|
# authenticate request. exclude 'channel_id' and 'app_id' included by sinatra but not sent by Pusher.
|
23
53
|
# Raises Signature::AuthenticationError if request does not authenticate.
|
24
54
|
Signature::Request.new('POST', env['PATH_INFO'], params.except('channel_id', 'app_id')).
|
25
55
|
authenticate { |key| Signature::Token.new key, Slanger::Config.secret }
|
56
|
+
end
|
26
57
|
|
58
|
+
def publish(channel, event, data)
|
27
59
|
f = Fiber.current
|
60
|
+
|
28
61
|
# Publish the event in Redis and translate the result into an HTTP
|
29
62
|
# status to return to the client.
|
30
|
-
Slanger::Redis.publish(
|
63
|
+
Slanger::Redis.publish(channel, payload(channel, event, data)).tap do |r|
|
31
64
|
r.callback { f.resume [202, {}, "202 ACCEPTED\n"] }
|
32
65
|
r.errback { f.resume [500, {}, "500 INTERNAL SERVER ERROR\n"] }
|
33
66
|
end
|
67
|
+
|
34
68
|
Fiber.yield
|
35
69
|
end
|
36
70
|
|
37
|
-
def payload
|
38
|
-
payload = {
|
39
|
-
event: params['name'],
|
40
|
-
data: request.body.read.tap{ |s| s.force_encoding('utf-8') },
|
41
|
-
channel: params[:channel_id],
|
42
|
-
socket_id: params[:socket_id]
|
43
|
-
}
|
44
|
-
Hash[payload.reject { |_,v| v.nil? }].to_json
|
45
|
-
end
|
46
71
|
end
|
47
72
|
end
|
48
73
|
|
data/lib/slanger/handler.rb
CHANGED
@@ -35,11 +35,14 @@ module Slanger
|
|
35
35
|
rescue JSON::ParserError
|
36
36
|
error({ code: 5001, message: "Invalid JSON" })
|
37
37
|
rescue Exception => e
|
38
|
-
error({ code: 500, message: "#{e.message}\n #{e.backtrace}" })
|
38
|
+
error({ code: 500, message: "#{e.message}\n #{e.backtrace.join "\n"}" })
|
39
39
|
end
|
40
40
|
|
41
41
|
def onclose
|
42
|
-
@subscriptions.
|
42
|
+
@subscriptions.select { |k,v| k && v }.
|
43
|
+
each do |channel_id, subscription_id|
|
44
|
+
Channel.unsubscribe channel_id, subscription_id
|
45
|
+
end
|
43
46
|
end
|
44
47
|
|
45
48
|
def authenticate
|
data/lib/slanger/redis.rb
CHANGED
@@ -7,15 +7,6 @@ module Slanger
|
|
7
7
|
module Redis
|
8
8
|
extend Forwardable
|
9
9
|
|
10
|
-
def self.extended base
|
11
|
-
# Dispatch messages received from Redis to their destination channel.
|
12
|
-
base.on(:message) do |channel, message|
|
13
|
-
message = JSON.parse message
|
14
|
-
c = Channel.from message['channel']
|
15
|
-
c.dispatch message, channel
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
10
|
def_delegator :publisher, :publish
|
20
11
|
def_delegators :subscriber, :on, :subscribe
|
21
12
|
def_delegators :regular_connection, :hgetall, :hdel, :hset, :hincrby
|
@@ -39,5 +30,12 @@ module Slanger
|
|
39
30
|
end
|
40
31
|
|
41
32
|
extend self
|
33
|
+
|
34
|
+
# Dispatch messages received from Redis to their destination channel.
|
35
|
+
on(:message) do |channel, message|
|
36
|
+
message = JSON.parse message
|
37
|
+
c = Channel.from message['channel']
|
38
|
+
c.dispatch message, channel
|
39
|
+
end
|
42
40
|
end
|
43
41
|
end
|
data/slanger.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slanger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 0.12.10
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.12.10
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: em-hiredis
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: 0.1.0
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: em-websocket
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ~>
|
@@ -43,54 +53,79 @@ dependencies:
|
|
43
53
|
version: 0.3.5
|
44
54
|
type: :runtime
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.5
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: rack
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ~>
|
53
68
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.3
|
69
|
+
version: '1.3'
|
55
70
|
type: :runtime
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.3'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: rack-fiber_pool
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
|
-
- - =
|
83
|
+
- - '='
|
64
84
|
- !ruby/object:Gem::Version
|
65
85
|
version: 0.9.1
|
66
86
|
type: :runtime
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - '='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.9.1
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: signature
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ~>
|
75
100
|
- !ruby/object:Gem::Version
|
76
|
-
version: 0.1.
|
101
|
+
version: 0.1.6
|
77
102
|
type: :runtime
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.1.6
|
80
110
|
- !ruby/object:Gem::Dependency
|
81
111
|
name: activesupport
|
82
|
-
requirement:
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
83
113
|
none: false
|
84
114
|
requirements:
|
85
115
|
- - ~>
|
86
116
|
- !ruby/object:Gem::Version
|
87
|
-
version: 3.1
|
117
|
+
version: '3.1'
|
88
118
|
type: :runtime
|
89
119
|
prerelease: false
|
90
|
-
version_requirements:
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3.1'
|
91
126
|
- !ruby/object:Gem::Dependency
|
92
127
|
name: glamazon
|
93
|
-
requirement:
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
94
129
|
none: false
|
95
130
|
requirements:
|
96
131
|
- - ~>
|
@@ -98,10 +133,15 @@ dependencies:
|
|
98
133
|
version: 0.3.1
|
99
134
|
type: :runtime
|
100
135
|
prerelease: false
|
101
|
-
version_requirements:
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.3.1
|
102
142
|
- !ruby/object:Gem::Dependency
|
103
143
|
name: sinatra
|
104
|
-
requirement:
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
105
145
|
none: false
|
106
146
|
requirements:
|
107
147
|
- - ~>
|
@@ -109,10 +149,15 @@ dependencies:
|
|
109
149
|
version: 1.2.6
|
110
150
|
type: :runtime
|
111
151
|
prerelease: false
|
112
|
-
version_requirements:
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.2.6
|
113
158
|
- !ruby/object:Gem::Dependency
|
114
159
|
name: thin
|
115
|
-
requirement:
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
116
161
|
none: false
|
117
162
|
requirements:
|
118
163
|
- - ~>
|
@@ -120,10 +165,15 @@ dependencies:
|
|
120
165
|
version: 1.2.11
|
121
166
|
type: :runtime
|
122
167
|
prerelease: false
|
123
|
-
version_requirements:
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.2.11
|
124
174
|
- !ruby/object:Gem::Dependency
|
125
175
|
name: em-http-request
|
126
|
-
requirement:
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
127
177
|
none: false
|
128
178
|
requirements:
|
129
179
|
- - ~>
|
@@ -131,32 +181,47 @@ dependencies:
|
|
131
181
|
version: 0.3.0
|
132
182
|
type: :runtime
|
133
183
|
prerelease: false
|
134
|
-
version_requirements:
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 0.3.0
|
135
190
|
- !ruby/object:Gem::Dependency
|
136
191
|
name: rspec
|
137
|
-
requirement:
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
138
193
|
none: false
|
139
194
|
requirements:
|
140
195
|
- - ~>
|
141
196
|
- !ruby/object:Gem::Version
|
142
|
-
version: 2.6
|
197
|
+
version: '2.6'
|
143
198
|
type: :development
|
144
199
|
prerelease: false
|
145
|
-
version_requirements:
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ~>
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '2.6'
|
146
206
|
- !ruby/object:Gem::Dependency
|
147
207
|
name: pusher
|
148
|
-
requirement:
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
149
209
|
none: false
|
150
210
|
requirements:
|
151
211
|
- - ~>
|
152
212
|
- !ruby/object:Gem::Version
|
153
|
-
version: 0.
|
213
|
+
version: 0.11.3
|
154
214
|
type: :development
|
155
215
|
prerelease: false
|
156
|
-
version_requirements:
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ~>
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: 0.11.3
|
157
222
|
- !ruby/object:Gem::Dependency
|
158
223
|
name: haml
|
159
|
-
requirement:
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
160
225
|
none: false
|
161
226
|
requirements:
|
162
227
|
- - ~>
|
@@ -164,10 +229,15 @@ dependencies:
|
|
164
229
|
version: 3.1.2
|
165
230
|
type: :development
|
166
231
|
prerelease: false
|
167
|
-
version_requirements:
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ~>
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: 3.1.2
|
168
238
|
- !ruby/object:Gem::Dependency
|
169
239
|
name: rake
|
170
|
-
requirement:
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
171
241
|
none: false
|
172
242
|
requirements:
|
173
243
|
- - ! '>='
|
@@ -175,10 +245,15 @@ dependencies:
|
|
175
245
|
version: '0'
|
176
246
|
type: :development
|
177
247
|
prerelease: false
|
178
|
-
version_requirements:
|
248
|
+
version_requirements: !ruby/object:Gem::Requirement
|
249
|
+
none: false
|
250
|
+
requirements:
|
251
|
+
- - ! '>='
|
252
|
+
- !ruby/object:Gem::Version
|
253
|
+
version: '0'
|
179
254
|
- !ruby/object:Gem::Dependency
|
180
255
|
name: timecop
|
181
|
-
requirement:
|
256
|
+
requirement: !ruby/object:Gem::Requirement
|
182
257
|
none: false
|
183
258
|
requirements:
|
184
259
|
- - ~>
|
@@ -186,10 +261,15 @@ dependencies:
|
|
186
261
|
version: 0.3.5
|
187
262
|
type: :development
|
188
263
|
prerelease: false
|
189
|
-
version_requirements:
|
264
|
+
version_requirements: !ruby/object:Gem::Requirement
|
265
|
+
none: false
|
266
|
+
requirements:
|
267
|
+
- - ~>
|
268
|
+
- !ruby/object:Gem::Version
|
269
|
+
version: 0.3.5
|
190
270
|
- !ruby/object:Gem::Dependency
|
191
271
|
name: webmock
|
192
|
-
requirement:
|
272
|
+
requirement: !ruby/object:Gem::Requirement
|
193
273
|
none: false
|
194
274
|
requirements:
|
195
275
|
- - ~>
|
@@ -197,10 +277,15 @@ dependencies:
|
|
197
277
|
version: 1.8.7
|
198
278
|
type: :development
|
199
279
|
prerelease: false
|
200
|
-
version_requirements:
|
280
|
+
version_requirements: !ruby/object:Gem::Requirement
|
281
|
+
none: false
|
282
|
+
requirements:
|
283
|
+
- - ~>
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: 1.8.7
|
201
286
|
- !ruby/object:Gem::Dependency
|
202
287
|
name: mocha
|
203
|
-
requirement:
|
288
|
+
requirement: !ruby/object:Gem::Requirement
|
204
289
|
none: false
|
205
290
|
requirements:
|
206
291
|
- - ~>
|
@@ -208,7 +293,12 @@ dependencies:
|
|
208
293
|
version: 0.12.0
|
209
294
|
type: :development
|
210
295
|
prerelease: false
|
211
|
-
version_requirements:
|
296
|
+
version_requirements: !ruby/object:Gem::Requirement
|
297
|
+
none: false
|
298
|
+
requirements:
|
299
|
+
- - ~>
|
300
|
+
- !ruby/object:Gem::Version
|
301
|
+
version: 0.12.0
|
212
302
|
description: A websocket service compatible with Pusher libraries
|
213
303
|
email: sjtgraham@mac.com
|
214
304
|
executables:
|
@@ -232,8 +322,7 @@ files:
|
|
232
322
|
- lib/slanger/web_socket_server.rb
|
233
323
|
- lib/slanger/webhook.rb
|
234
324
|
- slanger.rb
|
235
|
-
-
|
236
|
-
YmluL3NsYW5nZXI=
|
325
|
+
- bin/slanger
|
237
326
|
homepage: http://github.com/stevegraham/slanger
|
238
327
|
licenses: []
|
239
328
|
post_install_message:
|
@@ -254,9 +343,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
343
|
version: '0'
|
255
344
|
requirements: []
|
256
345
|
rubyforge_project:
|
257
|
-
rubygems_version: 1.8.
|
346
|
+
rubygems_version: 1.8.24
|
258
347
|
signing_key:
|
259
348
|
specification_version: 3
|
260
349
|
summary: A websocket service compatible with Pusher libraries
|
261
350
|
test_files: []
|
262
|
-
has_rdoc:
|