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
data/Rakefile ADDED
@@ -0,0 +1,72 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'rake'
4
+ begin
5
+ require 'bundler/setup'
6
+ rescue LoadError
7
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
8
+ end
9
+
10
+ begin
11
+ require 'rdoc/task'
12
+ rescue LoadError
13
+ require 'rdoc/rdoc'
14
+ require 'rake/rdoctask'
15
+ RDoc::Task = Rake::RDocTask
16
+ end
17
+
18
+ Bundler::GemHelper.install_tasks
19
+
20
+ RDoc::Task.new(:rdoc) do |rdoc|
21
+ rdoc.rdoc_dir = 'rdoc'
22
+ rdoc.title = 'websocket-rails'
23
+ rdoc.options << '--line-numbers'
24
+ rdoc.rdoc_files.include('README.md')
25
+ rdoc.rdoc_files.include('lib/**/*.rb')
26
+ end
27
+
28
+ require 'rspec/core/rake_task'
29
+
30
+ desc 'Default: run RSpec and Jasmine specs.'
31
+ task :default => :spec_and_jasmine
32
+
33
+ desc "Run specs"
34
+ RSpec::Core::RakeTask.new do |t|
35
+ t.pattern = "./spec/**/*_spec.rb"
36
+ end
37
+
38
+ desc "Run rspec and jasmine:ci at the same time"
39
+ task :spec_and_jasmine do
40
+ Rake::Task["spec"].execute
41
+ Rake::Task["jasmine:ci:headless"].execute
42
+ end
43
+
44
+ desc "Generate code coverage"
45
+ task :coverage do
46
+ ENV['COVERAGE'] = 'true'
47
+ Rake::Task["spec"].execute
48
+ `open coverage/index.html`
49
+ end
50
+
51
+ begin
52
+ require 'jasmine'
53
+ load 'jasmine/tasks/jasmine.rake'
54
+ rescue LoadError
55
+ task :jasmine do
56
+ abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
57
+ end
58
+ end
59
+
60
+ require 'headless'
61
+ require 'selenium-webdriver'
62
+
63
+ namespace :jasmine do
64
+ namespace :ci do
65
+ desc "Run Jasmine CI build headlessly"
66
+ task :headless do
67
+ ENV['DISPLAY'] = ':99.0'
68
+ puts "Running Jasmine Headlessly"
69
+ Rake::Task['jasmine:ci'].invoke
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # * Borrowed from the <tt>websocket-rack</tt>
3
+ # Gem as a temporary workaround for the
4
+ # timeout problem.
5
+ #
6
+ # Modified Thin command line interface script.
7
+ # This is fallback for WebSocket-Rack.
8
+ # Use it when you have EventMachine version < 1.0.0
9
+ # Rationale:
10
+ # Older versions of EM have bug that prevent to
11
+ # clearing connection inactivity once it's set.
12
+ # This one will set connection timeout to 0 at
13
+ # default, so there will be no need to overwrite it.
14
+ # Be aware that this will also change inactivity
15
+ # timeout for "normal" connection, so it will be
16
+ # easy to make DoS attack.
17
+
18
+ require 'rubygems'
19
+ require 'thin'
20
+
21
+ puts <<END
22
+ *** Deprecation Notice***
23
+
24
+ The thin-socketrails executable is now deprecated and
25
+ will be removed in the next release.
26
+
27
+ You may use the regular thin executable to launch the
28
+ server now.
29
+
30
+ Other EventMachine based web servers should now be
31
+ supported but have not yet been tested.
32
+
33
+ *************************
34
+ END
35
+
36
+ if EM::VERSION < "1.0.0"
37
+ begin
38
+ old_verbose, $VERBOSE = $VERBOSE, nil
39
+ ::Thin::Server.const_set 'DEFAULT_TIMEOUT', 0
40
+ ensure
41
+ $VERBOSE = old_verbose
42
+ end
43
+ end
44
+
45
+ Thin::Runner.new(ARGV).run!
@@ -0,0 +1,45 @@
1
+ ###
2
+ Abstract Interface for the WebSocketRails client.
3
+ ###
4
+ class WebSocketRails.AbstractConnection
5
+
6
+ constructor: (url, @dispatcher) ->
7
+ @message_queue = []
8
+
9
+ close: ->
10
+
11
+ trigger: (event) ->
12
+ if @dispatcher.state != 'connected'
13
+ @message_queue.push event
14
+ else
15
+ @send_event event
16
+
17
+ send_event: (event) ->
18
+ # Events queued before connecting do not have the correct
19
+ # connection_id set yet. We need to update it before dispatching.
20
+ event.connection_id = @connection_id if @connection_id?
21
+
22
+ # ...
23
+
24
+ on_close: (event) ->
25
+ if @dispatcher && @dispatcher._conn == @
26
+ close_event = new WebSocketRails.Event(['connection_closed', event])
27
+ @dispatcher.state = 'disconnected'
28
+ @dispatcher.dispatch close_event
29
+
30
+ on_error: (event) ->
31
+ if @dispatcher && @dispatcher._conn == @
32
+ error_event = new WebSocketRails.Event(['connection_error', event])
33
+ @dispatcher.state = 'disconnected'
34
+ @dispatcher.dispatch error_event
35
+
36
+ on_message: (event_data) ->
37
+ if @dispatcher && @dispatcher._conn == @
38
+ @dispatcher.new_message event_data
39
+
40
+ setConnectionId: (@connection_id) ->
41
+
42
+ flush_queue: ->
43
+ for event in @message_queue
44
+ @trigger event
45
+ @message_queue = []
@@ -0,0 +1,70 @@
1
+ ###
2
+ The channel object is returned when you subscribe to a channel.
3
+
4
+ For instance:
5
+ var dispatcher = new WebSocketRails('localhost:3000/websocket');
6
+ var awesome_channel = dispatcher.subscribe('awesome_channel');
7
+ awesome_channel.bind('event', function(data) { console.log('channel event!'); });
8
+ awesome_channel.trigger('awesome_event', awesome_object);
9
+
10
+ If you want to unbind an event, you can use the unbind function :
11
+ awesome_channel.unbind('event')
12
+ ###
13
+ class WebSocketRails.Channel
14
+
15
+ constructor: (@name, @_dispatcher, @is_private = false, @on_success, @on_failure) ->
16
+ @_callbacks = {}
17
+ @_token = undefined
18
+ @_queue = []
19
+ if @is_private
20
+ event_name = 'websocket_rails.subscribe_private'
21
+ else
22
+ event_name = 'websocket_rails.subscribe'
23
+
24
+ @connection_id = @_dispatcher._conn?.connection_id
25
+ event = new WebSocketRails.Event( [event_name, {data: {channel: @name}}, @connection_id], @_success_launcher, @_failure_launcher)
26
+ @_dispatcher.trigger_event event
27
+
28
+ destroy: ->
29
+ if @connection_id == @_dispatcher._conn?.connection_id
30
+ event_name = 'websocket_rails.unsubscribe'
31
+ event = new WebSocketRails.Event( [event_name, {data: {channel: @name}}, @connection_id] )
32
+ @_dispatcher.trigger_event event
33
+ @_callbacks = {}
34
+
35
+ bind: (event_name, callback) ->
36
+ @_callbacks[event_name] ?= []
37
+ @_callbacks[event_name].push callback
38
+
39
+ unbind: (event_name) ->
40
+ delete @_callbacks[event_name]
41
+
42
+ trigger: (event_name, message) ->
43
+ event = new WebSocketRails.Event( [event_name, {channel: @name, data: message, token: @_token}, @connection_id] )
44
+ if !@_token
45
+ @_queue.push event
46
+ else
47
+ @_dispatcher.trigger_event event
48
+
49
+ dispatch: (event_name, message) ->
50
+ if event_name == 'websocket_rails.channel_token'
51
+ @connection_id = @_dispatcher._conn?.connection_id
52
+ @_token = message['token']
53
+ @flush_queue()
54
+ else
55
+ return unless @_callbacks[event_name]?
56
+ for callback in @_callbacks[event_name]
57
+ callback message
58
+
59
+ # using this method because @on_success will not be defined when the constructor is executed
60
+ _success_launcher: (data) =>
61
+ @on_success(data) if @on_success?
62
+
63
+ # using this method because @on_failure will not be defined when the constructor is executed
64
+ _failure_launcher: (data) =>
65
+ @on_failure(data) if @on_failure?
66
+
67
+ flush_queue: ->
68
+ for event in @_queue
69
+ @_dispatcher.trigger_event event
70
+ @_queue = []
@@ -0,0 +1,42 @@
1
+ ###
2
+ The Event object stores all the relevant event information.
3
+ ###
4
+
5
+ class WebSocketRails.Event
6
+
7
+ constructor: (data, @success_callback, @failure_callback) ->
8
+ @name = data[0]
9
+ attr = data[1]
10
+ if attr?
11
+ @id = if attr['id']? then attr['id'] else (((1+Math.random())*0x10000)|0)
12
+ @channel = if attr.channel? then attr.channel
13
+ @data = if attr.data? then attr.data else attr
14
+ @token = if attr.token? then attr.token
15
+ @connection_id = data[2]
16
+ if attr.success?
17
+ @result = true
18
+ @success = attr.success
19
+
20
+ is_channel: ->
21
+ @channel?
22
+
23
+ is_result: ->
24
+ typeof @result != 'undefined'
25
+
26
+ is_ping: ->
27
+ @name == 'websocket_rails.ping'
28
+
29
+ serialize: ->
30
+ JSON.stringify [@name, @attributes()]
31
+
32
+ attributes: ->
33
+ id: @id,
34
+ channel: @channel,
35
+ data: @data
36
+ token: @token
37
+
38
+ run_callbacks: (@success, @result) ->
39
+ if @success == true
40
+ @success_callback?(@result)
41
+ else
42
+ @failure_callback?(@result)
@@ -0,0 +1,66 @@
1
+ ###
2
+ HTTP Interface for the WebSocketRails client.
3
+ ###
4
+ class WebSocketRails.HttpConnection extends WebSocketRails.AbstractConnection
5
+ connection_type: 'http'
6
+
7
+ _httpFactories: -> [
8
+ -> new XDomainRequest(),
9
+ -> new XMLHttpRequest(),
10
+ -> new ActiveXObject("Msxml2.XMLHTTP"),
11
+ -> new ActiveXObject("Msxml3.XMLHTTP"),
12
+ -> new ActiveXObject("Microsoft.XMLHTTP")
13
+ ]
14
+
15
+ constructor: (url, @dispatcher) ->
16
+ super
17
+ @_url = "http://#{url}"
18
+ @_conn = @_createXMLHttpObject()
19
+ @last_pos = 0
20
+ try
21
+ @_conn.onreadystatechange = => @_parse_stream()
22
+ @_conn.addEventListener("load", @on_close, false)
23
+ catch e
24
+ @_conn.onprogress = => @_parse_stream()
25
+ @_conn.onload = @on_close
26
+ # set this as 3 always for parse_stream as the object does not have this property at all
27
+ @_conn.readyState = 3
28
+ @_conn.open "GET", @_url, true
29
+ @_conn.send()
30
+
31
+ close: ->
32
+ @_conn.abort()
33
+
34
+ send_event: (event) ->
35
+ super
36
+ @_post_data event.serialize()
37
+
38
+ _post_data: (payload) ->
39
+ $.ajax @_url,
40
+ type: 'POST'
41
+ data:
42
+ client_id: @connection_id
43
+ data: payload
44
+ success: ->
45
+
46
+ _createXMLHttpObject: ->
47
+ xmlhttp = false
48
+ factories = @_httpFactories()
49
+ for factory in factories
50
+ try
51
+ xmlhttp = factory()
52
+ catch e
53
+ continue
54
+ break
55
+ xmlhttp
56
+
57
+ _parse_stream: ->
58
+ if @_conn.readyState == 3
59
+ data = @_conn.responseText.substring @last_pos
60
+ @last_pos = @_conn.responseText.length
61
+ data = data.replace( /\]\]\[\[/g, "],[" )
62
+ try
63
+ event_data = JSON.parse data
64
+ @on_message(event_data)
65
+ catch e
66
+ # just ignore if it cannot be parsed, probably whitespace
@@ -0,0 +1,6 @@
1
+ //= require ./websocket_rails
2
+ //= require ./event
3
+ //= require ./abstract_connection
4
+ //= require ./http_connection
5
+ //= require ./websocket_connection
6
+ //= require ./channel
@@ -0,0 +1,29 @@
1
+ ###
2
+ WebSocket Interface for the WebSocketRails client.
3
+ ###
4
+ class WebSocketRails.WebSocketConnection extends WebSocketRails.AbstractConnection
5
+ connection_type: 'websocket'
6
+
7
+ constructor: (@url, @dispatcher) ->
8
+ super
9
+ if @url.match(/^wss?:\/\//)
10
+ console.log "WARNING: Using connection urls with protocol specified is deprecated"
11
+ else if window.location.protocol == 'https:'
12
+ @url = "wss://#{@url}"
13
+ else
14
+ @url = "ws://#{@url}"
15
+ @_conn = new WebSocket(@url)
16
+ @_conn.onmessage = (event) =>
17
+ event_data = JSON.parse event.data
18
+ @on_message(event_data)
19
+ @_conn.onclose = (event) =>
20
+ @on_close(event)
21
+ @_conn.onerror = (event) =>
22
+ @on_error(event)
23
+
24
+ close: ->
25
+ @_conn.close()
26
+
27
+ send_event: (event) ->
28
+ super
29
+ @_conn.send event.serialize()
@@ -0,0 +1,158 @@
1
+ ###
2
+ WebsocketRails JavaScript Client
3
+
4
+ Setting up the dispatcher:
5
+ var dispatcher = new WebSocketRails('localhost:3000/websocket');
6
+ dispatcher.on_open = function() {
7
+ // trigger a server event immediately after opening connection
8
+ dispatcher.trigger('new_user',{user_name: 'guest'});
9
+ })
10
+
11
+ Triggering a new event on the server
12
+ dispatcherer.trigger('event_name',object_to_be_serialized_to_json);
13
+
14
+ Listening for new events from the server
15
+ dispatcher.bind('event_name', function(data) {
16
+ console.log(data.user_name);
17
+ });
18
+
19
+ Stop listening for new events from the server
20
+ dispatcher.unbind('event')
21
+ ###
22
+ class @WebSocketRails
23
+ constructor: (@url, @use_websockets = true) ->
24
+ @callbacks = {}
25
+ @channels = {}
26
+ @queue = {}
27
+
28
+ @connect()
29
+
30
+ connect: ->
31
+ @state = 'connecting'
32
+
33
+ unless @supports_websockets() and @use_websockets
34
+ @_conn = new WebSocketRails.HttpConnection @url, @
35
+ else
36
+ @_conn = new WebSocketRails.WebSocketConnection @url, @
37
+
38
+ @_conn.new_message = @new_message
39
+
40
+ disconnect: ->
41
+ if @_conn
42
+ @_conn.close()
43
+ delete @_conn._conn
44
+ delete @_conn
45
+
46
+ @state = 'disconnected'
47
+
48
+ # Reconnects the whole connection,
49
+ # keeping the messages queue and its' connected channels.
50
+ #
51
+ # After successfull connection, this will:
52
+ # - reconnect to all channels, that were active while disconnecting
53
+ # - resend all events from which we haven't received any response yet
54
+ reconnect: =>
55
+ old_connection_id = @_conn?.connection_id
56
+
57
+ @disconnect()
58
+ @connect()
59
+
60
+ # Resend all unfinished events from the previous connection.
61
+ for id, event of @queue
62
+ if event.connection_id == old_connection_id && !event.is_result()
63
+ @trigger_event event
64
+
65
+ @reconnect_channels()
66
+
67
+ new_message: (data) =>
68
+ for socket_message in data
69
+ event = new WebSocketRails.Event( socket_message )
70
+ if event.is_result()
71
+ @queue[event.id]?.run_callbacks(event.success, event.data)
72
+ delete @queue[event.id]
73
+ else if event.is_channel()
74
+ @dispatch_channel event
75
+ else if event.is_ping()
76
+ @pong()
77
+ else
78
+ @dispatch event
79
+
80
+ if @state == 'connecting' and event.name == 'client_connected'
81
+ @connection_established event.data
82
+
83
+ connection_established: (data) =>
84
+ @state = 'connected'
85
+ @_conn.setConnectionId(data.connection_id)
86
+ @_conn.flush_queue()
87
+ if @on_open?
88
+ @on_open(data)
89
+
90
+ bind: (event_name, callback) =>
91
+ @callbacks[event_name] ?= []
92
+ @callbacks[event_name].push callback
93
+
94
+ unbind: (event_name) =>
95
+ delete @callbacks[event_name]
96
+
97
+ trigger: (event_name, data, success_callback, failure_callback) =>
98
+ event = new WebSocketRails.Event( [event_name, data, @_conn?.connection_id], success_callback, failure_callback )
99
+ @trigger_event event
100
+
101
+ trigger_event: (event) =>
102
+ @queue[event.id] ?= event # Prevent replacing an event that has callbacks stored
103
+ @_conn.trigger event if @_conn
104
+ event
105
+
106
+ dispatch: (event) =>
107
+ return unless @callbacks[event.name]?
108
+ for callback in @callbacks[event.name]
109
+ callback event.data
110
+
111
+ subscribe: (channel_name, success_callback, failure_callback) =>
112
+ unless @channels[channel_name]?
113
+ channel = new WebSocketRails.Channel channel_name, @, false, success_callback, failure_callback
114
+ @channels[channel_name] = channel
115
+ channel
116
+ else
117
+ @channels[channel_name]
118
+
119
+ subscribe_private: (channel_name, success_callback, failure_callback) =>
120
+ unless @channels[channel_name]?
121
+ channel = new WebSocketRails.Channel channel_name, @, true, success_callback, failure_callback
122
+ @channels[channel_name] = channel
123
+ channel
124
+ else
125
+ @channels[channel_name]
126
+
127
+ unsubscribe: (channel_name) =>
128
+ return unless @channels[channel_name]?
129
+ @channels[channel_name].destroy()
130
+ delete @channels[channel_name]
131
+
132
+ dispatch_channel: (event) =>
133
+ return unless @channels[event.channel]?
134
+ @channels[event.channel].dispatch event.name, event.data
135
+
136
+ supports_websockets: =>
137
+ (typeof(WebSocket) == "function" or typeof(WebSocket) == "object")
138
+
139
+ pong: =>
140
+ pong = new WebSocketRails.Event( ['websocket_rails.pong', {}, @_conn?.connection_id] )
141
+ @_conn.trigger pong
142
+
143
+ connection_stale: =>
144
+ @state != 'connected'
145
+
146
+ # Destroy and resubscribe to all existing @channels.
147
+ reconnect_channels: ->
148
+ for name, channel of @channels
149
+ callbacks = channel._callbacks
150
+ channel.destroy()
151
+ delete @channels[name]
152
+
153
+ channel = if channel.is_private
154
+ @subscribe_private name
155
+ else
156
+ @subscribe name
157
+ channel._callbacks = callbacks
158
+ channel