hkroger-websocket-rails 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +328 -0
  3. data/Gemfile +27 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +237 -0
  6. data/Rakefile +72 -0
  7. data/bin/thin-socketrails +45 -0
  8. data/lib/assets/javascripts/websocket_rails/abstract_connection.js.coffee +45 -0
  9. data/lib/assets/javascripts/websocket_rails/channel.js.coffee +70 -0
  10. data/lib/assets/javascripts/websocket_rails/event.js.coffee +42 -0
  11. data/lib/assets/javascripts/websocket_rails/http_connection.js.coffee +66 -0
  12. data/lib/assets/javascripts/websocket_rails/main.js +6 -0
  13. data/lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee +29 -0
  14. data/lib/assets/javascripts/websocket_rails/websocket_rails.js.coffee +158 -0
  15. data/lib/config.ru +3 -0
  16. data/lib/generators/websocket_rails/install/install_generator.rb +33 -0
  17. data/lib/generators/websocket_rails/install/templates/events.rb +14 -0
  18. data/lib/generators/websocket_rails/install/templates/websocket_rails.rb +63 -0
  19. data/lib/hkroger-websocket-rails.rb +1 -0
  20. data/lib/rails/app/controllers/websocket_rails/delegation_controller.rb +13 -0
  21. data/lib/rails/config/routes.rb +7 -0
  22. data/lib/rails/tasks/websocket_rails.tasks +42 -0
  23. data/lib/spec_helpers/matchers/route_matchers.rb +65 -0
  24. data/lib/spec_helpers/matchers/trigger_matchers.rb +113 -0
  25. data/lib/spec_helpers/spec_helper_event.rb +34 -0
  26. data/lib/websocket-rails.rb +108 -0
  27. data/lib/websocket_rails/base_controller.rb +197 -0
  28. data/lib/websocket_rails/channel.rb +97 -0
  29. data/lib/websocket_rails/channel_manager.rb +55 -0
  30. data/lib/websocket_rails/configuration.rb +169 -0
  31. data/lib/websocket_rails/connection_adapters.rb +195 -0
  32. data/lib/websocket_rails/connection_adapters/http.rb +120 -0
  33. data/lib/websocket_rails/connection_adapters/web_socket.rb +36 -0
  34. data/lib/websocket_rails/connection_manager.rb +119 -0
  35. data/lib/websocket_rails/controller_factory.rb +80 -0
  36. data/lib/websocket_rails/data_store.rb +145 -0
  37. data/lib/websocket_rails/dispatcher.rb +129 -0
  38. data/lib/websocket_rails/engine.rb +26 -0
  39. data/lib/websocket_rails/event.rb +189 -0
  40. data/lib/websocket_rails/event_map.rb +184 -0
  41. data/lib/websocket_rails/event_queue.rb +33 -0
  42. data/lib/websocket_rails/internal_events.rb +37 -0
  43. data/lib/websocket_rails/logging.rb +133 -0
  44. data/lib/websocket_rails/spec_helpers.rb +3 -0
  45. data/lib/websocket_rails/synchronization.rb +182 -0
  46. data/lib/websocket_rails/user_manager.rb +276 -0
  47. data/lib/websocket_rails/version.rb +3 -0
  48. data/spec/dummy/Rakefile +7 -0
  49. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  50. data/spec/dummy/app/controllers/chat_controller.rb +53 -0
  51. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  52. data/spec/dummy/app/models/user.rb +2 -0
  53. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +45 -0
  56. data/spec/dummy/config/boot.rb +10 -0
  57. data/spec/dummy/config/database.yml +22 -0
  58. data/spec/dummy/config/environment.rb +5 -0
  59. data/spec/dummy/config/environments/development.rb +26 -0
  60. data/spec/dummy/config/environments/production.rb +49 -0
  61. data/spec/dummy/config/environments/test.rb +34 -0
  62. data/spec/dummy/config/events.rb +7 -0
  63. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/spec/dummy/config/initializers/inflections.rb +10 -0
  65. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  66. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  67. data/spec/dummy/config/initializers/session_store.rb +8 -0
  68. data/spec/dummy/config/locales/en.yml +5 -0
  69. data/spec/dummy/config/routes.rb +58 -0
  70. data/spec/dummy/db/development.sqlite3 +0 -0
  71. data/spec/dummy/db/migrate/20130902222552_create_users.rb +10 -0
  72. data/spec/dummy/db/schema.rb +23 -0
  73. data/spec/dummy/db/test.sqlite3 +0 -0
  74. data/spec/dummy/log/development.log +17 -0
  75. data/spec/dummy/log/production.log +0 -0
  76. data/spec/dummy/log/server.log +0 -0
  77. data/spec/dummy/public/404.html +26 -0
  78. data/spec/dummy/public/422.html +26 -0
  79. data/spec/dummy/public/500.html +26 -0
  80. data/spec/dummy/public/favicon.ico +0 -0
  81. data/spec/dummy/public/javascripts/application.js +2 -0
  82. data/spec/dummy/public/javascripts/controls.js +965 -0
  83. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  84. data/spec/dummy/public/javascripts/effects.js +1123 -0
  85. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  86. data/spec/dummy/public/javascripts/rails.js +202 -0
  87. data/spec/dummy/script/rails +6 -0
  88. data/spec/integration/connection_manager_spec.rb +135 -0
  89. data/spec/javascripts/support/jasmine.yml +52 -0
  90. data/spec/javascripts/support/jasmine_helper.rb +38 -0
  91. data/spec/javascripts/support/vendor/sinon-1.7.1.js +4343 -0
  92. data/spec/javascripts/websocket_rails/channel_spec.coffee +112 -0
  93. data/spec/javascripts/websocket_rails/event_spec.coffee +69 -0
  94. data/spec/javascripts/websocket_rails/helpers.coffee +6 -0
  95. data/spec/javascripts/websocket_rails/websocket_connection_spec.coffee +158 -0
  96. data/spec/javascripts/websocket_rails/websocket_rails_spec.coffee +274 -0
  97. data/spec/spec_helper.rb +41 -0
  98. data/spec/spec_helpers/matchers/route_matchers_spec.rb +109 -0
  99. data/spec/spec_helpers/matchers/trigger_matchers_spec.rb +247 -0
  100. data/spec/spec_helpers/spec_helper_event_spec.rb +66 -0
  101. data/spec/support/helper_methods.rb +42 -0
  102. data/spec/support/mock_web_socket.rb +41 -0
  103. data/spec/unit/base_controller_spec.rb +74 -0
  104. data/spec/unit/channel_manager_spec.rb +58 -0
  105. data/spec/unit/channel_spec.rb +169 -0
  106. data/spec/unit/connection_adapters/http_spec.rb +88 -0
  107. data/spec/unit/connection_adapters/web_socket_spec.rb +30 -0
  108. data/spec/unit/connection_adapters_spec.rb +259 -0
  109. data/spec/unit/connection_manager_spec.rb +148 -0
  110. data/spec/unit/controller_factory_spec.rb +76 -0
  111. data/spec/unit/data_store_spec.rb +106 -0
  112. data/spec/unit/dispatcher_spec.rb +203 -0
  113. data/spec/unit/event_map_spec.rb +120 -0
  114. data/spec/unit/event_queue_spec.rb +36 -0
  115. data/spec/unit/event_spec.rb +181 -0
  116. data/spec/unit/logging_spec.rb +162 -0
  117. data/spec/unit/synchronization_spec.rb +150 -0
  118. data/spec/unit/target_validator_spec.rb +88 -0
  119. data/spec/unit/user_manager_spec.rb +165 -0
  120. metadata +320 -0
