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,80 @@
1
+ module WebsocketRails
2
+ class ControllerFactory
3
+
4
+ attr_reader :controller_stores, :dispatcher
5
+
6
+ def initialize(dispatcher)
7
+ @dispatcher = dispatcher
8
+ @controller_stores = {}
9
+ @initialized_controllers = {}
10
+ end
11
+
12
+ # TODO: Add deprecation notice for user defined
13
+ # instance variables.
14
+ def new_for_event(event, controller_class, method)
15
+ controller_class = reload!(controller_class)
16
+ controller = controller_class.new
17
+
18
+ prepare(controller, event, method)
19
+
20
+ controller
21
+ end
22
+
23
+ private
24
+
25
+ def store_for_controller(controller)
26
+ @controller_stores[controller.class] ||= DataStore::Controller.new(controller)
27
+ end
28
+
29
+ def prepare(controller, event, method)
30
+ set_event(controller, event)
31
+ set_dispatcher(controller, dispatcher)
32
+ set_controller_store(controller)
33
+ set_action_name(controller, method)
34
+ initialize_controller(controller)
35
+ end
36
+
37
+ def set_event(controller, event)
38
+ set_ivar :@_event, controller, event
39
+ end
40
+
41
+ def set_dispatcher(controller, dispatcher)
42
+ set_ivar :@_dispatcher, controller, dispatcher
43
+ end
44
+
45
+ def set_controller_store(controller)
46
+ set_ivar :@_controller_store, controller, store_for_controller(controller)
47
+ end
48
+
49
+ def set_action_name(controller, method)
50
+ set_ivar :@_action_name, controller, method.to_s
51
+ end
52
+
53
+ def set_ivar(ivar, object, value)
54
+ object.instance_variable_set(ivar, value)
55
+ end
56
+
57
+ def initialize_controller(controller)
58
+ unless @initialized_controllers[controller.class] == true
59
+ controller.send(:initialize_session) if controller.respond_to?(:initialize_session)
60
+ @initialized_controllers[controller.class] = true
61
+ end
62
+ end
63
+
64
+ # Reloads the controller class to pick up code changes
65
+ # while in the development environment.
66
+ def reload!(controller)
67
+ return controller unless defined?(Rails) and !Rails.configuration.cache_classes
68
+ # we don't reload our own controller as we assume it provide as 'library'
69
+ unless controller.name == "WebsocketRails::InternalController"
70
+ class_name = controller.name
71
+ filename = class_name.underscore
72
+ load "#{filename}.rb"
73
+ return class_name.constantize
74
+ end
75
+
76
+ return controller
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,145 @@
1
+ module WebsocketRails
2
+ # The {DataStore} provides a convenient way to persist information between
3
+ # execution of events. Since every event is executed within a new instance
4
+ # of the controller class, instance variables set while processing an
5
+ # action will be lost after the action finishes executing.
6
+ #
7
+ # There are two different {DataStore} classes that you can use:
8
+ #
9
+ # The {DataStore::Connection} class is unique for every active connection.
10
+ # You can use it similar to the Rails session store. The connection data
11
+ # store can be accessed within your controller using the `#connection_store`
12
+ # method.
13
+ #
14
+ # The {DataStore::Controller} class is unique for every controller. You
15
+ # can use it similar to how you would use instance variables within a
16
+ # plain ruby class. The values set within the controller store will be
17
+ # persisted between events. The controller store can be accessed within
18
+ # your controller using the `#controller_store` method.
19
+ module DataStore
20
+ class Base < ActiveSupport::HashWithIndifferentAccess
21
+
22
+ cattr_accessor :all_instances
23
+ @@all_instances = Hash.new { |h,k| h[k] = [] }
24
+
25
+ def self.clear_all_instances
26
+ @@all_instances = Hash.new { |h,k| h[k] = [] }
27
+ end
28
+
29
+ def initialize
30
+ instances << self
31
+ end
32
+
33
+ def instances
34
+ all_instances[self.class]
35
+ end
36
+
37
+ def collect_all(key)
38
+ collection = instances.each_with_object([]) do |instance, array|
39
+ array << instance[key]
40
+ end
41
+
42
+ if block_given?
43
+ collection.each do |item|
44
+ yield(item)
45
+ end
46
+ else
47
+ collection
48
+ end
49
+ end
50
+
51
+ def destroy!
52
+ instances.delete_if {|store| store.object_id == self.object_id }
53
+ end
54
+
55
+ end
56
+
57
+ # The connection data store operates much like the {Controller} store. The
58
+ # biggest difference is that the data placed inside is private for
59
+ # individual users and accessible from any controller. Anything placed
60
+ # inside the connection data store will be deleted when a user disconnects.
61
+ #
62
+ # The connection data store is accessed through the `#connection_store`
63
+ # instance method inside your controller.
64
+ #
65
+ # If we were writing a basic chat system, we could use the connection data
66
+ # store to hold onto a user's current screen name.
67
+ #
68
+ #
69
+ # class UserController < WebsocketRails::BaseController
70
+ #
71
+ # def set_screen_name
72
+ # connection_store[:screen_name] = message[:screen_name]
73
+ # end
74
+ #
75
+ # end
76
+ #
77
+ # class ChatController < WebsocketRails::BaseController
78
+ #
79
+ # def say_hello
80
+ # screen_name = connection_store[:screen_name]
81
+ # send_message :new_message, "#{screen_name} says hello"
82
+ # end
83
+ #
84
+ # end
85
+ class Connection < Base
86
+
87
+ attr_accessor :connection
88
+
89
+ def initialize(connection)
90
+ super()
91
+ @connection = connection
92
+ end
93
+
94
+ end
95
+
96
+ # The Controller DataStore acts as a stand-in for instance variables in your
97
+ # controller. At it's core, it is a Hash which is accessible inside your
98
+ # controller through the `#controller_store` instance method. Any values
99
+ # set in the controller store will be visible by all connected users which
100
+ # trigger events that use that controller. However, values set in one
101
+ # controller will not be visible by other controllers.
102
+ #
103
+ #
104
+ # class AccountController < WebsocketRails::BaseController
105
+ # # We will use a before filter to set the initial value
106
+ # before_action { controller_store[:event_count] ||= 0 }
107
+ #
108
+ # # Mapped as `accounts.important_event` in the Event Router
109
+ # def important_event
110
+ # # This will be private for each controller
111
+ # controller_store[:event_count] += 1
112
+ # trigger_success controller_store[:event_count]
113
+ # end
114
+ # end
115
+ #
116
+ # class ProductController < WebsocketRails::BaseController
117
+ # # We will use a before filter to set the initial value
118
+ # before_action { controller_store[:event_count] ||= 0 }
119
+ #
120
+ # # Mapped as `products.boring_event` in the Event Router
121
+ # def boring_event
122
+ # # This will be private for each controller
123
+ # controller_store[:event_count] += 1
124
+ # trigger_success controller_store[:event_count]
125
+ # end
126
+ # end
127
+ #
128
+ # # trigger `accounts.important_event`
129
+ # => 1
130
+ # # trigger `accounts.important_event`
131
+ # => 2
132
+ # # trigger `products.boring_event`
133
+ # => 1
134
+ class Controller < Base
135
+
136
+ attr_accessor :controller
137
+
138
+ def initialize(controller)
139
+ super()
140
+ @controller = controller
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,129 @@
1
+ module WebsocketRails
2
+ class Dispatcher
3
+
4
+ include Logging
5
+
6
+ attr_reader :event_map, :connection_manager, :controller_factory
7
+
8
+ delegate :filtered_channels, to: WebsocketRails
9
+
10
+ def initialize(connection_manager)
11
+ @connection_manager = connection_manager
12
+ @controller_factory = ControllerFactory.new(self)
13
+ @event_map = EventMap.new(self)
14
+ end
15
+
16
+ def receive_encoded(encoded_data,connection)
17
+ event = Event.new_from_json( encoded_data, connection )
18
+ dispatch( event )
19
+ end
20
+
21
+ def receive(event_name,data,connection)
22
+ event = Event.new event_name, data, connection
23
+ dispatch( event )
24
+ end
25
+
26
+ def dispatch(event)
27
+ return if event.is_invalid?
28
+
29
+ if event.is_channel?
30
+ filter_channel(event)
31
+ else
32
+ reload_event_map! unless event.is_internal?
33
+ route event
34
+ end
35
+ end
36
+
37
+ def send_message(event)
38
+ event.connection.trigger event
39
+ end
40
+
41
+ def broadcast_message(event)
42
+ connection_manager.connections.map do |_, connection|
43
+ connection.trigger event
44
+ end
45
+ end
46
+
47
+ def reload_event_map!
48
+ return unless defined?(Rails) and !Rails.configuration.cache_classes
49
+ begin
50
+ load "#{Rails.root}/config/events.rb"
51
+ @event_map = EventMap.new(self)
52
+ rescue Exception => ex
53
+ log(:warn, "EventMap reload failed: #{ex.message}")
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def route(event)
60
+ actions = []
61
+ event_map.routes_for event do |controller_class, method|
62
+ actions << Fiber.new do
63
+ begin
64
+ log_event(event) do
65
+ controller = controller_factory.new_for_event(event, controller_class, method)
66
+
67
+ controller.process_action(method, event)
68
+ end
69
+ rescue Exception => ex
70
+ event.success = false
71
+ event.data = extract_exception_data ex
72
+ event.trigger
73
+ end
74
+ end
75
+ end
76
+ execute actions
77
+ end
78
+
79
+ def filter_channel(event)
80
+ actions = []
81
+ actions << Fiber.new do
82
+ begin
83
+ log_event(event) do
84
+ controller_class, catch_all = filtered_channels[event.channel]
85
+
86
+ controller = controller_factory.new_for_event(event, controller_class, event.name)
87
+ # send to the method of the event name
88
+ # silently skip routing to the controller on event.name if it doesnt respond
89
+ controller.process_action(event.name, event) if controller.respond_to?(event.name)
90
+ # send to our defined catch all method
91
+ controller.process_action(catch_all, event) if catch_all
92
+
93
+ end
94
+ rescue Exception => ex
95
+ event.success = false
96
+ event.data = extract_exception_data ex
97
+ event.trigger
98
+ end
99
+ end if filtered_channels[event.channel]
100
+
101
+ actions << Fiber.new{ WebsocketRails[event.channel].trigger_event(event) }
102
+ execute actions
103
+ end
104
+
105
+ def execute(actions)
106
+ actions.map do |action|
107
+ EM.next_tick { action.resume }
108
+ end
109
+ end
110
+
111
+ def extract_exception_data(ex)
112
+ if record_invalid_defined? and ex.is_a? ActiveRecord::RecordInvalid
113
+ {
114
+ :record => ex.record.attributes,
115
+ :errors => ex.record.errors,
116
+ :full_messages => ex.record.errors.full_messages
117
+ }
118
+ else
119
+ ex if ex.respond_to?(:to_json)
120
+ end
121
+ end
122
+
123
+ def record_invalid_defined?
124
+ Object.const_defined?('ActiveRecord') and ActiveRecord.const_defined?('RecordInvalid')
125
+ end
126
+
127
+
128
+ end
129
+ end
@@ -0,0 +1,26 @@
1
+ module WebsocketRails
2
+
3
+ class Engine < Rails::Engine
4
+
5
+ config.autoload_paths += [File.expand_path("../../lib", __FILE__)]
6
+
7
+ paths["app"] << "lib/rails/app"
8
+ paths["app/controllers"] << "lib/rails/app/controllers"
9
+
10
+ if ::Rails.version >= '4.0.0'
11
+ paths["config/routes.rb"] << "lib/rails/config/routes.rb"
12
+ else
13
+ paths["config/routes"] << "lib/rails/config/routes.rb"
14
+ end
15
+
16
+ initializer 'websocket_rails.load_event_routes', :before => :preload_frameworks do |app|
17
+ load "#{Rails.root}/config/events.rb" if File.exists?("#{Rails.root}/config/events.rb")
18
+ end
19
+
20
+ rake_tasks do
21
+ require 'websocket-rails'
22
+ load 'rails/tasks/websocket_rails.tasks'
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,189 @@
1
+ module WebsocketRails
2
+
3
+ module StaticEvents
4
+
5
+ def new_on_open(connection,data=nil)
6
+ connection_id = {
7
+ :connection_id => connection.id
8
+ }
9
+ data = data.is_a?(Hash) ? data.merge( connection_id ) : connection_id
10
+ Event.new :client_connected, :data => data, :connection => connection
11
+ end
12
+
13
+ def new_on_close(connection,data=nil)
14
+ Event.new :client_disconnected, :data => data, :connection => connection
15
+ end
16
+
17
+ def new_on_error(connection,data=nil)
18
+ Event.new :client_error, :data => data, :connection => connection
19
+ end
20
+
21
+ def new_on_ping(connection)
22
+ Event.new :ping, :data => {}, :connection => connection, :namespace => :websocket_rails
23
+ end
24
+
25
+ def new_on_invalid_event_received(connection,data=nil)
26
+ Event.new :invalid_event, :data => data, :connection => connection
27
+ end
28
+
29
+ end
30
+
31
+ # Contains all of the relevant information for incoming and outgoing events.
32
+ # All events except for channel events will have a connection object associated.
33
+ #
34
+ # Events require an event name and hash of options:
35
+ #
36
+ # :data =>
37
+ # The data object will be passed to any callback functions bound on the
38
+ # client side.
39
+ #
40
+ # You can also pass a Hash of options to specify:
41
+ #
42
+ # :connection =>
43
+ # Connection that will be receiving or that sent this event.
44
+ #
45
+ # :namespace =>
46
+ # The namespace this event is under. Will default to :global
47
+ # If the namespace is nested under multiple levels pass them as an array.
48
+ # For instance, if the namespace route looks like the following:
49
+ #
50
+ # namespace :products do
51
+ # namespace :hats do
52
+ # # events
53
+ # end
54
+ # end
55
+ #
56
+ # Then you would pass the namespace argument as [:products,:hats]
57
+ #
58
+ # :channel =>
59
+ # The name of the channel that this event is destined for.
60
+ class Event
61
+
62
+ class UnknownDataType < StandardError; end;
63
+
64
+ extend Logging
65
+
66
+ def self.log_header
67
+ "Event"
68
+ end
69
+
70
+ def self.new_from_json(encoded_data, connection)
71
+ case encoded_data
72
+ when String
73
+ event_name, data = JSON.parse encoded_data
74
+
75
+ unless event_name.is_a?(String) && data.is_a?(Hash)
76
+ raise UnknownDataType
77
+ end
78
+
79
+ data = data.merge(:connection => connection).with_indifferent_access
80
+ Event.new event_name, data
81
+ # when Array
82
+ # TODO: Handle file
83
+ #File.open("/tmp/test#{rand(100)}.jpg", "wb") do |file|
84
+ # encoded_data.each do |byte|
85
+ # file << byte.chr
86
+ # end
87
+ #end
88
+ else
89
+ raise UnknownDataType
90
+ end
91
+ rescue JSON::ParserError, UnknownDataType => ex
92
+ warn "Invalid Event Received: #{ex}"
93
+ debug "Event Data: #{encoded_data}"
94
+ log_exception(ex)
95
+ Event.new_on_invalid_event_received(connection, nil)
96
+ end
97
+
98
+ include Logging
99
+ extend StaticEvents
100
+
101
+ attr_reader :id, :name, :connection, :namespace, :channel, :user_id, :token
102
+
103
+ attr_accessor :data, :result, :success, :server_token, :propagate
104
+
105
+ def initialize(event_name, options={})
106
+ case event_name
107
+ when String
108
+ namespace = event_name.split('.')
109
+ @name = namespace.pop.to_sym
110
+ when Symbol
111
+ @name = event_name
112
+ namespace = [:global]
113
+ end
114
+ @id = options[:id]
115
+ @data = options[:data].is_a?(Hash) ? options[:data].with_indifferent_access : options[:data]
116
+ @channel = options[:channel].to_sym rescue options[:channel].to_s.to_sym if options[:channel]
117
+ @token = options[:token] if options[:token]
118
+ @connection = options[:connection]
119
+ @server_token = options[:server_token]
120
+ @user_id = options[:user_id]
121
+ @propagate = options[:propagate].nil? ? true : options[:propagate]
122
+ @namespace = validate_namespace( options[:namespace] || namespace )
123
+ end
124
+
125
+ def as_json
126
+ [
127
+ encoded_name,
128
+ {
129
+ :id => id,
130
+ :channel => channel,
131
+ :user_id => user_id,
132
+ :data => data,
133
+ :success => success,
134
+ :result => result,
135
+ :token => token,
136
+ :server_token => server_token
137
+ }
138
+ ]
139
+ end
140
+
141
+ def serialize
142
+ as_json.to_json
143
+ end
144
+
145
+ def is_channel?
146
+ !@channel.nil?
147
+ end
148
+
149
+ def is_user?
150
+ !@user_id.nil? && !is_channel?
151
+ end
152
+
153
+ def is_invalid?
154
+ name == :invalid_event
155
+ end
156
+
157
+ def is_internal?
158
+ namespace.include?(:websocket_rails)
159
+ end
160
+
161
+ def should_propagate?
162
+ @propagate
163
+ end
164
+
165
+ def trigger
166
+ connection.trigger self if connection
167
+ end
168
+
169
+ def encoded_name
170
+ if namespace.size > 1
171
+ child_namespace = namespace.dup[1..-1]
172
+ child_namespace << name
173
+ combined_name = child_namespace.join('.')
174
+ else
175
+ combined_name = name
176
+ end
177
+ combined_name
178
+ end
179
+
180
+ private
181
+
182
+ def validate_namespace(namespace)
183
+ namespace = [namespace] unless namespace.is_a?(Array)
184
+ namespace.unshift :global unless namespace.first == :global
185
+ namespace.map(&:to_sym) rescue [:global]
186
+ end
187
+
188
+ end
189
+ end