wwl-websocket-rails 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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 +239 -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 +46 -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 +68 -0
  19. data/lib/rails/app/controllers/websocket_rails/delegation_controller.rb +13 -0
  20. data/lib/rails/config/routes.rb +7 -0
  21. data/lib/rails/tasks/websocket_rails.tasks +42 -0
  22. data/lib/spec_helpers/matchers/route_matchers.rb +65 -0
  23. data/lib/spec_helpers/matchers/trigger_matchers.rb +138 -0
  24. data/lib/spec_helpers/spec_helper_event.rb +34 -0
  25. data/lib/websocket-rails.rb +108 -0
  26. data/lib/websocket_rails/base_controller.rb +208 -0
  27. data/lib/websocket_rails/channel.rb +97 -0
  28. data/lib/websocket_rails/channel_manager.rb +55 -0
  29. data/lib/websocket_rails/configuration.rb +177 -0
  30. data/lib/websocket_rails/connection_adapters/http.rb +120 -0
  31. data/lib/websocket_rails/connection_adapters/web_socket.rb +35 -0
  32. data/lib/websocket_rails/connection_adapters.rb +195 -0
  33. data/lib/websocket_rails/connection_manager.rb +119 -0
  34. data/lib/websocket_rails/controller_factory.rb +80 -0
  35. data/lib/websocket_rails/data_store.rb +145 -0
  36. data/lib/websocket_rails/dispatcher.rb +129 -0
  37. data/lib/websocket_rails/engine.rb +26 -0
  38. data/lib/websocket_rails/event.rb +193 -0
  39. data/lib/websocket_rails/event_map.rb +184 -0
  40. data/lib/websocket_rails/event_queue.rb +33 -0
  41. data/lib/websocket_rails/internal_events.rb +37 -0
  42. data/lib/websocket_rails/logging.rb +133 -0
  43. data/lib/websocket_rails/spec_helpers.rb +3 -0
  44. data/lib/websocket_rails/synchronization.rb +178 -0
  45. data/lib/websocket_rails/user_manager.rb +276 -0
  46. data/lib/websocket_rails/version.rb +3 -0
  47. data/spec/dummy/Rakefile +7 -0
  48. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  49. data/spec/dummy/app/controllers/chat_controller.rb +53 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/models/user.rb +2 -0
  52. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  53. data/spec/dummy/config/application.rb +45 -0
  54. data/spec/dummy/config/boot.rb +10 -0
  55. data/spec/dummy/config/database.yml +22 -0
  56. data/spec/dummy/config/environment.rb +5 -0
  57. data/spec/dummy/config/environments/development.rb +26 -0
  58. data/spec/dummy/config/environments/production.rb +49 -0
  59. data/spec/dummy/config/environments/test.rb +34 -0
  60. data/spec/dummy/config/events.rb +7 -0
  61. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  62. data/spec/dummy/config/initializers/inflections.rb +10 -0
  63. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  64. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  65. data/spec/dummy/config/initializers/session_store.rb +8 -0
  66. data/spec/dummy/config/locales/en.yml +5 -0
  67. data/spec/dummy/config/routes.rb +58 -0
  68. data/spec/dummy/config.ru +4 -0
  69. data/spec/dummy/db/development.sqlite3 +0 -0
  70. data/spec/dummy/db/migrate/20130902222552_create_users.rb +10 -0
  71. data/spec/dummy/db/schema.rb +23 -0
  72. data/spec/dummy/db/test.sqlite3 +0 -0
  73. data/spec/dummy/log/development.log +17 -0
  74. data/spec/dummy/log/production.log +0 -0
  75. data/spec/dummy/log/server.log +0 -0
  76. data/spec/dummy/log/test.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 +81 -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 +273 -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 +358 -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,193 @@
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
+ SUCCEEDED = 0
67
+ FAILED = 1
68
+ FINISHED_WITHOUT_RESULT = 2
69
+
70
+ def self.log_header
71
+ "Event"
72
+ end
73
+
74
+ def self.new_from_json(encoded_data, connection)
75
+ case encoded_data
76
+ when String
77
+ event_name, data = JSON.parse encoded_data
78
+
79
+ unless event_name.is_a?(String) && data.is_a?(Hash)
80
+ raise UnknownDataType
81
+ end
82
+
83
+ data = data.merge(:connection => connection).with_indifferent_access
84
+ Event.new event_name, data
85
+ # when Array
86
+ # TODO: Handle file
87
+ #File.open("/tmp/test#{rand(100)}.jpg", "wb") do |file|
88
+ # encoded_data.each do |byte|
89
+ # file << byte.chr
90
+ # end
91
+ #end
92
+ else
93
+ raise UnknownDataType
94
+ end
95
+ rescue JSON::ParserError, UnknownDataType => ex
96
+ warn "Invalid Event Received: #{ex}"
97
+ debug "Event Data: #{encoded_data}"
98
+ log_exception(ex)
99
+ Event.new_on_invalid_event_received(connection, nil)
100
+ end
101
+
102
+ include Logging
103
+ extend StaticEvents
104
+
105
+ attr_reader :id, :name, :connection, :namespace, :channel, :user_id, :token
106
+
107
+ attr_accessor :data, :result, :success, :server_token, :propagate
108
+
109
+ def initialize(event_name, options={})
110
+ case event_name
111
+ when String
112
+ namespace = event_name.split('.')
113
+ @name = namespace.pop.to_sym
114
+ when Symbol
115
+ @name = event_name
116
+ namespace = [:global]
117
+ end
118
+ @id = options[:id]
119
+ @data = options[:data].is_a?(Hash) ? options[:data].with_indifferent_access : options[:data]
120
+ @channel = options[:channel].to_sym rescue options[:channel].to_s.to_sym if options[:channel]
121
+ @token = options[:token] if options[:token]
122
+ @connection = options[:connection]
123
+ @server_token = options[:server_token]
124
+ @user_id = options[:user_id]
125
+ @propagate = options[:propagate].nil? ? true : options[:propagate]
126
+ @namespace = validate_namespace( options[:namespace] || namespace )
127
+ end
128
+
129
+ def as_json
130
+ [
131
+ encoded_name,
132
+ {
133
+ :id => id,
134
+ :channel => channel,
135
+ :user_id => user_id,
136
+ :data => data,
137
+ :success => success,
138
+ :result => result,
139
+ :token => token,
140
+ :server_token => server_token
141
+ }
142
+ ]
143
+ end
144
+
145
+ def serialize
146
+ as_json.to_json
147
+ end
148
+
149
+ def is_channel?
150
+ !@channel.nil?
151
+ end
152
+
153
+ def is_user?
154
+ !@user_id.nil? && !is_channel?
155
+ end
156
+
157
+ def is_invalid?
158
+ name == :invalid_event
159
+ end
160
+
161
+ def is_internal?
162
+ namespace.include?(:websocket_rails)
163
+ end
164
+
165
+ def should_propagate?
166
+ @propagate
167
+ end
168
+
169
+ def trigger
170
+ connection.trigger self if connection
171
+ end
172
+
173
+ def encoded_name
174
+ if namespace.size > 1
175
+ child_namespace = namespace.dup[1..-1]
176
+ child_namespace << name
177
+ combined_name = child_namespace.join('.')
178
+ else
179
+ combined_name = name
180
+ end
181
+ combined_name
182
+ end
183
+
184
+ private
185
+
186
+ def validate_namespace(namespace)
187
+ namespace = [namespace] unless namespace.is_a?(Array)
188
+ namespace.unshift :global unless namespace.first == :global
189
+ namespace.map(&:to_sym) rescue [:global]
190
+ end
191
+
192
+ end
193
+ end