webmate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.gitignore +19 -0
  2. data/GUIDE.md +115 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +47 -0
  6. data/Rakefile +1 -0
  7. data/bin/webmate +70 -0
  8. data/lib/webmate/application.rb +138 -0
  9. data/lib/webmate/config.rb +23 -0
  10. data/lib/webmate/decorators/base.rb +38 -0
  11. data/lib/webmate/env.rb +17 -0
  12. data/lib/webmate/logger.rb +20 -0
  13. data/lib/webmate/observers/base.rb +24 -0
  14. data/lib/webmate/presenters/base.rb +69 -0
  15. data/lib/webmate/presenters/base_presenter.rb +115 -0
  16. data/lib/webmate/presenters/scoped.rb +30 -0
  17. data/lib/webmate/responders/abstract.rb +127 -0
  18. data/lib/webmate/responders/base.rb +21 -0
  19. data/lib/webmate/responders/callbacks.rb +79 -0
  20. data/lib/webmate/responders/exceptions.rb +4 -0
  21. data/lib/webmate/responders/response.rb +36 -0
  22. data/lib/webmate/responders/templates.rb +65 -0
  23. data/lib/webmate/route_helpers/route.rb +91 -0
  24. data/lib/webmate/route_helpers/routes_collection.rb +273 -0
  25. data/lib/webmate/socket.io/actions/connection.rb +14 -0
  26. data/lib/webmate/socket.io/actions/handshake.rb +34 -0
  27. data/lib/webmate/socket.io/packets/ack.rb +5 -0
  28. data/lib/webmate/socket.io/packets/base.rb +156 -0
  29. data/lib/webmate/socket.io/packets/connect.rb +5 -0
  30. data/lib/webmate/socket.io/packets/disconnect.rb +5 -0
  31. data/lib/webmate/socket.io/packets/error.rb +5 -0
  32. data/lib/webmate/socket.io/packets/event.rb +5 -0
  33. data/lib/webmate/socket.io/packets/heartbeat.rb +5 -0
  34. data/lib/webmate/socket.io/packets/json.rb +5 -0
  35. data/lib/webmate/socket.io/packets/message.rb +5 -0
  36. data/lib/webmate/socket.io/packets/noop.rb +5 -0
  37. data/lib/webmate/support/em_mongoid.rb +53 -0
  38. data/lib/webmate/version.rb +3 -0
  39. data/lib/webmate/views/scope.rb +25 -0
  40. data/lib/webmate/websockets.rb +50 -0
  41. data/lib/webmate.rb +129 -0
  42. data/spec/lib/route_helpers/route_spec.rb +41 -0
  43. data/spec/spec_helper.rb +18 -0
  44. data/vendor/.DS_Store +0 -0
  45. data/vendor/assets/.DS_Store +0 -0
  46. data/vendor/assets/javascripts/.DS_Store +0 -0
  47. data/vendor/assets/javascripts/webmate/.DS_Store +0 -0
  48. data/vendor/assets/javascripts/webmate/auth.coffee +63 -0
  49. data/vendor/assets/javascripts/webmate/backbone_ext/.DS_Store +0 -0
  50. data/vendor/assets/javascripts/webmate/backbone_ext/resources.coffee +60 -0
  51. data/vendor/assets/javascripts/webmate/backbone_ext/sync.coffee +131 -0
  52. data/vendor/assets/javascripts/webmate/client.coffee +133 -0
  53. data/vendor/assets/javascripts/webmate/init.coffee +7 -0
  54. data/vendor/assets/javascripts/webmate/libs/.DS_Store +0 -0
  55. data/vendor/assets/javascripts/webmate/libs/backbone.js +1572 -0
  56. data/vendor/assets/javascripts/webmate/libs/benchmark.coffee +27 -0
  57. data/vendor/assets/javascripts/webmate/libs/icanhaz.js +542 -0
  58. data/vendor/assets/javascripts/webmate/libs/socket.io.js +3871 -0
  59. data/vendor/assets/javascripts/webmate/libs/underscore.js +1 -0
  60. data/vendor/assets/javascripts/webmate.js +10 -0
  61. data/webmate.gemspec +31 -0
  62. metadata +290 -0
