pusher-fake 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pusher-fake.rb +4 -1
- data/lib/pusher-fake/channel.rb +9 -3
- data/lib/pusher-fake/channel/presence.rb +11 -9
- data/lib/pusher-fake/channel/private.rb +8 -5
- data/lib/pusher-fake/channel/public.rb +13 -14
- data/lib/pusher-fake/configuration.rb +2 -1
- data/lib/pusher-fake/connection.rb +41 -29
- data/lib/pusher-fake/cucumber.rb +2 -1
- data/lib/pusher-fake/server.rb +46 -45
- data/lib/pusher-fake/server/application.rb +89 -50
- data/lib/pusher-fake/support/base.rb +4 -9
- data/lib/pusher-fake/webhook.rb +3 -1
- data/spec/features/api/channels_spec.rb +7 -5
- data/spec/features/api/users_spec.rb +1 -1
- data/spec/features/client/event_spec.rb +6 -4
- data/spec/features/client/subscribe_spec.rb +8 -6
- data/spec/features/server/event_spec.rb +7 -7
- data/spec/features/server/webhooks_spec.rb +16 -4
- data/spec/lib/pusher-fake/channel/presence_spec.rb +57 -49
- data/spec/lib/pusher-fake/channel/private_spec.rb +42 -31
- data/spec/lib/pusher-fake/channel/public_spec.rb +37 -27
- data/spec/lib/pusher-fake/channel_spec.rb +51 -91
- data/spec/lib/pusher-fake/configuration_spec.rb +11 -5
- data/spec/lib/pusher-fake/connection_spec.rb +65 -39
- data/spec/lib/pusher-fake/server/application_spec.rb +219 -94
- data/spec/lib/pusher-fake/server_spec.rb +31 -41
- data/spec/lib/pusher-fake/webhook_spec.rb +29 -18
- data/spec/lib/pusher_fake_spec.rb +17 -15
- data/spec/support/application.rb +21 -19
- data/spec/support/application/public/javascripts/vendor/{pusher-3.1.0.js → pusher-3.2.1.js} +69 -48
- data/spec/support/application/views/index.erb +1 -1
- data/spec/support/capybara.rb +1 -3
- data/spec/support/helpers/connect.rb +1 -3
- data/spec/support/matchers/have_configuration_option.rb +2 -2
- data/spec/support/{pusher-fake.rb → pusher_fake.rb} +2 -1
- data/spec/support/webhooks.rb +5 -3
- metadata +38 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2900ffcdf8c54ba90ada932afc74bd755bf5155
|
4
|
+
data.tar.gz: 01f7a685ef41b5c5c155220a87576f095a9e941e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b7245c91e900ff1406d89cc106df53a691558a0f47526c7df0363ada92365f0773d7cd93a2ba4081e0a87fa13fd0b61e9d2f3b8ba946304895a2a5d28377158
|
7
|
+
data.tar.gz: 6c0b75ac27837cf28d8267ef4cdec7553763c2647985a1a3011b1604eada70bb264204f7822c69b36d69b202cbfc987c833fa523960828f503b0a994319e2f72
|
data/lib/pusher-fake.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# rubocop:disable Style/FileName
|
2
|
+
|
1
3
|
require "em-http-request"
|
2
4
|
require "em-websocket"
|
3
5
|
require "multi_json"
|
4
6
|
require "openssl"
|
5
7
|
require "thin"
|
6
8
|
|
9
|
+
# A Pusher fake.
|
7
10
|
module PusherFake
|
8
11
|
# The current version string.
|
9
|
-
VERSION = "1.
|
12
|
+
VERSION = "1.7.0".freeze
|
10
13
|
|
11
14
|
autoload :Channel, "pusher-fake/channel"
|
12
15
|
autoload :Configuration, "pusher-fake/configuration"
|
data/lib/pusher-fake/channel.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module PusherFake
|
2
|
+
# Channel creation and management.
|
2
3
|
module Channel
|
3
4
|
autoload :Public, "pusher-fake/channel/public"
|
4
5
|
autoload :Private, "pusher-fake/channel/private"
|
@@ -6,13 +7,18 @@ module PusherFake
|
|
6
7
|
|
7
8
|
class << self
|
8
9
|
# Name matcher for private channels.
|
9
|
-
PRIVATE_CHANNEL_MATCHER = /\Aprivate
|
10
|
+
PRIVATE_CHANNEL_MATCHER = /\Aprivate-/
|
10
11
|
|
11
12
|
# Name matcher for presence channels.
|
12
|
-
PRESENCE_CHANNEL_MATCHER = /\Apresence
|
13
|
+
PRESENCE_CHANNEL_MATCHER = /\Apresence-/
|
13
14
|
|
14
15
|
# @return [Hash] Cache of existing channels.
|
15
|
-
|
16
|
+
attr_writer :channels
|
17
|
+
|
18
|
+
# @return [Hash] Cache of existing channels.
|
19
|
+
def channels
|
20
|
+
@channels ||= {}
|
21
|
+
end
|
16
22
|
|
17
23
|
# Create a channel, determining the type by the name.
|
18
24
|
#
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module PusherFake
|
2
2
|
module Channel
|
3
|
+
# A presence channel.
|
3
4
|
class Presence < Private
|
4
5
|
# @return [Hash] Channel members hash.
|
5
6
|
attr_reader :members
|
@@ -21,11 +22,12 @@ module PusherFake
|
|
21
22
|
def remove(connection)
|
22
23
|
super
|
23
24
|
|
24
|
-
|
25
|
-
trigger("member_removed", channel: name, user_id: members[connection][:user_id])
|
25
|
+
return unless members.key?(connection)
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
trigger("member_removed",
|
28
|
+
channel: name, user_id: members[connection][:user_id])
|
29
|
+
|
30
|
+
emit("pusher_internal:member_removed", members.delete(connection))
|
29
31
|
end
|
30
32
|
|
31
33
|
# Return a hash containing presence information for the channel.
|
@@ -33,9 +35,7 @@ module PusherFake
|
|
33
35
|
# @return [Hash] Hash containing presence information.
|
34
36
|
def subscription_data
|
35
37
|
hash = Hash[
|
36
|
-
members.map { |_, member|
|
37
|
-
[member[:user_id], member[:user_info]]
|
38
|
-
}
|
38
|
+
members.map { |_, member| [member[:user_id], member[:user_info]] }
|
39
39
|
]
|
40
40
|
|
41
41
|
{ presence: { hash: hash, count: members.size } }
|
@@ -48,10 +48,12 @@ module PusherFake
|
|
48
48
|
#
|
49
49
|
# Also trigger the member_added webhook.
|
50
50
|
#
|
51
|
-
# @param [Connection] connection
|
51
|
+
# @param [Connection] connection Connection a subscription succeeded for.
|
52
52
|
# @param [Hash] options The options for the channel.
|
53
53
|
def subscription_succeeded(connection, options = {})
|
54
|
-
member = members[connection] = MultiJson.load(
|
54
|
+
member = members[connection] = MultiJson.load(
|
55
|
+
options[:channel_data], symbolize_keys: true
|
56
|
+
)
|
55
57
|
|
56
58
|
emit("pusher_internal:member_added", member)
|
57
59
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module PusherFake
|
2
2
|
module Channel
|
3
|
+
# A private channel.
|
3
4
|
class Private < Public
|
4
5
|
# Add the connection to the channel if they are authorized.
|
5
6
|
#
|
6
7
|
# @param [Connection] connection The connection to add.
|
7
8
|
# @param [Hash] options The options for the channel.
|
8
9
|
# @option options [String] :auth The authentication string.
|
9
|
-
# @option options [Hash] :channel_data
|
10
|
+
# @option options [Hash] :channel_data Information for subscribed client.
|
10
11
|
def add(connection, options = {})
|
11
12
|
if authorized?(connection, options)
|
12
13
|
subscription_succeeded(connection, options)
|
@@ -22,7 +23,8 @@ module PusherFake
|
|
22
23
|
# @option options [String] :auth The authentication string.
|
23
24
|
# @return [Boolean] +true+ if authorized, +false+ otherwise.
|
24
25
|
def authorized?(connection, options)
|
25
|
-
authentication_for(connection.id, options[:channel_data]) ==
|
26
|
+
authentication_for(connection.id, options[:channel_data]) ==
|
27
|
+
options[:auth]
|
26
28
|
end
|
27
29
|
|
28
30
|
# Generate an authentication string from the channel based on the
|
@@ -34,9 +36,10 @@ module PusherFake
|
|
34
36
|
# @return [String] The authentication string.
|
35
37
|
def authentication_for(id, data = nil)
|
36
38
|
configuration = PusherFake.configuration
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
|
40
|
+
data = [id, name, data].compact.map(&:to_s).join(":")
|
41
|
+
digest = OpenSSL::Digest::SHA256.new
|
42
|
+
signature = OpenSSL::HMAC.hexdigest(digest, configuration.secret, data)
|
40
43
|
|
41
44
|
"#{configuration.key}:#{signature}"
|
42
45
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module PusherFake
|
2
2
|
module Channel
|
3
|
+
# A public channel.
|
3
4
|
class Public
|
4
5
|
# @return [Array] Connections in this channel.
|
5
6
|
attr_reader :connections
|
@@ -38,7 +39,7 @@ module PusherFake
|
|
38
39
|
# Determine if the +connection+ is in the channel.
|
39
40
|
#
|
40
41
|
# @param [Connection] connection The connection.
|
41
|
-
# @return [Boolean]
|
42
|
+
# @return [Boolean] If the connection is in the channel or not.
|
42
43
|
def includes?(connection)
|
43
44
|
connections.index(connection)
|
44
45
|
end
|
@@ -51,9 +52,7 @@ module PusherFake
|
|
51
52
|
def remove(connection)
|
52
53
|
connections.delete(connection)
|
53
54
|
|
54
|
-
if connections.empty?
|
55
|
-
trigger("channel_vacated", channel: name)
|
56
|
-
end
|
55
|
+
trigger("channel_vacated", channel: name) if connections.empty?
|
57
56
|
end
|
58
57
|
|
59
58
|
# Return subscription data for the channel.
|
@@ -64,6 +63,10 @@ module PusherFake
|
|
64
63
|
{}
|
65
64
|
end
|
66
65
|
|
66
|
+
def trigger(name, data = {})
|
67
|
+
PusherFake::Webhook.trigger(name, data)
|
68
|
+
end
|
69
|
+
|
67
70
|
private
|
68
71
|
|
69
72
|
# Notify the +connection+ of the successful subscription and add the
|
@@ -71,19 +74,15 @@ module PusherFake
|
|
71
74
|
#
|
72
75
|
# If it is the first connection, trigger the channel_occupied webhook.
|
73
76
|
#
|
74
|
-
# @param [Connection] connection
|
77
|
+
# @param [Connection] connection Connection a subscription succeeded for.
|
75
78
|
# @param [Hash] options The options for the channel.
|
76
|
-
def subscription_succeeded(connection,
|
77
|
-
connection.emit("pusher_internal:subscription_succeeded",
|
78
|
-
|
79
|
+
def subscription_succeeded(connection, _options = {})
|
80
|
+
connection.emit("pusher_internal:subscription_succeeded",
|
81
|
+
subscription_data, name)
|
79
82
|
|
80
|
-
|
81
|
-
trigger("channel_occupied", channel: name)
|
82
|
-
end
|
83
|
-
end
|
83
|
+
connections.push(connection)
|
84
84
|
|
85
|
-
|
86
|
-
PusherFake::Webhook.trigger(name, data)
|
85
|
+
trigger("channel_occupied", channel: name) if connections.length == 1
|
87
86
|
end
|
88
87
|
end
|
89
88
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module PusherFake
|
2
|
+
# Configuration class.
|
2
3
|
class Configuration
|
3
4
|
# @return [String] The Pusher Applicaiton ID. (Defaults to +PUSHER_APP_ID+.)
|
4
5
|
attr_accessor :app_id
|
@@ -12,7 +13,7 @@ module PusherFake
|
|
12
13
|
# @return [String] The Pusher API token. (Defaults to +PUSHER_API_SECRET+.)
|
13
14
|
attr_accessor :secret
|
14
15
|
|
15
|
-
# Options for the socket server. See +EventMachine::WebSocket.start
|
16
|
+
# Options for the socket server. See +EventMachine::WebSocket.start+.
|
16
17
|
#
|
17
18
|
# @return [Hash] Options for the socket server.
|
18
19
|
attr_accessor :socket_options
|
@@ -1,14 +1,15 @@
|
|
1
1
|
module PusherFake
|
2
|
+
# A client connection.
|
2
3
|
class Connection
|
3
4
|
# Name matcher for client events.
|
4
|
-
CLIENT_EVENT_MATCHER
|
5
|
+
CLIENT_EVENT_MATCHER = /\Aclient-(.+)\z/
|
5
6
|
|
6
|
-
# @return [EventMachine::WebSocket::Connection]
|
7
|
+
# @return [EventMachine::WebSocket::Connection] Socket for the connection.
|
7
8
|
attr_reader :socket
|
8
9
|
|
9
10
|
# Create a new {Connection} object.
|
10
11
|
#
|
11
|
-
# @param [EventMachine::WebSocket::Connection] socket
|
12
|
+
# @param [EventMachine::WebSocket::Connection] socket Connection object.
|
12
13
|
def initialize(socket)
|
13
14
|
@socket = socket
|
14
15
|
end
|
@@ -39,7 +40,8 @@ module PusherFake
|
|
39
40
|
|
40
41
|
# Notify the Pusher client that a connection has been established.
|
41
42
|
def establish
|
42
|
-
emit("pusher:connection_established",
|
43
|
+
emit("pusher:connection_established",
|
44
|
+
socket_id: id, activity_timeout: 120)
|
43
45
|
end
|
44
46
|
|
45
47
|
# Process an event.
|
@@ -47,41 +49,51 @@ module PusherFake
|
|
47
49
|
# @param [String] data The event data as JSON.
|
48
50
|
def process(data)
|
49
51
|
message = MultiJson.load(data, symbolize_keys: true)
|
52
|
+
event = message[:event]
|
50
53
|
|
51
54
|
PusherFake.log("RECV #{id}: #{message}")
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
case event
|
59
|
-
when "pusher:subscribe"
|
60
|
-
channel.add(self, data)
|
61
|
-
when "pusher:unsubscribe"
|
62
|
-
channel.remove(self)
|
63
|
-
when "pusher:ping"
|
64
|
-
emit("pusher:pong")
|
65
|
-
when CLIENT_EVENT_MATCHER
|
66
|
-
if channel.is_a?(Channel::Private) && channel.includes?(self)
|
67
|
-
channel.emit(event, data, socket_id: id)
|
68
|
-
|
69
|
-
trigger(channel, id, event, data)
|
70
|
-
end
|
56
|
+
if event =~ CLIENT_EVENT_MATCHER
|
57
|
+
process_trigger(event, message)
|
58
|
+
else
|
59
|
+
process_event(event, message)
|
71
60
|
end
|
72
61
|
end
|
73
62
|
|
74
63
|
private
|
75
64
|
|
65
|
+
def channel_for(message)
|
66
|
+
Channel.factory(message[:channel] || message[:data][:channel])
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_event(event, message)
|
70
|
+
if event == "pusher:subscribe"
|
71
|
+
channel_for(message).add(self, message[:data])
|
72
|
+
elsif event == "pusher:unsubscribe"
|
73
|
+
channel_for(message).remove(self)
|
74
|
+
elsif event == "pusher:ping"
|
75
|
+
emit("pusher:pong")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def process_trigger(event, message)
|
80
|
+
channel = channel_for(message)
|
81
|
+
|
82
|
+
return unless channel.is_a?(Channel::Private) && channel.includes?(self)
|
83
|
+
|
84
|
+
channel.emit(event, message[:data], socket_id: id)
|
85
|
+
|
86
|
+
trigger(channel, id, event, message[:data])
|
87
|
+
end
|
88
|
+
|
76
89
|
def trigger(channel, id, event, data)
|
77
90
|
Thread.new do
|
78
|
-
hook = {
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
hook[:user_id] = channel.members[self][:user_id] if channel.is_a?(Channel::Presence)
|
91
|
+
hook = { event: event, channel: channel.name, socket_id: id }
|
92
|
+
hook[:data] = MultiJson.dump(data) if data
|
93
|
+
|
94
|
+
if channel.is_a?(Channel::Presence)
|
95
|
+
hook[:user_id] = channel.members[self][:user_id]
|
96
|
+
end
|
85
97
|
|
86
98
|
channel.trigger("client_event", hook)
|
87
99
|
end
|
data/lib/pusher-fake/cucumber.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
1
|
require "pusher-fake/support/cucumber"
|
2
2
|
|
3
|
-
warn %
|
3
|
+
warn %([DEPRECATION] "pusher-fake/cucumber" is deprecated.) +
|
4
|
+
%(Please use "pusher-fake/support/cucumber" instead.)
|
data/lib/pusher-fake/server.rb
CHANGED
@@ -1,65 +1,66 @@
|
|
1
1
|
module PusherFake
|
2
|
+
# Socket and web server manager.
|
2
3
|
module Server
|
3
4
|
autoload :Application, "pusher-fake/server/application"
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
class << self
|
7
|
+
# Start the servers.
|
8
|
+
#
|
9
|
+
# @see start_socket_server
|
10
|
+
# @see start_web_server
|
11
|
+
def start
|
12
|
+
EventMachine.run do
|
13
|
+
start_web_server
|
14
|
+
start_socket_server
|
15
|
+
end
|
13
16
|
end
|
14
|
-
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
# Start the WebSocket server.
|
19
|
+
def start_socket_server
|
20
|
+
EventMachine::WebSocket.start(configuration.socket_options) do |socket|
|
21
|
+
socket.onopen do
|
22
|
+
connection = Connection.new(socket)
|
23
|
+
connection.establish
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
socket.onclose do
|
27
|
-
Channel.remove(connection)
|
25
|
+
socket.onmessage { |data| connection.process(data) }
|
26
|
+
socket.onclose { Channel.remove(connection) }
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
31
|
-
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
# Start the web server.
|
32
|
+
def start_web_server
|
33
|
+
options = configuration.web_options.dup
|
34
|
+
host = options.delete(:host)
|
35
|
+
port = options.delete(:port)
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
Thin::Logging.silent = true
|
38
|
+
Thin::Server.new(host, port, Application).tap do |server|
|
39
|
+
options.each do |key, value|
|
40
|
+
server.__send__("#{key}=", value)
|
41
|
+
end
|
42
42
|
|
43
|
-
|
43
|
+
server.start!
|
44
|
+
end
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
|
47
|
+
private
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
# Convenience method for access the configuration object.
|
50
|
+
#
|
51
|
+
# @return [Configuration] The configuration object.
|
52
|
+
def configuration
|
53
|
+
PusherFake.configuration
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
56
|
+
# Return a hash of options for the socket server based on
|
57
|
+
# the configuration.
|
58
|
+
#
|
59
|
+
# @return [Hash] The socket server configuration options.
|
60
|
+
def socket_server_options
|
61
|
+
{ host: configuration.socket_host,
|
62
|
+
port: configuration.socket_port }
|
63
|
+
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
end
|
@@ -1,54 +1,58 @@
|
|
1
1
|
module PusherFake
|
2
2
|
module Server
|
3
|
+
# The fake web application.
|
3
4
|
class Application
|
4
|
-
CHANNEL_FILTER_ERROR = "user_count may only be requested for presence
|
5
|
-
|
5
|
+
CHANNEL_FILTER_ERROR = "user_count may only be requested for presence " \
|
6
|
+
"channels - please supply filter_by_prefix " \
|
7
|
+
"begining with presence-".freeze
|
6
8
|
|
7
|
-
CHANNEL_USER_COUNT_ERROR = "Cannot retrieve the user count unless the
|
9
|
+
CHANNEL_USER_COUNT_ERROR = "Cannot retrieve the user count unless the " \
|
10
|
+
"channel is a presence channel".freeze
|
8
11
|
|
9
|
-
PRESENCE_PREFIX_MATCHER = /\Apresence
|
12
|
+
PRESENCE_PREFIX_MATCHER = /\Apresence-/
|
13
|
+
|
14
|
+
REQUEST_PATHS = {
|
15
|
+
%r{\A/apps/:id/batch_events\z} => :batch_events,
|
16
|
+
%r{\A/apps/:id/events\z} => :events,
|
17
|
+
%r{\A/apps/:id/channels\z} => :channels,
|
18
|
+
%r{\A/apps/:id/channels/([^/]+)\z} => :channel,
|
19
|
+
%r{\A/apps/:id/channels/([^/]+)/users\z} => :users
|
20
|
+
}.freeze
|
10
21
|
|
11
22
|
# Process an API request.
|
12
23
|
#
|
13
24
|
# @param [Hash] environment The request environment.
|
14
25
|
# @return [Rack::Response] A successful response.
|
15
26
|
def self.call(environment)
|
16
|
-
id = PusherFake.configuration.app_id
|
17
27
|
request = Rack::Request.new(environment)
|
18
|
-
response =
|
19
|
-
when %r{\A/apps/#{id}/events\Z}
|
20
|
-
events(request)
|
21
|
-
when %r{\A/apps/#{id}/channels\Z}
|
22
|
-
channels(request)
|
23
|
-
when %r{\A/apps/#{id}/channels/([^/]+)\Z}
|
24
|
-
channel($1, request)
|
25
|
-
when %r{\A/apps/#{id}/channels/([^/]+)/users\Z}
|
26
|
-
users($1)
|
27
|
-
else
|
28
|
-
raise "Unknown path: #{request.path}"
|
29
|
-
end
|
28
|
+
response = response_for(request)
|
30
29
|
|
31
30
|
Rack::Response.new(MultiJson.dump(response)).finish
|
32
31
|
rescue => error
|
33
32
|
Rack::Response.new(error.message, 400).finish
|
34
33
|
end
|
35
34
|
|
35
|
+
# Emit batch events with data to the requested channel(s).
|
36
|
+
#
|
37
|
+
# @param [Rack::Request] request The HTTP request.
|
38
|
+
# @return [Hash] An empty hash.
|
39
|
+
def self.batch_events(request)
|
40
|
+
batch = MultiJson.load(request.body.read)["batch"]
|
41
|
+
batch.each do |event|
|
42
|
+
send_event(event)
|
43
|
+
end
|
44
|
+
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
|
36
48
|
# Emit an event with data to the requested channel(s).
|
37
49
|
#
|
38
50
|
# @param [Rack::Request] request The HTTP request.
|
39
51
|
# @return [Hash] An empty hash.
|
40
52
|
def self.events(request)
|
41
53
|
event = MultiJson.load(request.body.read)
|
42
|
-
data = begin
|
43
|
-
MultiJson.load(event["data"])
|
44
|
-
rescue MultiJson::LoadError
|
45
|
-
event["data"]
|
46
|
-
end
|
47
54
|
|
48
|
-
event
|
49
|
-
channel = Channel.factory(channel_name)
|
50
|
-
channel.emit(event["name"], data, socket_id: event["socket_id"])
|
51
|
-
end
|
55
|
+
send_event(event)
|
52
56
|
|
53
57
|
{}
|
54
58
|
end
|
@@ -62,53 +66,71 @@ module PusherFake
|
|
62
66
|
# @param [Rack::Request] request The HTTP request.
|
63
67
|
# @return [Hash] A hash of channel information.
|
64
68
|
def self.channel(name, request)
|
65
|
-
|
69
|
+
count = request.params["info"].to_s.split(",").include?("user_count")
|
66
70
|
|
67
|
-
if
|
71
|
+
if count && name !~ PRESENCE_PREFIX_MATCHER
|
68
72
|
raise CHANNEL_USER_COUNT_ERROR
|
69
73
|
end
|
70
74
|
|
71
|
-
|
72
|
-
|
75
|
+
channel = PusherFake::Channel.channels[name]
|
76
|
+
connections = channel ? channel.connections : []
|
73
77
|
|
74
|
-
{
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
+
result = { occupied: connections.any? }
|
79
|
+
result[:user_count] = connections.size if count
|
80
|
+
result
|
78
81
|
end
|
79
82
|
|
80
|
-
# Returns a hash of occupied channels, optionally filtering with a
|
81
|
-
#
|
82
|
-
#
|
83
|
+
# Returns a hash of occupied channels, optionally filtering with a
|
84
|
+
# prefix. When filtering to presence chanenls, the user count maybe also
|
85
|
+
# be requested.
|
83
86
|
#
|
84
87
|
# @param [Rack::Request] request The HTTP request.
|
85
88
|
# @return [Hash] A hash of occupied channels.
|
89
|
+
#
|
90
|
+
# rubocop:disable Metrics/AbcSize
|
86
91
|
def self.channels(request)
|
87
|
-
|
92
|
+
count = request.params["info"].to_s.split(",").include?("user_count")
|
88
93
|
prefix = request.params["filter_by_prefix"].to_s
|
89
94
|
|
90
|
-
if
|
91
|
-
raise CHANNEL_FILTER_ERROR
|
92
|
-
end
|
95
|
+
raise CHANNEL_FILTER_ERROR if count && prefix !~ PRESENCE_PREFIX_MATCHER
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
channels[name
|
100
|
-
channels[name][:user_count] = channel.connections.
|
97
|
+
PusherFake::Channel
|
98
|
+
.channels
|
99
|
+
.each_with_object(channels: {}) do |(name, channel), result|
|
100
|
+
next unless name.start_with?(prefix)
|
101
|
+
|
102
|
+
channels = result[:channels].merge!(name => {})
|
103
|
+
channels[name][:user_count] = channel.connections.size if count
|
101
104
|
end
|
105
|
+
end
|
106
|
+
# rubocop:enable Metrics/AbcSize
|
107
|
+
|
108
|
+
# Attempt to provide a response for the provided request.
|
109
|
+
#
|
110
|
+
# @param [Rack::Request] request The HTTP request.
|
111
|
+
# @return [Hash] A response hash.
|
112
|
+
def self.response_for(request)
|
113
|
+
id = PusherFake.configuration.app_id
|
102
114
|
|
103
|
-
|
115
|
+
REQUEST_PATHS.each do |path, method|
|
116
|
+
matcher = Regexp.new(path.to_s.sub(":id", id))
|
117
|
+
matches = matcher.match(request.path)
|
118
|
+
|
119
|
+
next if matches.nil?
|
120
|
+
|
121
|
+
arguments = [matches[1], request].compact
|
122
|
+
|
123
|
+
return public_send(method, *arguments)
|
104
124
|
end
|
125
|
+
|
126
|
+
raise "Unknown path: #{request.path}"
|
105
127
|
end
|
106
128
|
|
107
129
|
# Returns a hash of the IDs for the users in the channel.
|
108
130
|
#
|
109
131
|
# @param [String] name The channel name.
|
110
132
|
# @return [Hash] A hash of user IDs.
|
111
|
-
def self.users(name)
|
133
|
+
def self.users(name, _request = nil)
|
112
134
|
channels = PusherFake::Channel.channels || {}
|
113
135
|
channel = channels[name]
|
114
136
|
|
@@ -120,6 +142,23 @@ module PusherFake
|
|
120
142
|
|
121
143
|
{ users: users || [] }
|
122
144
|
end
|
145
|
+
|
146
|
+
private_class_method
|
147
|
+
|
148
|
+
# Emit an event with data to the requested channel(s).
|
149
|
+
#
|
150
|
+
# @param [Hash] event The raw event JSON.
|
151
|
+
#
|
152
|
+
# rubocop:disable Style/RescueModifier
|
153
|
+
def self.send_event(event)
|
154
|
+
data = MultiJson.load(event["data"]) rescue event["data"]
|
155
|
+
channels = Array(event["channels"] || event["channel"])
|
156
|
+
channels.each do |channel_name|
|
157
|
+
channel = Channel.factory(channel_name)
|
158
|
+
channel.emit(event["name"], data, socket_id: event["socket_id"])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
# rubocop:enable Style/RescueModifier
|
123
162
|
end
|
124
163
|
end
|
125
164
|
end
|