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,65 @@
1
+ module WebsocketRails
2
+
3
+ module SpecHelpers
4
+
5
+ def self.verify_route(event, target, non_exclusive)
6
+
7
+ raise ArgumentError, 'event must be of type SpecHelperEvent' unless event.is_a? WebsocketRails::SpecHelperEvent
8
+ target_class, target_method = WebsocketRails::TargetValidator.validate_target target
9
+
10
+ result = false
11
+ no_of_routes = 0
12
+ event.dispatcher.event_map.routes_for event do |controller_class, method|
13
+ no_of_routes += 1
14
+ controller = controller_class.new
15
+ if controller.class == target_class and method == target_method
16
+ result = true
17
+ end
18
+ end
19
+ result and (non_exclusive or no_of_routes == 1)
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+
27
+ RSpec::Matchers.define :be_routed_to do |target|
28
+
29
+ match do |event|
30
+ WebsocketRails::SpecHelpers.verify_route event, target, true
31
+ end
32
+
33
+ failure_message_for_should do |event|
34
+ "expected event #{event.name} to be routed to target #{target}"
35
+ end
36
+
37
+ failure_message_for_should_not do |event|
38
+ "expected event #{event.name} not to be routed to target #{target}"
39
+ end
40
+
41
+ description do
42
+ "be routed to target #{target}"
43
+ end
44
+
45
+ end
46
+
47
+ RSpec::Matchers.define :be_routed_only_to do |target|
48
+
49
+ match do |event|
50
+ WebsocketRails::SpecHelpers.verify_route event, target, false
51
+ end
52
+
53
+ failure_message_for_should do |event|
54
+ "expected event #{event.name} to be routed only to target #{target}"
55
+ end
56
+
57
+ failure_message_for_should_not do |event|
58
+ "expected event #{event.name} not to be routed only to target #{target}"
59
+ end
60
+
61
+ description do
62
+ "be routed only to target #{target}"
63
+ end
64
+
65
+ end
@@ -0,0 +1,138 @@
1
+ module WebsocketRails
2
+
3
+ module SpecHelpers
4
+
5
+ def self.compare_trigger_data(event, data)
6
+ return true if data.nil?
7
+ return true if data == :any and event.data
8
+ return true if data == :nil and event.data.nil?
9
+ data.eql? event.data
10
+ end
11
+
12
+ def self.expected_data_for_spec_message(data)
13
+ case data
14
+ when nil
15
+ ''
16
+ when :nil
17
+ ' with no data'
18
+ when :any
19
+ ' with some data'
20
+ else
21
+ " with data #{data}"
22
+ end
23
+ end
24
+
25
+ def self.actual_data_for_spec_message(data)
26
+ data ? "with data #{data}": 'with no data'
27
+ end
28
+
29
+ def self.actual_for_spec_message(event)
30
+ if event.triggered?
31
+ success = event.success
32
+ if success.nil?
33
+ "triggered message #{actual_data_for_spec_message(event.data)}"
34
+ else
35
+ success_state =
36
+ case success
37
+ when 0 then "a success"
38
+ when 1 then "a failure"
39
+ when 2 then "a no result"
40
+ else success
41
+ end
42
+ "triggered #{success_state} message #{actual_data_for_spec_message(event.data)}"
43
+ end
44
+ else
45
+ 'did not trigger any message'
46
+ end
47
+ end
48
+
49
+ def self.verify_trigger(event, data, success=nil)
50
+ return false unless event.triggered?
51
+ return false unless compare_trigger_data(event, data)
52
+ success.nil? || success == event.success
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+
60
+ RSpec::Matchers.define :trigger_message do |data|
61
+ match do |event|
62
+ WebsocketRails::SpecHelpers.verify_trigger event, data
63
+ end
64
+
65
+ failure_message_for_should do |event|
66
+ "expected #{event.encoded_name} to trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, " +
67
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event}"
68
+ end
69
+
70
+ failure_message_for_should_not do |event|
71
+ "expected #{event.encoded_name} not to trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
72
+ end
73
+
74
+ description do
75
+ "trigger message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
76
+ end
77
+ end
78
+
79
+ RSpec::Matchers.define :trigger_success_message do |data|
80
+
81
+ match do |event|
82
+ WebsocketRails::SpecHelpers.verify_trigger event, data, 0
83
+ end
84
+
85
+ failure_message_for_should do |event|
86
+ "expected #{event.encoded_name} to trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, "+
87
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event}"
88
+ end
89
+
90
+ failure_message_for_should_not do |event|
91
+ "expected #{event.encoded_name} not to trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
92
+ end
93
+
94
+ description do
95
+ "trigger success message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
96
+ end
97
+
98
+ end
99
+
100
+ RSpec::Matchers.define :trigger_failure_message do |data|
101
+
102
+ match do |event|
103
+ WebsocketRails::SpecHelpers.verify_trigger event, data, 1
104
+ end
105
+
106
+ failure_message_for_should do |event|
107
+ "expected #{event.encoded_name} to trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, " +
108
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event}"
109
+ end
110
+
111
+ failure_message_for_should_not do |event|
112
+ "expected #{event.encoded_name} not to trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
113
+ end
114
+
115
+ description do
116
+ "trigger failure message#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
117
+ end
118
+
119
+ end
120
+
121
+ Rspec::Matchers.define :trigger_no_result_message do |data|
122
+ match do |event|
123
+ WebsocketRails::SpecHelpers.verify_trigger event, data, 2
124
+ end
125
+
126
+ failure_message_for_should do |event|
127
+ "expected #{event.encoded_name} to trigger no result message (success == 2)#{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}, "+
128
+ "instead it #{WebsocketRails::SpecHelpers.actual_for_spec_message event}"
129
+ end
130
+
131
+ failure_message_for_should_not do |event|
132
+ "expected #{event.encoded_name} not to trigger no result message (success == 2)"
133
+ end
134
+
135
+ description do
136
+ "trigger no result message #{WebsocketRails::SpecHelpers.expected_data_for_spec_message data}"
137
+ end
138
+ end
@@ -0,0 +1,34 @@
1
+ module WebsocketRails
2
+
3
+ class SpecHelperEvent < Event
4
+
5
+ attr_reader :dispatcher, :triggered
6
+
7
+ alias :triggered? :triggered
8
+
9
+ def initialize(event_name,options={})
10
+ super(event_name, options)
11
+ @triggered = false
12
+ @dispatcher = Dispatcher.new(nil)
13
+ end
14
+
15
+ def trigger
16
+ @triggered = true
17
+ end
18
+
19
+ def dispatch
20
+ @dispatcher.dispatch(self)
21
+ self
22
+ end
23
+
24
+ def connection
25
+ OpenStruct.new(:id => 1)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ def create_event(name, data)
33
+ WebsocketRails::SpecHelperEvent.new(name, {data: data})
34
+ end
@@ -0,0 +1,108 @@
1
+ require "active_support/dependencies"
2
+ require "logger"
3
+ require "thin"
4
+
5
+ module WebsocketRails
6
+
7
+ class << self
8
+ def setup
9
+ yield config
10
+ end
11
+
12
+ def config
13
+ @config ||= Configuration.new
14
+ end
15
+
16
+ def synchronize?
17
+ config.synchronize == true || config.standalone == true
18
+ end
19
+
20
+ def standalone?
21
+ config.standalone == true
22
+ end
23
+
24
+ def logger
25
+ config.logger
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ require 'websocket_rails/engine'
32
+
33
+ require 'websocket_rails/configuration'
34
+ require 'websocket_rails/logging'
35
+ require 'websocket_rails/synchronization'
36
+ require 'websocket_rails/connection_manager'
37
+ require 'websocket_rails/dispatcher'
38
+ require 'websocket_rails/controller_factory'
39
+ require 'websocket_rails/event'
40
+ require 'websocket_rails/event_map'
41
+ require 'websocket_rails/event_queue'
42
+ require 'websocket_rails/channel'
43
+ require 'websocket_rails/channel_manager'
44
+ require 'websocket_rails/user_manager'
45
+ require 'websocket_rails/base_controller'
46
+ require 'websocket_rails/internal_events'
47
+
48
+ require 'websocket_rails/connection_adapters'
49
+ require 'websocket_rails/connection_adapters/http'
50
+ require 'websocket_rails/connection_adapters/web_socket'
51
+
52
+ # Exceptions
53
+ class WebsocketRails::InvalidConnectionError < StandardError
54
+ def rack_response
55
+ [400,{'Content-Type' => 'text/plain'},['invalid connection']]
56
+ end
57
+ end
58
+
59
+ class WebsocketRails::EventRoutingError < StandardError
60
+
61
+ attr_reader :event, :controller, :method
62
+
63
+ def initialize(event, controller, method)
64
+ @event = event
65
+ @controller = controller
66
+ end
67
+
68
+ def to_s
69
+ out = "Routing Error:\n"
70
+ out << "Event: #{event.name}\n"
71
+ out << "Controller #{controller.class} does not respond to #{method}"
72
+ out
73
+ end
74
+
75
+ def to_json
76
+ super({
77
+ :error => "EventRoutingError",
78
+ :event => event.name,
79
+ :method => method,
80
+ :reason => "Controller #{controller.class} does not respond to #{method}"
81
+ })
82
+ end
83
+
84
+ end
85
+
86
+ class WebsocketRails::ConfigDeprecationError < StandardError
87
+ def to_s
88
+ out = "Deprecation Error:\n\n\t"
89
+ out << "config/initializers/events.rb has been moved to config/events.rb\n\t"
90
+ out << "Make sure events.rb is in the proper location and the old one has been removed.\n\t"
91
+ out << "More information can be found in the wiki.\n\n"
92
+ end
93
+ end
94
+
95
+ raise WebsocketRails::ConfigDeprecationError if File.exists?("config/initializers/events.rb")
96
+
97
+
98
+ # Deprecation Notices
99
+ class WebsocketRails::Dispatcher
100
+ def self.describe_events(&block)
101
+ raise "This method has been deprecated. Please use WebsocketRails::EventMap.describe instead."
102
+ end
103
+ end
104
+ class WebsocketRails::Events
105
+ def self.describe_events(&block)
106
+ raise "This method has been deprecated. Please use WebsocketRails::EventMap.describe instead."
107
+ end
108
+ end
@@ -0,0 +1,208 @@
1
+ require "websocket_rails/data_store"
2
+ require 'abstract_controller/callbacks'
3
+
4
+ module WebsocketRails
5
+ # Provides controller helper methods for developing a WebsocketRails controller. Action methods
6
+ # defined on a WebsocketRails controller can be mapped to events using the {EventMap} class.
7
+ #
8
+ # == Example WebsocketRails controller
9
+ # class ChatController < WebsocketRails::BaseController
10
+ # # Can be mapped to the :client_connected event in the events.rb file.
11
+ # def new_user
12
+ # send_message :new_message, {:message => 'Welcome to the Chat Room!'}
13
+ # end
14
+ # end
15
+ #
16
+ # It is best to use the provided {DataStore} to temporarily persist data for each client between
17
+ # events. Read more about it in the {DataStore} documentation.
18
+ #
19
+ #
20
+ class BaseController
21
+
22
+ # We need process_action to be in a module loaded before AbstractController::Callbacks
23
+ # to get inheritance properly
24
+ module Metal
25
+ def process_action(method, event)
26
+ if respond_to?(method)
27
+ self.send(method)
28
+ trigger_finished
29
+ else
30
+ raise EventRoutingError.new(event, self, method)
31
+ end
32
+ end
33
+ def response_body
34
+ false
35
+ end
36
+ end
37
+
38
+ include Metal
39
+ include AbstractController::Callbacks
40
+
41
+ # Tell Rails that BaseController and children can be reloaded when in
42
+ # the Development environment.
43
+ def self.inherited(controller)
44
+ unless controller.name == "WebsocketRails::InternalController" || Rails.version =~/^4/
45
+ unloadable controller
46
+ end
47
+ end
48
+
49
+ # Tell the dispatcher to use channel filtering on specific channels.
50
+ # If supplied, the :catch_all => :method will be processed for every
51
+ # event that comes into the channel(s).
52
+ #
53
+ #
54
+ # Example:
55
+ # To process events based upon the event_name inside :channel_one
56
+ #
57
+ # filter_for_channels :channel_one
58
+ #
59
+ # To process events based upon the event_name and a catch all
60
+ #
61
+ # filter_for_channels :channel_one, :catch_all => :logger_method
62
+ #
63
+ def self.filter_for_channels(*channels)
64
+ options = channels.last.is_a?(Hash) ? channels.pop : {}
65
+ channels.each do |channel|
66
+ WebsocketRails.filtered_channels[channel] = options[:catch_all].nil? ? self : [self, options[:catch_all]]
67
+ end
68
+ end
69
+
70
+ # Provides direct access to the connection object for the client that
71
+ # initiated the event that is currently being executed.
72
+ def connection
73
+ @_event.connection
74
+ end
75
+
76
+ # The numerical ID for the client connection that initiated the event. The ID is unique
77
+ # for each currently active connection but can not be used to associate a client between
78
+ # multiple connection attempts.
79
+ def client_id
80
+ connection.id
81
+ end
82
+
83
+ # The {Event} object that triggered this action.
84
+ # Find the current event name with event.name
85
+ # Access the data sent with the event with event.data
86
+ # Find the event's namespace with event.namespace
87
+ def event
88
+ @_event
89
+ end
90
+
91
+ # The current message that was passed from the client when the event was initiated. The
92
+ # message is typically a standard ruby Hash object. See the README for more information.
93
+ def message
94
+ @_event.data
95
+ end
96
+ alias_method :data, :message
97
+
98
+ # Trigger the success callback function attached to the client event that triggered
99
+ # this action. The object passed to this method will be passed as an argument to
100
+ # the callback function on the client.
101
+ def trigger_success(data=nil)
102
+ event.success = Event::SUCCEEDED
103
+ event.data = data
104
+ event.trigger
105
+ end
106
+
107
+ # Trigger the failure callback function attached to the client event that triggered
108
+ # this action. The object passed to this method will be passed as an argument to
109
+ # the callback function on the client.
110
+ def trigger_failure(data=nil)
111
+ event.success = Event::FAILED
112
+ event.data = data
113
+ event.trigger
114
+ end
115
+
116
+ def accept_channel(data=nil)
117
+ channel_name = event.data[:channel]
118
+ WebsocketRails[channel_name].subscribe connection
119
+ trigger_success data
120
+ end
121
+
122
+ def deny_channel(data=nil)
123
+ trigger_failure data
124
+ end
125
+
126
+ def stop_event_propagation!
127
+ event.propagate = false
128
+ end
129
+
130
+ # Sends a message to the client that initiated the current event being executed. Messages
131
+ # are serialized as JSON into a two element Array where the first element is the event
132
+ # and the second element is the message that was passed, typically a Hash.
133
+ #
134
+ # To send an event under a namespace, add the `:namespace => :target_namespace` option.
135
+ #
136
+ # send_message :new_message, message_hash, :namespace => :product
137
+ #
138
+ # Nested namespaces can be passed as an array like the following:
139
+ #
140
+ # send_message :new, message_hash, :namespace => [:products,:glasses]
141
+ #
142
+ # See the {EventMap} documentation for more on mapping namespaced actions.
143
+ def send_message(event_name, message, options={})
144
+ options.merge! :connection => connection, :data => message
145
+ event = Event.new( event_name, options )
146
+ @_dispatcher.send_message event if @_dispatcher.respond_to?(:send_message)
147
+ end
148
+
149
+ # Broadcasts a message to all connected clients. See {#send_message} for message passing details.
150
+ def broadcast_message(event_name, message, options={})
151
+ options.merge! :connection => connection, :data => message
152
+ event = Event.new( event_name, options )
153
+ @_dispatcher.broadcast_message event if @_dispatcher.respond_to?(:broadcast_message)
154
+ end
155
+
156
+ def request
157
+ connection.request
158
+ end
159
+
160
+ def action_name
161
+ @_action_name
162
+ end
163
+
164
+ # Provides access to the {DataStore} for the current controller. The {DataStore} provides convenience
165
+ # methods for keeping track of data associated with active connections. See it's documentation for
166
+ # more information.
167
+ def controller_store
168
+ @_controller_store
169
+ end
170
+
171
+ def connection_store
172
+ connection.data_store
173
+ end
174
+
175
+ def self.controller_name
176
+ self.name.underscore.gsub(/_controller$/,'')
177
+ end
178
+
179
+ def controller_name
180
+ self.class.controller_name
181
+ end
182
+
183
+ private
184
+
185
+ def delegate
186
+ connection.controller_delegate
187
+ end
188
+
189
+ def method_missing(method,*args,&block)
190
+ if delegate.respond_to? method
191
+ delegate.send method, *args, &block
192
+ else
193
+ super
194
+ end
195
+ end
196
+
197
+ def trigger_finished
198
+ return if event.nil? || event.success
199
+ if WebsocketRails.config.trigger_success_by_default
200
+ event.success = Event::SUCCEEDED
201
+ else
202
+ event.success = Event::FINISHED_WITHOUT_RESULT
203
+ end
204
+ event.trigger
205
+ end
206
+
207
+ end
208
+ end
@@ -0,0 +1,97 @@
1
+ module WebsocketRails
2
+ class Channel
3
+
4
+ include Logging
5
+
6
+ delegate :config, :channel_tokens, :channel_manager, :filtered_channels, :to => WebsocketRails
7
+
8
+ attr_reader :name, :subscribers
9
+
10
+ def initialize(channel_name)
11
+ @subscribers = []
12
+ @name = channel_name
13
+ @private = false
14
+ end
15
+
16
+ def subscribe(connection)
17
+ info "#{connection} subscribed to channel #{@name}"
18
+ trigger 'subscriber_join', connection.user if config.broadcast_subscriber_events?
19
+ @subscribers << connection
20
+ send_token connection
21
+ end
22
+
23
+ def unsubscribe(connection)
24
+ return unless @subscribers.include? connection
25
+ info "#{connection} unsubscribed from channel #{@name}"
26
+ @subscribers.delete connection
27
+ trigger 'subscriber_part', connection.user if config.broadcast_subscriber_events?
28
+ end
29
+
30
+ def trigger(event_name,data={},options={})
31
+ options.merge! :channel => @name, :token => token
32
+ options[:data] = data
33
+
34
+ event = Event.new event_name, options
35
+
36
+ info "[#{@name}] #{event.data.inspect}"
37
+ send_data event
38
+ end
39
+
40
+ def trigger_event(event)
41
+ return if event.token != token
42
+ info "[#{@name}] #{event.data.inspect}"
43
+ send_data event
44
+ end
45
+
46
+ def make_private
47
+ unless config.keep_subscribers_when_private?
48
+ @subscribers.clear
49
+ end
50
+ @private = true
51
+ end
52
+
53
+ def filter_with(controller, catch_all=nil)
54
+ filtered_channels[@name] = catch_all.nil? ? controller : [controller, catch_all]
55
+ end
56
+
57
+ def is_private?
58
+ @private
59
+ end
60
+
61
+ def token
62
+ @token ||= channel_tokens[@name] ||= generate_unique_token
63
+ end
64
+
65
+ private
66
+
67
+ def generate_unique_token
68
+ begin
69
+ new_token = SecureRandom.uuid
70
+ end while channel_tokens.values.include?(new_token)
71
+
72
+ new_token
73
+ end
74
+
75
+ def send_token(connection)
76
+ options = {
77
+ :channel => @name,
78
+ :data => {:token => token},
79
+ :connection => connection
80
+ }
81
+ info 'sending token'
82
+ Event.new('websocket_rails.channel_token', options).trigger
83
+ end
84
+
85
+ def send_data(event)
86
+ return unless event.should_propagate?
87
+ if WebsocketRails.synchronize? && event.server_token.nil?
88
+ Synchronization.publish event
89
+ end
90
+
91
+ @subscribers.each do |subscriber|
92
+ subscriber.trigger event
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,55 @@
1
+ require 'redis-objects'
2
+
3
+ module WebsocketRails
4
+
5
+ class << self
6
+
7
+ def channel_manager
8
+ @channel_manager ||= ChannelManager.new
9
+ end
10
+
11
+ def [](channel)
12
+ channel_manager[channel]
13
+ end
14
+
15
+ def channel_tokens
16
+ channel_manager.channel_tokens
17
+ end
18
+
19
+ def filtered_channels
20
+ channel_manager.filtered_channels
21
+ end
22
+
23
+ end
24
+
25
+ class ChannelManager
26
+
27
+ attr_reader :channels, :filtered_channels
28
+
29
+ def initialize
30
+ @channels = {}.with_indifferent_access
31
+ @filtered_channels = {}.with_indifferent_access
32
+ end
33
+
34
+ def channel_tokens
35
+ @channel_tokens ||= begin
36
+ if WebsocketRails.synchronize?
37
+ ::Redis::HashKey.new('websocket_rails.channel_tokens', Synchronization.redis)
38
+ else
39
+ {}
40
+ end
41
+ end
42
+ end
43
+
44
+ def [](channel)
45
+ @channels[channel] ||= Channel.new channel
46
+ end
47
+
48
+ def unsubscribe(connection)
49
+ @channels.each do |channel_name, channel|
50
+ channel.unsubscribe(connection)
51
+ end
52
+ end
53
+
54
+ end
55
+ end