@@ -0,0 +1,273 @@
1
+ module Webmate
2
+ class RoutesCollection
3
+ TRANSPORTS = [:ws, :http]
4
+
5
+ attr_reader :routes
6
+
7
+ def initialize
8
+ @routes = {}
9
+ @resource_scope = []
10
+
11
+ enable_websockets_support
12
+ end
13
+
14
+ def define_routes(&block)
15
+ instance_eval(&block)
16
+ end
17
+
18
+ # get info about matched route
19
+ # method - GET/POST/PUT/PATCH/DELETE
20
+ # transport - HTTP / WS [ HTTPS / WSS ]
21
+ # path - /projects/123/tasks
22
+ #
23
+ def match(method, transport, path)
24
+ routes = get_routes(method, transport)
25
+ routes.each do |route|
26
+ if info = route.match(path)
27
+ return info
28
+ end
29
+ end
30
+ nil
31
+ end
32
+
33
+ def get_routes(method, transport)
34
+ @routes[method] ||= {}
35
+ @routes[method][transport] || []
36
+ end
37
+
38
+ private
39
+
40
+ # if websockets enabled, we should add specific http routes
41
+ # - for handshake [ get session id ]
42
+ # - for connection opening [ switch protocol from http to ws ]
43
+ def enable_websockets_support
44
+ namespace = configatron.websockets.namespace
45
+ namespace = 'api' if namespace.blank? # || not working with configatron
46
+
47
+ route_options = { method: 'GET', transport: ['HTTP'], action: 'websocket' }
48
+
49
+ # handshake
50
+ add_route(Webmate::Route.new(route_options.merge(
51
+ path: "/#{namespace}/:version_id",
52
+ responder: Webmate::SocketIO::Actions::Handshake,
53
+ )))
54
+
55
+ # transport connection
56
+ add_route(Webmate::Route.new(route_options.merge(
57
+ transport: ["WS"],
58
+ path: "/#{namespace}/:version_id/websocket/:session_id",
59
+ responder: Webmate::SocketIO::Actions::Connection,
60
+ action: 'open'
61
+ )))
62
+ end
63
+
64
+ # we store routes in following structure
65
+ # { method:
66
+ # transport: [ routes ]
67
+ # route - valid object of Webmate::Route class
68
+ def add_route(route)
69
+ # add route to specific node of routes hash
70
+ @routes[route.method.to_s.upcase] ||= {}
71
+ route.transport.each do |transport|
72
+ (@routes[route.method.to_s.upcase][transport.to_s.upcase] ||= []).push(route)
73
+ end
74
+ end
75
+
76
+ # define methods for separate routes
77
+ # get '/path', to: , transport: ,
78
+ # or
79
+ # resources :projects
80
+ # member do
81
+ # get 'read_formatted'
82
+ %w[get post put delete patch].each do |method_name|
83
+ define_method method_name.to_sym do |path, options = {}|
84
+ route_options = process_options(options)
85
+
86
+ # process case inside resources/collection or resources/member block
87
+ if is_member_scope? or is_collection_scope?
88
+ route_options[:responder] ||= get_responder_from_scope
89
+ route_options[:action] ||= path
90
+ route_options[:path] ||= "#{path_prefix}/#{path}"
91
+ else
92
+ route_options[:path] = path || '/'
93
+ end
94
+ route_options[:method] = method_name.to_sym
95
+
96
+ add_route(Webmate::Route.new(route_options))
97
+ end
98
+ end
99
+
100
+ # available options are
101
+ # transport: [:http, :ws] or any single transport
102
+ # responder can be specified
103
+ # action: some_action
104
+ # responder: SomeResponder or 'some/responder/api/v1'
105
+ # or with to: param
106
+ # to: 'responder_name#action_name'
107
+ def process_options(raw_options)
108
+ options = {}
109
+ options[:transport] = normalized_transport_option(raw_options[:transport])
110
+
111
+ if responder_with_action = raw_options[:to]
112
+ # extract action & responder from :to
113
+ responder_name, action = responder_with_action.split('#')
114
+ options[:responder] = "#{responder_name}_responder".classify.constantize
115
+ options[:action] = action
116
+ else
117
+ # use action & responder options
118
+ options[:responder] = raw_options[:responder]
119
+ options[:action] = raw_options[:action]
120
+ end
121
+
122
+ options
123
+ end
124
+
125
+ # resource :name, options, &block
126
+ # can register following methods
127
+ # get 'name' => read_all
128
+ # get 'name/:id' => read
129
+ # post 'name' => create
130
+ # put 'name/:id' => update
131
+ # delete 'name/:id' => destroy
132
+ #
133
+ # examples
134
+ # resources :projects, transport: :http, only: [:read, :read_all, :update, :delete, :create]
135
+ #
136
+ def resources(*resources, &block)
137
+ options = resources.last.is_a?(Hash) ? resources.pop : {}
138
+ actions = normalized_action_option(options.delete(:only))
139
+
140
+ resources.each do |resource_name|
141
+ responder = (options[:responder] || "#{resource_name}_responder").classify
142
+ route_args = { responder: responder, transport: options[:transport] }
143
+
144
+ [:read, :read_all, :update, :delete, :create].each do |action_name|
145
+ if actions.include?(action_name)
146
+ self.send "define_resource_#{action_name}_method", resource_name.to_s, route_args
147
+ end
148
+ end
149
+
150
+ nested_resources_eval(resource_name, &block) if block_given?
151
+ end
152
+ end
153
+
154
+ # should process blocks inside other resource
155
+ # and correctly set prefix of resources-parents
156
+ # resourcesprojects do
157
+ # resources :tasks
158
+ # end
159
+ def nested_resources_eval(resource_name, &block)
160
+ @resource_scope.push({
161
+ resource: resource_name,
162
+ resource_id: "#{resource_name.to_s.singularize}_id".to_sym
163
+ })
164
+ yield block
165
+ ensure
166
+ @resource_scope.pop
167
+ end
168
+
169
+ def path_prefix
170
+ prefix = ''
171
+ @resource_scope.each do |scope|
172
+ prefix << "/#{scope[:resource]}"
173
+ prefix << "/:#{scope[:resource_id]}" unless scope[:collection]
174
+ end
175
+ prefix
176
+ end
177
+
178
+ def get_responder_from_scope
179
+ responder_name = @resource_scope.last[:resource]
180
+ "#{responder_name}_responder".classify.constantize
181
+ end
182
+
183
+ # methods below designed to set actions on member/collection
184
+ # inside resource definition block
185
+ #
186
+ # "projects/do_on_collection"
187
+ # "projects/:project_id/do_on_member"
188
+ #
189
+ # can be designed as
190
+ # example
191
+ # resources :projects do
192
+ # collection do
193
+ # get 'do_on_collection'
194
+ # end
195
+ #
196
+ # member do
197
+ # get "do_on_member"
198
+ # end
199
+ # prefix /resource_name/resource_id
200
+ def member(&block)
201
+ return if @resource_scope.blank?
202
+ @resource_scope.last[:member] = true
203
+ yield block
204
+ ensure
205
+ @resource_scope.last[:member] = false
206
+ end
207
+
208
+ # prefix /resource_name
209
+ def collection(&block)
210
+ return if @resource_scope.blank?
211
+ @resource_scope.last[:collection] = true
212
+ yield block
213
+ ensure
214
+ @resource_scope.last[:collection] = false
215
+ end
216
+
217
+ # track definition inside collection block
218
+ # collection do
219
+ # path 'code', options
220
+ def is_collection_scope?
221
+ @resource_scope.present? && @resource_scope.last[:collection]
222
+ end
223
+
224
+ # track definition inside member block
225
+ # collection do
226
+ # path 'code', options
227
+ def is_member_scope?
228
+ @resource_scope.present? && @resource_scope.last[:member]
229
+ end
230
+
231
+
232
+ # helper methods
233
+ # normalize_transport_option
234
+ # returns array of requested transports, but available ones only
235
+ def normalized_transport_option(transport = nil)
236
+ return TRANSPORTS.dup if transport.blank?
237
+ transport = [transport] unless transport.is_a?(Array)
238
+
239
+ transport.map{|t| t.to_s.downcase.to_sym} & TRANSPORTS
240
+ end
241
+
242
+ # methods list
243
+ # combination from available
244
+ # [:read, :read_all, :update, :delete, :create]
245
+ def normalized_action_option(methods = nil)
246
+ default_methods = [:read, :read_all, :update, :delete, :create]
247
+ return default_methods if methods.blank?
248
+ methods = [methods] unless methods.is_a?(Array)
249
+
250
+ methods.map{|m| m.to_s.downcase.to_sym} & default_methods
251
+ end
252
+
253
+ def define_resource_read_all_method(resource_name, route_args)
254
+ get "#{path_prefix}/#{resource_name}", route_args.merge(action: :read_all)
255
+ end
256
+
257
+ def define_resource_read_method(resource_name, route_args)
258
+ get "#{path_prefix}/#{resource_name}/:#{resource_name.singularize}_id", route_args.merge(action: :read)
259
+ end
260
+
261
+ def define_resource_create_method(resource_name, route_args)
262
+ post "#{path_prefix}/#{resource_name}", route_args.merge(action: :create)
263
+ end
264
+
265
+ def define_resource_update_method(resource_name, route_args)
266
+ put "#{path_prefix}/#{resource_name}/:#{resource_name.singularize}_id", route_args.merge(action: :update)
267
+ end
268
+
269
+ def define_resource_delete_method(resource_name, route_args)
270
+ delete "#{path_prefix}/#{resource_name}/:#{resource_name.singularize}_id", route_args.merge(action: :delete)
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,14 @@
1
+ module Webmate
2
+ module SocketIO
3
+ module Actions
4
+ class Connection
5
+ def initialize(*args)
6
+ end
7
+
8
+ def respond(base)
9
+ puts "this case should be process directly in Webmate::Application"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ module Webmate
2
+ module SocketIO
3
+ module Actions
4
+ class Handshake
5
+ def initialize(options = {})
6
+ @session_id = generate_session_id
7
+
8
+ default_options = {
9
+ transports: %w{websocket},
10
+ heartbeat_timeout: 300,
11
+ closing_timeout: 300
12
+ }
13
+ @settings = OpenStruct.new(default_options.merge(options))
14
+ end
15
+
16
+ def respond
17
+ body = [
18
+ @session_id,
19
+ @settings.heartbeat_timeout,
20
+ @settings.closing_timeout,
21
+ @settings.transports.join(',')
22
+ ]
23
+ Webmate::Responders::Response.new(body.join(':'))
24
+ end
25
+
26
+ private
27
+
28
+ def generate_session_id
29
+ SecureRandom.hex
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Ack < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'ack'
4
+ end
5
+ end
@@ -0,0 +1,156 @@
1
+ module Webmate
2
+ module SocketIO
3
+ module Packets
4
+ class Base
5
+ cattr_accessor :current_id
6
+
7
+ attr_writer :packet_id, :packet_endpoint
8
+
9
+ # the same order, as in socket io packet.packets
10
+ PACKETS_TYPES = %W{
11
+ disconnect
12
+ connect
13
+ heartbeat
14
+ message
15
+ json
16
+ event
17
+ ack
18
+ error
19
+ noop
20
+ }
21
+
22
+ def initialize(packet_data = {})
23
+ @packet_data = packet_data.with_indifferent_access
24
+ end
25
+
26
+ # packet should be created by socket.io spec
27
+ #[message type] ':' [message id ('+')] ':' [message endpoint] (':' [message data])
28
+ # and webmate spec
29
+ # message_data = {
30
+ # method: GET/POST/...
31
+ # path: '/projects'
32
+ # params: {}
33
+ # metadata: { data should be returned back with answer }
34
+ # }
35
+ def self.parse(packet)
36
+ # last element is encoded json array, so there can be many ':'
37
+ packet_type_id, packet_id, packet_endpoint, json_data = packet.split(':', 4)
38
+
39
+ packet_data = (Yajl::Parser.parse(json_data) || {}).with_indifferent_access
40
+
41
+ if packet_data[:params].is_a?(String)
42
+ packet_data[:params] = Yajl::Parser.parse(packet_data[:params])
43
+ end
44
+
45
+ if packet_data[:metadata].is_a?(String)
46
+ packet_data[:metadata] = Yajl::Parser.parse(packet_data[:metadata])
47
+ end
48
+
49
+ packet = OpenStruct.new(
50
+ path: packet_data[:path],
51
+ method: packet_data[:method],
52
+ params: packet_data[:params] || {},
53
+ metadata: packet_data[:metadata] || {},
54
+ packet_id: packet_id,
55
+ packet_endpoint: packet_endpoint
56
+ )
57
+
58
+ packet
59
+ end
60
+
61
+ # convert response from Responders::Base to socket io message
62
+ #
63
+ def self.build_response_packet(response)
64
+ new(self.prepare_packet_data(response))
65
+ end
66
+
67
+ def self.prepare_packet_data(response)
68
+ packet_data = {
69
+ action: response.action,
70
+ body: response.data,
71
+ path: response.path,
72
+ params: response.params,
73
+ metadata: response.metadata
74
+ }
75
+ end
76
+
77
+ # socket io spec
78
+ #[message type] ':' [message id ('+')] ':' [message endpoint] (':' [message data])
79
+ def to_packet
80
+ data = {
81
+ action: action,
82
+ request: {
83
+ path: path,
84
+ metadata: metadata
85
+ },
86
+ response: {
87
+ body: body,
88
+ status: status || 200
89
+ }
90
+ }
91
+ encoded_data = Yajl::Encoder.new.encode(data)
92
+ [
93
+ packet_type_id,
94
+ packet_id,
95
+ packet_endpoint,
96
+ encoded_data
97
+ ].join(':')
98
+ end
99
+
100
+ def packet_type_id
101
+ PACKETS_TYPES.index(self.packet_type)
102
+ end
103
+
104
+ def packet_endpoint
105
+ @packet_endpoint ||= ''
106
+ end
107
+
108
+ def packet_data
109
+ (@packet_data || {})
110
+ end
111
+
112
+ def metadata
113
+ packet_data[:metadata]
114
+ end
115
+
116
+ def path
117
+ packet_data[:path]
118
+ end
119
+
120
+ def action
121
+ packet_data[:action]
122
+ end
123
+
124
+ def params
125
+ packet_data[:params]
126
+ end
127
+
128
+ def body
129
+ packet_data[:body]
130
+ end
131
+
132
+ def status
133
+ packet_data[:status]
134
+ end
135
+
136
+ def packet_id
137
+ @id ||= generate_packet_id
138
+ end
139
+
140
+ # update counter
141
+ def packet_id=(new_packet_id)
142
+ self.class.current_id = new_packet_id
143
+ @id ||= generate_packet_id
144
+ end
145
+
146
+ # unique packet id
147
+ # didn't find any influence for now,
148
+ # uniqueness not matter
149
+ def generate_packet_id
150
+ self.class.current_id ||= 0
151
+ self.class.current_id += 1
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Connect < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'connect'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Disconnect < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'disconnect'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Error < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'error'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Event < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'event'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Heartbeat < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'heartbeat'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Json < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'json'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Message < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'message'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Webmate::SocketIO::Packets::Noop < Webmate::SocketIO::Packets::Base
2
+ def packet_type
3
+ 'noop'
4
+ end
5
+ end
@@ -0,0 +1,53 @@
1
+ # TODO: this is needed to use latest mongoid with moped, but it doesn't work at this moment
2
+
3
+ begin
4
+ require "moped"
5
+ rescue LoadError => error
6
+ raise "Missing EM-Synchrony dependency: gem install moped"
7
+ end
8
+
9
+ module Moped
10
+ class TimeoutHandler
11
+ def self.timeout(op_timeout, &block)
12
+ f = Fiber.current
13
+ timer = EM::Timer.new(op_timeout) { f.resume(nil) }
14
+ res = block.call
15
+ timer.cancel
16
+ res
17
+ end
18
+ end
19
+ module Sockets
20
+ module Connectable
21
+ module ClassMethods
22
+ def connect(host, port, timeout)
23
+ TimeoutHandler.timeout(timeout) do
24
+ sock = new(host, port)
25
+ #sock.set_encoding('binary')
26
+ #sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
27
+ sock
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ class EM_TCP < ::EventMachine::Synchrony::TCPSocket
34
+ include Connectable
35
+
36
+ def connection_completed
37
+ @opening = false
38
+ @in_req.succeed(self) if @in_req
39
+ end
40
+ end
41
+ Mutex = ::EventMachine::Synchrony::Thread::Mutex
42
+ ConditionVariable = ::EventMachine::Synchrony::Thread::ConditionVariable
43
+ end
44
+ class Connection
45
+ def connect
46
+ @sock = if !!options[:ssl]
47
+ Sockets::SSL.connect(host, port, timeout)
48
+ else
49
+ Sockets::EM_TCP.connect(host, port, timeout)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Webmate
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,25 @@
1
+ module Webmate::Views
2
+ class Scope
3
+ include Sinatra::Cookies
4
+ include Webmate::Sprockets::Helpers
5
+
6
+ def initialize(responder)
7
+ @responder = responder
8
+ end
9
+
10
+ def user_websocket_token
11
+ end
12
+
13
+ def javascript_client_configs
14
+ %Q{<script type="text/javascript">
15
+ if (!window.Webmate) {window.Webmate = {}};
16
+ window.Webmate.websocketsPort = #{configatron.websockets.port};
17
+ window.Webmate.websocketsEnabled = #{configatron.websockets.enabled ? 'true' : 'false'};
18
+ </script>}
19
+ end
20
+
21
+ def user_websocket_token_tag
22
+ %Q{<meta content="#{user_websocket_token}" name="websocket-token" />}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ module Webmate
2
+ class Websockets
3
+ class << self
4
+ def subscribe(session_id, request, &block)
5
+ user_id = request.env['warden'].user.id
6
+
7
+ request.websocket do |websocket|
8
+ # subscribe user to redis channel
9
+ subscribe_to_personal_channel(user_id, websocket)
10
+
11
+ websocket.onopen do
12
+ websocket.send(Webmate::SocketIO::Packets::Connect.new.to_packet)
13
+ warn("Socket connection opened for session_id: #{session_id}")
14
+ end
15
+
16
+ websocket.onmessage do |message|
17
+ response = block.call(Webmate::SocketIO::Packets::Base.parse(message))
18
+ if response
19
+ packet = Webmate::SocketIO::Packets::Message.build_response_packet(response)
20
+ websocket.send(packet.to_packet)
21
+ else
22
+ warn("empty response for #{message.inspect}")
23
+ end
24
+ end
25
+
26
+ websocket.onclose do
27
+ warn("Socket connection closed '#{session_id}'")
28
+ end
29
+ end
30
+ end
31
+
32
+ def subscribe_to_personal_channel(user_id, websocket)
33
+ channel_name = Webmate::Application.get_channel_name_for(user_id)
34
+
35
+ subscriber = EM::Hiredis.connect.pubsub
36
+ subscriber.subscribe(channel_name)
37
+ warn("user has been subscribed to channel '#{channel_name}'")
38
+
39
+ subscriber.on(:message) do |channel, message_data|
40
+ response_data = Webmate::Application.restore(message_data)
41
+ packet = Webmate::SocketIO::Packets::Message.new(response_data)
42
+
43
+ websocket.send(packet.to_packet)
44
+ end
45
+
46
+ subscriber
47
+ end
48
+ end
49
+ end
50
+ end