faye 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +14 -0
- data/README.rdoc +98 -0
- data/Rakefile +17 -15
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +14 -5
- data/lib/faye/adapters/rack_adapter.rb +12 -5
- data/lib/faye/engines/base.rb +62 -0
- data/lib/faye/engines/connection.rb +63 -0
- data/lib/faye/engines/memory.rb +89 -0
- data/lib/faye/engines/redis.rb +141 -0
- data/lib/faye/error.rb +16 -4
- data/lib/faye/mixins/publisher.rb +6 -0
- data/lib/faye/protocol/channel.rb +34 -86
- data/lib/faye/protocol/client.rb +36 -52
- data/lib/faye/protocol/extensible.rb +3 -0
- data/lib/faye/protocol/server.rb +119 -169
- data/lib/faye/transport/http.rb +45 -0
- data/lib/faye/transport/local.rb +15 -0
- data/lib/faye/{network → transport}/transport.rb +36 -49
- data/spec/browser.html +35 -0
- data/spec/install.sh +48 -0
- data/spec/javascript/channel_spec.js +15 -0
- data/spec/javascript/client_spec.js +610 -0
- data/spec/javascript/engine_spec.js +319 -0
- data/spec/javascript/faye_spec.js +15 -0
- data/spec/javascript/grammar_spec.js +66 -0
- data/spec/javascript/node_adapter_spec.js +276 -0
- data/spec/javascript/server/connect_spec.js +168 -0
- data/spec/javascript/server/disconnect_spec.js +121 -0
- data/spec/javascript/server/extensions_spec.js +60 -0
- data/spec/javascript/server/handshake_spec.js +153 -0
- data/spec/javascript/server/subscribe_spec.js +245 -0
- data/spec/javascript/server/unsubscribe_spec.js +245 -0
- data/spec/javascript/server_spec.js +146 -0
- data/spec/javascript/transport_spec.js +130 -0
- data/spec/node.js +34 -0
- data/spec/ruby/channel_spec.rb +17 -0
- data/spec/ruby/client_spec.rb +615 -0
- data/spec/ruby/engine_spec.rb +312 -0
- data/spec/ruby/faye_spec.rb +14 -0
- data/spec/ruby/grammar_spec.rb +68 -0
- data/spec/ruby/rack_adapter_spec.rb +209 -0
- data/spec/ruby/server/connect_spec.rb +170 -0
- data/spec/ruby/server/disconnect_spec.rb +120 -0
- data/spec/ruby/server/extensions_spec.rb +69 -0
- data/spec/ruby/server/handshake_spec.rb +151 -0
- data/spec/ruby/server/subscribe_spec.rb +247 -0
- data/spec/ruby/server/unsubscribe_spec.rb +247 -0
- data/spec/ruby/server_spec.rb +138 -0
- data/spec/ruby/transport_spec.rb +128 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/testswarm.pl +200 -0
- data/spec/thin_proxy.rb +36 -0
- metadata +119 -84
- data/Manifest.txt +0 -27
- data/README.txt +0 -98
- data/lib/faye/protocol/connection.rb +0 -111
- data/test/scenario.rb +0 -172
- data/test/test_channel.rb +0 -54
- data/test/test_clients.rb +0 -381
- data/test/test_grammar.rb +0 -86
- data/test/test_server.rb +0 -488
@@ -0,0 +1,141 @@
|
|
1
|
+
module Faye
|
2
|
+
module Engine
|
3
|
+
|
4
|
+
class Redis < Base
|
5
|
+
DEFAULT_HOST = 'localhost'
|
6
|
+
DEFAULT_PORT = 6379
|
7
|
+
|
8
|
+
def init
|
9
|
+
return if @redis
|
10
|
+
require 'em-hiredis'
|
11
|
+
|
12
|
+
host = @options[:host] || DEFAULT_HOST
|
13
|
+
port = @options[:port] || DEFAULT_PORT
|
14
|
+
|
15
|
+
@redis = EventMachine::Hiredis::Client.connect(host, port)
|
16
|
+
@subscriber = EventMachine::Hiredis::Client.connect(host, port)
|
17
|
+
|
18
|
+
@subscriber.subscribe('/notifications')
|
19
|
+
@subscriber.on(:message) do |topic, message|
|
20
|
+
empty_queue(message) if topic == '/notifications'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def disconnect
|
25
|
+
@subscriber.unsubscribe('/notifications')
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_client(&callback)
|
29
|
+
init
|
30
|
+
client_id = Faye.random
|
31
|
+
@redis.sadd('/clients', client_id) do |added|
|
32
|
+
if added == 0
|
33
|
+
create_client(&callback)
|
34
|
+
else
|
35
|
+
debug 'Created new client ?', client_id
|
36
|
+
ping(client_id)
|
37
|
+
callback.call(client_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def destroy_client(client_id, &callback)
|
43
|
+
init
|
44
|
+
@redis.srem('/clients', client_id)
|
45
|
+
@redis.del("/clients/#{client_id}/messages")
|
46
|
+
@redis.smembers("/clients/#{client_id}/channels") do |channels|
|
47
|
+
n, i = channels.size, 0
|
48
|
+
if n == 0
|
49
|
+
debug 'Destroyed client ?', client_id
|
50
|
+
callback.call if callback
|
51
|
+
else
|
52
|
+
channels.each do |channel|
|
53
|
+
unsubscribe(client_id, channel) do
|
54
|
+
i += 1
|
55
|
+
if i == n
|
56
|
+
debug 'Destroyed client ?', client_id
|
57
|
+
callback.call if callback
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def client_exists(client_id, &callback)
|
66
|
+
init
|
67
|
+
@redis.sismember('/clients', client_id) do |exists|
|
68
|
+
callback.call(exists != 0)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def ping(client_id)
|
73
|
+
timeout = @options[:timeout]
|
74
|
+
time = Time.now.to_i.to_s
|
75
|
+
|
76
|
+
return unless Numeric === timeout
|
77
|
+
|
78
|
+
debug 'Ping ?, ?', client_id, timeout
|
79
|
+
remove_timeout(client_id)
|
80
|
+
@redis.set("/clients/#{client_id}/ping", time)
|
81
|
+
add_timeout(client_id, 2 * timeout) do
|
82
|
+
@redis.get("/clients/#{client_id}/ping") do |ping|
|
83
|
+
destroy_client(client_id) if ping == time
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def subscribe(client_id, channel, &callback)
|
89
|
+
init
|
90
|
+
@redis.sadd("/clients/#{client_id}/channels", channel)
|
91
|
+
@redis.sadd("/channels#{channel}", client_id) do
|
92
|
+
debug 'Subscribed client ? to channel ?', client_id, channel
|
93
|
+
callback.call if callback
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def unsubscribe(client_id, channel, &callback)
|
98
|
+
init
|
99
|
+
@redis.srem("/clients/#{client_id}/channels", channel)
|
100
|
+
@redis.srem("/channels#{channel}", client_id) do
|
101
|
+
debug 'Unsubscribed client ? from channel ?', client_id, channel
|
102
|
+
callback.call if callback
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def publish(message)
|
107
|
+
init
|
108
|
+
debug 'Publishing message ?', message
|
109
|
+
json_message = JSON.dump(message)
|
110
|
+
channels = Channel.expand(message['channel'])
|
111
|
+
channels.each do |channel|
|
112
|
+
@redis.smembers("/channels#{channel}") do |clients|
|
113
|
+
clients.each do |client_id|
|
114
|
+
debug 'Queueing for client ?: ?', client_id, message
|
115
|
+
@redis.sadd("/clients/#{client_id}/messages", json_message)
|
116
|
+
@redis.publish('/notifications', client_id)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def empty_queue(client_id)
|
125
|
+
return unless conn = connection(client_id, false)
|
126
|
+
init
|
127
|
+
|
128
|
+
key = "/clients/#{client_id}/messages"
|
129
|
+
@redis.smembers(key) do |json_messages|
|
130
|
+
json_messages.each do |json_message|
|
131
|
+
@redis.srem(key, json_message)
|
132
|
+
conn.deliver(JSON.parse(json_message))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
register 'redis', Redis
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
data/lib/faye/error.rb
CHANGED
@@ -6,16 +6,28 @@ module Faye
|
|
6
6
|
new(code[0], args, code[1]).to_s
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
def self.parse(message)
|
10
|
+
message ||= ''
|
11
|
+
return new(nil, [], message) unless Grammar::ERROR =~ message
|
12
|
+
|
13
|
+
parts = message.split(':')
|
14
|
+
code = parts[0].to_i
|
15
|
+
params = parts[1].split(',')
|
16
|
+
message = parts[2]
|
17
|
+
|
18
|
+
new(code, params, message)
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :code, :params, :message
|
10
22
|
|
11
|
-
def initialize(code,
|
23
|
+
def initialize(code, params, message)
|
12
24
|
@code = code
|
13
|
-
@
|
25
|
+
@params = params
|
14
26
|
@message = message
|
15
27
|
end
|
16
28
|
|
17
29
|
def to_s
|
18
|
-
"#{ @code }:#{ @
|
30
|
+
"#{ @code }:#{ @params * ',' }:#{ @message }"
|
19
31
|
end
|
20
32
|
|
21
33
|
# http://code.google.com/p/cometd/wiki/BayeuxCodes
|
@@ -14,9 +14,15 @@ module Faye
|
|
14
14
|
|
15
15
|
def remove_subscriber(event_type, listener)
|
16
16
|
return unless @subscribers and @subscribers[event_type]
|
17
|
+
return @subscribers.delete(event_type) unless listener
|
18
|
+
|
17
19
|
@subscribers[event_type].delete_if(&listener.method(:==))
|
18
20
|
end
|
19
21
|
|
22
|
+
def remove_subscribers
|
23
|
+
@subscribers = {}
|
24
|
+
end
|
25
|
+
|
20
26
|
def publish_event(event_type, *args)
|
21
27
|
return unless @subscribers and @subscribers[event_type]
|
22
28
|
@subscribers[event_type].each do |listener|
|
@@ -26,6 +26,23 @@ module Faye
|
|
26
26
|
SERVICE = :service
|
27
27
|
|
28
28
|
class << self
|
29
|
+
def expand(name)
|
30
|
+
segments = parse(name)
|
31
|
+
channels = ['/**', name]
|
32
|
+
|
33
|
+
copy = segments.dup
|
34
|
+
copy[copy.size - 1] = '*'
|
35
|
+
channels << unparse(copy)
|
36
|
+
|
37
|
+
1.upto(segments.size - 1) do |i|
|
38
|
+
copy = segments[0...i]
|
39
|
+
copy << '**'
|
40
|
+
channels << unparse(copy)
|
41
|
+
end
|
42
|
+
|
43
|
+
channels
|
44
|
+
end
|
45
|
+
|
29
46
|
def valid?(name)
|
30
47
|
Grammar::CHANNEL_NAME =~ name or
|
31
48
|
Grammar::CHANNEL_PATTERN =~ name
|
@@ -36,6 +53,10 @@ module Faye
|
|
36
53
|
name.split('/')[1..-1].map { |s| s.to_sym }
|
37
54
|
end
|
38
55
|
|
56
|
+
def unparse(segments)
|
57
|
+
'/' + segments.join('/')
|
58
|
+
end
|
59
|
+
|
39
60
|
def meta?(name)
|
40
61
|
segments = parse(name)
|
41
62
|
segments ? (segments.first == META) : nil
|
@@ -52,110 +73,35 @@ module Faye
|
|
52
73
|
end
|
53
74
|
end
|
54
75
|
|
55
|
-
class
|
56
|
-
include Enumerable
|
57
|
-
attr_accessor :value
|
58
|
-
|
76
|
+
class Set
|
59
77
|
def initialize(parent = nil, value = nil)
|
60
|
-
@
|
61
|
-
@value = value
|
62
|
-
@children = {}
|
63
|
-
end
|
64
|
-
|
65
|
-
def each_child
|
66
|
-
@children.each { |key, subtree| yield(key, subtree) }
|
67
|
-
end
|
68
|
-
|
69
|
-
def each(prefix = [], &block)
|
70
|
-
each_child { |path, subtree| subtree.each(prefix + [path], &block) }
|
71
|
-
yield(prefix, @value) unless @value.nil?
|
78
|
+
@channels = {}
|
72
79
|
end
|
73
80
|
|
74
81
|
def keys
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
def [](name)
|
79
|
-
subtree = traverse(name)
|
80
|
-
subtree ? subtree.value : nil
|
81
|
-
end
|
82
|
-
|
83
|
-
def []=(name, value)
|
84
|
-
subtree = traverse(name, true)
|
85
|
-
subtree.value = value unless subtree.nil?
|
82
|
+
@channels.keys
|
86
83
|
end
|
87
84
|
|
88
|
-
def remove(name
|
89
|
-
|
90
|
-
subtree = traverse(name)
|
91
|
-
subtree.remove unless subtree.nil?
|
92
|
-
else
|
93
|
-
return unless @parent
|
94
|
-
@parent.remove_child(self)
|
95
|
-
@parent = @value = nil
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def remove_child(subtree)
|
100
|
-
each_child do |key, child|
|
101
|
-
@children.delete(key) if child == subtree
|
102
|
-
end
|
103
|
-
remove if @children.empty? and @value.nil?
|
104
|
-
end
|
105
|
-
|
106
|
-
def traverse(path, create_if_absent = false)
|
107
|
-
path = Channel.parse(path) if String === path
|
108
|
-
|
109
|
-
return nil if path.nil?
|
110
|
-
return self if path.empty?
|
111
|
-
|
112
|
-
subtree = @children[path.first]
|
113
|
-
return nil if subtree.nil? and not create_if_absent
|
114
|
-
subtree = @children[path.first] = Tree.new(self) if subtree.nil?
|
115
|
-
|
116
|
-
subtree.traverse(path[1..-1], create_if_absent)
|
85
|
+
def remove(name)
|
86
|
+
@channels.delete(name)
|
117
87
|
end
|
118
88
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
return [] if path.nil?
|
123
|
-
return @value.nil? ? [] : [@value] if path.empty?
|
124
|
-
|
125
|
-
if path == [:*]
|
126
|
-
return @children.inject([]) do |list, (key, subtree)|
|
127
|
-
list << subtree.value unless subtree.value.nil?
|
128
|
-
list
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
if path == [:**]
|
133
|
-
list = map { |key, value| value }
|
134
|
-
list.pop unless @value.nil?
|
135
|
-
return list
|
136
|
-
end
|
137
|
-
|
138
|
-
list = @children.values_at(path.first, :*).
|
139
|
-
compact.
|
140
|
-
map { |t| t.glob(path[1..-1]) }
|
141
|
-
|
142
|
-
list << @children[:**].value if @children[:**]
|
143
|
-
list.flatten
|
89
|
+
def has_subscription?(name)
|
90
|
+
@channels.has_key?(name)
|
144
91
|
end
|
145
92
|
|
146
93
|
def subscribe(names, callback)
|
147
94
|
return unless callback
|
148
95
|
names.each do |name|
|
149
|
-
channel =
|
96
|
+
channel = @channels[name] ||= Channel.new(name)
|
150
97
|
channel.add_subscriber(:message, callback)
|
151
98
|
end
|
152
99
|
end
|
153
100
|
|
154
101
|
def unsubscribe(name, callback)
|
155
|
-
channel =
|
102
|
+
channel = @channels[name]
|
156
103
|
return false unless channel
|
157
104
|
channel.remove_subscriber(:message, callback)
|
158
|
-
|
159
105
|
if channel.unused?
|
160
106
|
remove(name)
|
161
107
|
true
|
@@ -165,8 +111,10 @@ module Faye
|
|
165
111
|
end
|
166
112
|
|
167
113
|
def distribute_message(message)
|
168
|
-
|
169
|
-
|
114
|
+
channels = Channel.expand(message['channel'])
|
115
|
+
channels.each do |name|
|
116
|
+
channel = @channels[name]
|
117
|
+
channel.publish_event(:message, message['data']) if channel
|
170
118
|
end
|
171
119
|
end
|
172
120
|
end
|
data/lib/faye/protocol/client.rb
CHANGED
@@ -2,7 +2,6 @@ module Faye
|
|
2
2
|
class Client
|
3
3
|
|
4
4
|
include EventMachine::Deferrable
|
5
|
-
include Timeouts
|
6
5
|
include Logging
|
7
6
|
include Extensible
|
8
7
|
|
@@ -27,19 +26,27 @@ module Faye
|
|
27
26
|
|
28
27
|
@transport = Transport.get(self, MANDATORY_CONNECTION_TYPES)
|
29
28
|
@state = UNCONNECTED
|
30
|
-
@
|
31
|
-
@channels = Channel::Tree.new
|
29
|
+
@channels = Channel::Set.new
|
32
30
|
|
33
31
|
@namespace = Namespace.new
|
34
32
|
@response_callbacks = {}
|
35
33
|
|
36
34
|
@advice = {
|
37
35
|
'reconnect' => RETRY,
|
38
|
-
'interval' => 1000.0 * (@options[:interval] ||
|
39
|
-
'timeout' => 1000.0 * (@options[:timeout]
|
36
|
+
'interval' => 1000.0 * (@options[:interval] || Engine::INTERVAL),
|
37
|
+
'timeout' => 1000.0 * (@options[:timeout] || CONNECTION_TIMEOUT)
|
40
38
|
}
|
41
39
|
end
|
42
40
|
|
41
|
+
def state
|
42
|
+
case @state
|
43
|
+
when UNCONNECTED then :UNCONNECTED
|
44
|
+
when CONNECTING then :CONNECTING
|
45
|
+
when CONNECTED then :CONNECTED
|
46
|
+
when DISCONNECTED then :DISCONNECTED
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
43
50
|
# Request
|
44
51
|
# MUST include: * channel
|
45
52
|
# * version
|
@@ -81,7 +88,7 @@ module Faye
|
|
81
88
|
|
82
89
|
info('Handshake successful: ?', @client_id)
|
83
90
|
|
84
|
-
subscribe(@channels.keys)
|
91
|
+
subscribe(@channels.keys, true)
|
85
92
|
block.call if block_given?
|
86
93
|
|
87
94
|
else
|
@@ -148,7 +155,7 @@ module Faye
|
|
148
155
|
})
|
149
156
|
|
150
157
|
info('Clearing channel listeners for ?', @client_id)
|
151
|
-
@channels = Channel::
|
158
|
+
@channels = Channel::Set.new
|
152
159
|
end
|
153
160
|
|
154
161
|
# Request Response
|
@@ -161,16 +168,21 @@ module Faye
|
|
161
168
|
# * ext
|
162
169
|
# * id
|
163
170
|
# * timestamp
|
164
|
-
def subscribe(channels, &block)
|
171
|
+
def subscribe(channels, force = false, &block)
|
165
172
|
if Array === channels
|
166
173
|
return channels.each do |channel|
|
167
|
-
subscribe(channel, &block)
|
174
|
+
subscribe(channel, force, &block)
|
168
175
|
end
|
169
176
|
end
|
170
177
|
|
171
|
-
validate_channel(channels)
|
172
178
|
subscription = Subscription.new(self, channels, block)
|
173
179
|
|
180
|
+
if not force and @channels.has_subscription?(channels)
|
181
|
+
@channels.subscribe([channels], block)
|
182
|
+
subscription.set_deferred_status(:succeeded)
|
183
|
+
return subscription
|
184
|
+
end
|
185
|
+
|
174
186
|
connect {
|
175
187
|
info('Client ? attempting to subscribe to ?', @client_id, channels)
|
176
188
|
|
@@ -187,6 +199,8 @@ module Faye
|
|
187
199
|
@channels.subscribe(channels, block)
|
188
200
|
|
189
201
|
subscription.set_deferred_status(:succeeded)
|
202
|
+
else
|
203
|
+
subscription.set_deferred_status(:failed, Error.parse(response['error']))
|
190
204
|
end
|
191
205
|
end
|
192
206
|
}
|
@@ -210,8 +224,6 @@ module Faye
|
|
210
224
|
end
|
211
225
|
end
|
212
226
|
|
213
|
-
validate_channel(channels)
|
214
|
-
|
215
227
|
dead = @channels.unsubscribe(channels, block)
|
216
228
|
return unless dead
|
217
229
|
|
@@ -240,7 +252,9 @@ module Faye
|
|
240
252
|
# * id * error
|
241
253
|
# * ext * ext
|
242
254
|
def publish(channel, data)
|
243
|
-
|
255
|
+
unless Grammar::CHANNEL_NAME =~ channel
|
256
|
+
raise "Cannot publish: '#{channel}' is not a valid channel name"
|
257
|
+
end
|
244
258
|
|
245
259
|
connect {
|
246
260
|
info('Client ? queueing published message to ?: ?', @client_id, channel, data)
|
@@ -271,6 +285,15 @@ module Faye
|
|
271
285
|
|
272
286
|
private
|
273
287
|
|
288
|
+
def send(message, &callback)
|
289
|
+
message['id'] = @namespace.generate
|
290
|
+
@response_callbacks[message['id']] = callback if callback
|
291
|
+
|
292
|
+
pipe_through_extensions(:outgoing, message) do |message|
|
293
|
+
@transport.send(message, @advice['timeout'] / 1000.0) if message
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
274
297
|
def handle_advice(advice)
|
275
298
|
@advice.update(advice)
|
276
299
|
|
@@ -298,45 +321,6 @@ module Faye
|
|
298
321
|
EventMachine.add_timer(@advice['interval'] / 1000.0) { connect }
|
299
322
|
end
|
300
323
|
|
301
|
-
def send(message, &callback)
|
302
|
-
message['id'] = @namespace.generate
|
303
|
-
@response_callbacks[message['id']] = callback if callback
|
304
|
-
|
305
|
-
pipe_through_extensions(:outgoing, message) do |message|
|
306
|
-
if message
|
307
|
-
if message['channel'] == Channel::HANDSHAKE
|
308
|
-
@transport.send(message, @advice['timeout'] / 1000.0)
|
309
|
-
else
|
310
|
-
@outbox << message
|
311
|
-
|
312
|
-
if message['channel'] == Channel::CONNECT
|
313
|
-
@connect_message = message
|
314
|
-
end
|
315
|
-
|
316
|
-
add_timeout(:publish, Connection::MAX_DELAY) { flush! }
|
317
|
-
end
|
318
|
-
end
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
def flush!
|
323
|
-
remove_timeout(:publish)
|
324
|
-
|
325
|
-
if @outbox.size > 1 and @connect_message
|
326
|
-
@connect_message['advice'] = {'timeout' => 0}
|
327
|
-
end
|
328
|
-
|
329
|
-
@connect_message = nil
|
330
|
-
|
331
|
-
@transport.send(@outbox, @advice['timeout'] / 1000.0)
|
332
|
-
@outbox = []
|
333
|
-
end
|
334
|
-
|
335
|
-
def validate_channel(channel)
|
336
|
-
raise "'#{ channel }' is not a valid channel name" unless Channel.valid?(channel)
|
337
|
-
raise "Clients may not subscribe to channel '#{ channel }'" unless Channel.subscribable?(channel)
|
338
|
-
end
|
339
|
-
|
340
324
|
end
|
341
325
|
end
|
342
326
|
|