@@ -0,0 +1,184 @@
1
+ module WebsocketRails
2
+ # Provides a DSL for mapping client events to controller actions.
3
+ #
4
+ # == Example events.rb file
5
+ # # located in config/initializers/events.rb
6
+ # WebsocketRails::EventMap.describe do
7
+ # subscribe :client_connected, to: ChatController, with_method: :client_connected
8
+ # end
9
+ #
10
+ # A single event can be mapped to any number of controller actions.
11
+ #
12
+ # subscribe :new_message, :to => ChatController, :with_method => :rebroadcast_message
13
+ # subscribe :new_message, :to => LogController, :with_method => :log_message
14
+ #
15
+ # Events can be nested underneath namesapces.
16
+ #
17
+ # namespace :product do
18
+ # subscribe :new, :to => ProductController, :with_method => :new
19
+ # end
20
+ class EventMap
21
+
22
+ def self.describe(&block)
23
+ WebsocketRails.config.route_block = block
24
+ end
25
+
26
+ attr_reader :namespace
27
+
28
+ def initialize(dispatcher)
29
+ @dispatcher = dispatcher
30
+ @namespace = DSL.new(dispatcher).evaluate WebsocketRails.config.route_block
31
+ @namespace = DSL.new(dispatcher,@namespace).evaluate InternalEvents.events
32
+ end
33
+
34
+ def routes_for(event, &block)
35
+ @namespace.routes_for event, &block
36
+ end
37
+
38
+ # Proxy the reload_controllers! method to the global namespace.
39
+ def reload_controllers!
40
+ @namespace.reload_controllers!
41
+ end
42
+
43
+ # Provides the DSL methods available to the Event routes file
44
+ class DSL
45
+
46
+ def initialize(dispatcher,namespace=nil)
47
+ if namespace
48
+ @namespace = namespace
49
+ else
50
+ @namespace = Namespace.new :global, dispatcher
51
+ end
52
+ end
53
+
54
+ def evaluate(route_block)
55
+ instance_eval &route_block unless route_block.nil?
56
+ @namespace
57
+ end
58
+
59
+ def subscribe(event_name,options)
60
+ @namespace.store event_name, options
61
+ end
62
+
63
+ def namespace(new_namespace,&block)
64
+ @namespace = @namespace.find_or_create new_namespace
65
+ instance_eval &block if block.present?
66
+ @namespace = @namespace.parent
67
+ end
68
+
69
+ def private_channel(channel)
70
+ WebsocketRails[channel].make_private
71
+ end
72
+
73
+ end
74
+
75
+ # Stores route map for nested namespaces
76
+ class Namespace
77
+
78
+ include Logging
79
+
80
+ attr_reader :name, :controllers, :actions, :namespaces, :parent
81
+
82
+ def initialize(name,dispatcher,parent=nil)
83
+ @name = name
84
+ @parent = parent
85
+ @dispatcher = dispatcher
86
+ @actions = Hash.new {|h,k| h[k] = Array.new}
87
+ @controllers = Hash.new
88
+ @namespaces = Hash.new
89
+ end
90
+
91
+ def find_or_create(namespace)
92
+ unless child = namespaces[namespace]
93
+ child = Namespace.new namespace, @dispatcher, self
94
+ namespaces[namespace] = child
95
+ end
96
+ child
97
+ end
98
+
99
+ # Stores controller/action pairs for events subscribed under
100
+ # this namespace.
101
+ def store(event_name,options)
102
+ klass, action = TargetValidator.validate_target options
103
+ actions[event_name] << [klass,action]
104
+ end
105
+
106
+ # Iterates through the namespace tree and yields all
107
+ # controller/action pairs stored for the target event.
108
+ def routes_for(event, event_namespace=nil, &block)
109
+
110
+ # Grab the first level namespace from the namespace array
111
+ # and remove it from the copy.
112
+ event_namespace = copy_event_namespace( event, event_namespace ) || return
113
+ namespace = event_namespace.shift
114
+
115
+ # If the namespace matches the current namespace and we are
116
+ # at the last namespace level, yield any controller/action
117
+ # pairs for this event.
118
+ #
119
+ # If the namespace does not match, search the list of child
120
+ # namespaces stored at this level for a match and delegate
121
+ # to it's #routes_for method, passing along the current
122
+ # copy of the event's namespace array.
123
+ if namespace == @name and event_namespace.empty?
124
+ actions[event.name].each do |klass,action|
125
+ block.call klass, action
126
+ end
127
+ else
128
+ child_namespace = event_namespace.first
129
+ child = namespaces[child_namespace]
130
+ child.routes_for event, event_namespace, &block unless child.nil?
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ def copy_event_namespace(event, namespace=nil)
137
+ namespace = event.namespace.dup if namespace.nil?
138
+ namespace
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+
145
+ class TargetValidator
146
+
147
+ # Parses the target and extracts controller/action pair or raises an error if target is invalid
148
+ def self.validate_target(target)
149
+ case target
150
+ when Hash
151
+ validate_hash_target target
152
+ when String
153
+ validate_string_target target
154
+ else
155
+ raise('Must specify the event target either as a string product#new_product or as a Hash to: ProductController, with_method: :new_product')
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ # Parses the target as a Hash, expecting keys to: and with_method:
162
+ def self.validate_hash_target(target)
163
+ klass = target[:to] || raise("Must specify a class for to: option in event route")
164
+ action = target[:with_method] || raise("Must specify a method for with_method: option in event route")
165
+ [klass, action]
166
+ end
167
+
168
+ # Parses the target as a String, expecting it to be in the format "product#new_product"
169
+ def self.validate_string_target(target)
170
+ strings = target.split('#')
171
+ raise('The string must be in a format like product#new_product') unless strings.count == 2
172
+ klass = constantize_controller strings[0]
173
+ action = strings[1].to_sym
174
+ [klass, action]
175
+ end
176
+
177
+ def self.constantize_controller(controller_string)
178
+ strings = (controller_string << '_controller').split('/')
179
+ strings.map{|string| string.camelize}.join('::').constantize
180
+ end
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,33 @@
1
+ module WebsocketRails
2
+ class EventQueue
3
+
4
+ attr_reader :queue
5
+
6
+ def initialize
7
+ @queue = []
8
+ end
9
+
10
+ def enqueue(event)
11
+ @queue << event
12
+ end
13
+ alias :<< :enqueue
14
+
15
+ def last
16
+ @queue.last
17
+ end
18
+
19
+ def size
20
+ @queue.size
21
+ end
22
+
23
+ def flush(&block)
24
+ unless block.nil?
25
+ @queue.each do |item|
26
+ block.call item
27
+ end
28
+ end
29
+ @queue = []
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ module WebsocketRails
2
+ class InternalEvents
3
+ def self.events
4
+ Proc.new do
5
+ namespace :websocket_rails do
6
+ subscribe :pong, :to => InternalController, :with_method => :do_pong
7
+ subscribe :subscribe, :to => InternalController, :with_method => :subscribe_to_channel
8
+ subscribe :unsubscribe, :to => InternalController, :with_method => :unsubscribe_to_channel
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ class InternalController < BaseController
15
+ include Logging
16
+
17
+ def subscribe_to_channel
18
+ channel_name = event.data[:channel]
19
+ unless WebsocketRails[channel_name].is_private?
20
+ WebsocketRails[channel_name].subscribe connection
21
+ trigger_success
22
+ else
23
+ trigger_failure( { :reason => "channel is private", :hint => "use subscribe_private instead." } )
24
+ end
25
+ end
26
+
27
+ def unsubscribe_to_channel
28
+ channel_name = event.data[:channel]
29
+ WebsocketRails[channel_name].unsubscribe connection
30
+ trigger_success
31
+ end
32
+
33
+ def do_pong
34
+ connection.pong = true
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,133 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'active_support/core_ext/hash'
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
5
+
6
+ module WebsocketRails
7
+ module Logging
8
+ # Logging module heavily influenced by Travis-Support library
9
+
10
+ LOGGABLE_DATA = [
11
+ String,
12
+ Hash,
13
+ ActiveSupport::HashWithIndifferentAccess
14
+ ]
15
+
16
+ ANSI = {
17
+ :red => 31,
18
+ :green => 32,
19
+ :yellow => 33,
20
+ :cyan => 36
21
+ }
22
+
23
+ class << self
24
+ def configure(logger)
25
+ logger.tap do
26
+ logger.formatter = proc { |*args| Format.format(*args) }
27
+ logger.level = Logger.const_get(log_level.to_s.upcase)
28
+ end
29
+ end
30
+
31
+ def log_level
32
+ WebsocketRails.config.log_level || :debug
33
+ end
34
+ end
35
+
36
+ delegate :logger, :to => WebsocketRails
37
+
38
+ [:fatal, :error, :warn, :info, :debug].each do |level|
39
+ define_method(level) do |*args|
40
+ message, options = *args
41
+ log(level, message, options)
42
+ end
43
+ end
44
+
45
+ def log(level, message, options = {})
46
+ message.chomp.split("\n").each do |line|
47
+ logger.send(level, wrap(level, self, line, options || {}))
48
+ end
49
+ end
50
+
51
+ def log_event_start(event)
52
+ message = "Started Event: #{event.encoded_name}\n"
53
+ message << "#{colorize(:cyan, "Name:")} #{event.encoded_name}\n"
54
+ message << "#{colorize(:cyan, "Data:")} #{event.data.inspect}\n" if log_data?(event)
55
+ message << "#{colorize(:cyan, "Connection:")} #{event.connection}\n\n"
56
+ info message
57
+ end
58
+
59
+ def log_event_end(event, time)
60
+ info "Event #{event.encoded_name} Finished in #{time.to_f.to_d.to_s} seconds\n\n"
61
+ end
62
+
63
+ def log_event(event, &block)
64
+ log_event_start(event) if log_event?(event)
65
+ start_time = Time.now
66
+ block.call
67
+ total_time = Time.now - start_time
68
+ log_event_end(event, total_time) if log_event?(event)
69
+ rescue Exception => ex
70
+ log_exception(ex)
71
+ raise
72
+ end
73
+
74
+ def log_event?(event)
75
+ if event.is_internal?
76
+ WebsocketRails.config.log_internal_events?
77
+ else
78
+ true
79
+ end
80
+ end
81
+
82
+ def log_data?(event)
83
+ LOGGABLE_DATA.include?(event.data.class)
84
+ end
85
+
86
+ def log_exception(exception)
87
+ logger.error(wrap(:error, self, "#{exception.class.name}: #{exception.message}"))
88
+ exception.backtrace.each { |line| logger.error(wrap(:error, self, line)) } if exception.backtrace
89
+ logger << "\n"
90
+ rescue Exception => ex
91
+ puts '--- FATAL ---'
92
+ puts 'an exception occured while logging an exception'
93
+ puts ex.message, ex.backtrace
94
+ puts exception.message, exception.backtrace
95
+ end
96
+
97
+ def wrap(level, object, message, options = {})
98
+ header = options[:header] || object.log_header
99
+ color = color_for_level(level)
100
+ "[#{colorize(color, header)}] #{message.chomp}"
101
+ end
102
+
103
+ def colorize(color, text)
104
+ "\e[#{ANSI[color]}m#{text}\e[0m"
105
+ end
106
+
107
+ def color_for_level(level)
108
+ case level
109
+ when :info then :green
110
+ when :debug then :yellow
111
+ else
112
+ :red
113
+ end
114
+ end
115
+
116
+ def log_header
117
+ self.class.name.split('::').last
118
+ end
119
+
120
+ module Format
121
+ class << self
122
+ def format(severity, time, progname, msg)
123
+ "#{severity[0, 1]} [#{format_time(time)}] #{msg}\n"
124
+ end
125
+
126
+ def format_time(time)
127
+ time.strftime("%Y-%m-%d %H:%M:%S.") << time.usec.to_s[0, 3]
128
+ end
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,3 @@
1
+ require 'spec_helpers/spec_helper_event'
2
+ require 'spec_helpers/matchers/route_matchers'
3
+ require 'spec_helpers/matchers/trigger_matchers'
@@ -0,0 +1,182 @@
1
+ require "redis/connection/synchrony"
2
+ require "redis"
3
+ require "redis/connection/ruby"
4
+
5
+ module WebsocketRails
6
+ class Synchronization
7
+
8
+ def self.all_users
9
+ singleton.all_users
10
+ end
11
+
12
+ def self.find_user(connection)
13
+ singleton.find_user connection
14
+ end
15
+
16
+ def self.register_user(connection)
17
+ singleton.register_user connection
18
+ end
19
+
20
+ def self.destroy_user(connection)
21
+ singleton.destroy_user connection
22
+ end
23
+
24
+ def self.publish(event)
25
+ singleton.publish event
26
+ end
27
+
28
+ def self.synchronize!
29
+ singleton.synchronize!
30
+ end
31
+
32
+ def self.shutdown!
33
+ singleton.shutdown!
34
+ end
35
+
36
+ def self.redis
37
+ singleton.redis
38
+ end
39
+
40
+ def self.singleton
41
+ @singleton ||= new
42
+ end
43
+
44
+ include Logging
45
+
46
+ def redis
47
+ @redis ||= begin
48
+ redis_options = WebsocketRails.config.redis_options
49
+ debug "Reactor is not running - engaging ruby redis" unless EM.reactor_running?
50
+ debug "Reactor is running - engaging standard redis new" if EM.reactor_running?
51
+ EM.reactor_running? ? Redis.new(redis_options) : ruby_redis
52
+ end
53
+ end
54
+
55
+ def ruby_redis
56
+ @ruby_redis ||= begin
57
+ WebsocketRails.config.redis_options.merge(:driver => :ruby) unless WebsocketRails.config.redis_options.has_key? :driver
58
+ redis_options = WebsocketRails.config.redis_options
59
+ Redis.new(redis_options)
60
+ end
61
+ end
62
+
63
+ def publish(event)
64
+ Fiber.new do
65
+ event.server_token = server_token
66
+ redis.publish "websocket_rails.events", event.serialize
67
+ end.resume
68
+ end
69
+
70
+ def server_token
71
+ @server_token
72
+ end
73
+
74
+ def synchronize!
75
+ unless @synchronizing
76
+ @server_token = generate_server_token
77
+ register_server(@server_token)
78
+
79
+ synchro = Fiber.new do
80
+ EM.defer do
81
+ fiber_redis = Redis.new(WebsocketRails.config.redis_options)
82
+ fiber_redis.subscribe "websocket_rails.events" do |on|
83
+ debug "Subscribed to websocket_rails events"
84
+
85
+ on.message do |_, encoded_event|
86
+ event = Event.new_from_json(encoded_event, nil)
87
+
88
+ # Do nothing if this is the server that sent this event.
89
+ next if event.server_token == server_token
90
+
91
+ # Ensure an event never gets triggered twice. Events added to the
92
+ # redis queue from other processes may not have a server token
93
+ # attached.
94
+ event.server_token = server_token if event.server_token.nil?
95
+
96
+ trigger_incoming event
97
+ end
98
+ end
99
+ end
100
+ info "Beginning Synchronization"
101
+ end
102
+ @synchronizing = true
103
+
104
+ EM.next_tick { synchro.resume }
105
+
106
+ trap('TERM') do
107
+ Thread.new { shutdown! }
108
+ end
109
+ trap('INT') do
110
+ Thread.new { shutdown! }
111
+ end
112
+ trap('QUIT') do
113
+ Thread.new { shutdown! }
114
+ end
115
+ end
116
+ end
117
+
118
+ def trigger_incoming(event)
119
+ case
120
+ when event.is_channel?
121
+ WebsocketRails[event.channel].trigger_event(event)
122
+ when event.is_user?
123
+ connection = WebsocketRails.users[event.user_id.to_s]
124
+ return if connection.nil?
125
+ connection.trigger event
126
+ end
127
+ end
128
+
129
+ def shutdown!
130
+ remove_server(server_token)
131
+ end
132
+
133
+ def generate_server_token
134
+ begin
135
+ token = SecureRandom.urlsafe_base64
136
+ end while redis.sismember("websocket_rails.active_servers", token)
137
+
138
+ token
139
+ end
140
+
141
+ def register_server(token)
142
+ Fiber.new do
143
+ redis.sadd "websocket_rails.active_servers", token
144
+ info "Server Registered: #{token}"
145
+ end.resume
146
+ end
147
+
148
+ def remove_server(token)
149
+ ruby_redis.srem "websocket_rails.active_servers", token
150
+ info "Server Removed: #{token}"
151
+ EM.stop
152
+ end
153
+
154
+ def register_user(connection)
155
+ Fiber.new do
156
+ id = connection.user_identifier
157
+ user = connection.user
158
+ redis.hset 'websocket_rails.users', id, user.as_json(root: false).to_json
159
+ end.resume
160
+ end
161
+
162
+ def destroy_user(identifier)
163
+ Fiber.new do
164
+ redis.hdel 'websocket_rails.users', identifier
165
+ end.resume
166
+ end
167
+
168
+ def find_user(identifier)
169
+ Fiber.new do
170
+ raw_user = redis.hget('websocket_rails.users', identifier)
171
+ raw_user ? JSON.parse(raw_user) : nil
172
+ end.resume
173
+ end
174
+
175
+ def all_users
176
+ Fiber.new do
177
+ redis.hgetall('websocket_rails.users')
178
+ end.resume
179
+ end
180
+
181
+ end
182
+ end