hkroger-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 +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