diff options
Diffstat (limited to 'actioncable')
19 files changed, 60 insertions, 41 deletions
diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index 2c84d3158f..4f17274a01 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -13,12 +13,12 @@ *Vladimir Dementyev* -* Buffer now writes to websocket connections, to avoid blocking threads +* Buffer now writes to WebSocket connections, to avoid blocking threads that could be doing more useful things. *Matthew Draper*, *Tinco Andringa* -* Protect against concurrent writes to a websocket connection from +* Protect against concurrent writes to a WebSocket connection from multiple threads; the underlying OS write is not always threadsafe. *Tinco Andringa* diff --git a/actioncable/MIT-LICENSE b/actioncable/MIT-LICENSE index 27a17cf41b..1a0e653b69 100644 --- a/actioncable/MIT-LICENSE +++ b/actioncable/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2016 Basecamp, LLC +Copyright (c) 2015-2017 Basecamp, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/actioncable/README.md b/actioncable/README.md index 8ad9aeb1f1..c55b7dc57b 100644 --- a/actioncable/README.md +++ b/actioncable/README.md @@ -51,7 +51,7 @@ module ApplicationCable self.current_user = find_verified_user end - protected + private def find_verified_user if current_user = User.find_by(id: cookies.signed[:user_id]) current_user @@ -536,6 +536,15 @@ cable.subscriptions.create 'AppearanceChannel', # normal channel code goes here... ``` +## Download and Installation + +The latest version of Action Cable can be installed with [RubyGems](#gem-usage), +or with [npm](#npm-usage). + +Source code can be downloaded as part of the Rails project on GitHub + +* https://github.com/rails/rails/tree/master/actioncable + ## License Action Cable is released under the MIT license: diff --git a/actioncable/actioncable.gemspec b/actioncable/actioncable.gemspec index e7f91d0e34..6d95f022fa 100644 --- a/actioncable/actioncable.gemspec +++ b/actioncable/actioncable.gemspec @@ -20,6 +20,6 @@ Gem::Specification.new do |s| s.add_dependency "actionpack", version - s.add_dependency "nio4r", "~> 1.2" + s.add_dependency "nio4r", "~> 2.0" s.add_dependency "websocket-driver", "~> 0.6.1" end diff --git a/actioncable/app/assets/javascripts/action_cable/connection.coffee b/actioncable/app/assets/javascripts/action_cable/connection.coffee index 29ad676290..7fd68cad2f 100644 --- a/actioncable/app/assets/javascripts/action_cable/connection.coffee +++ b/actioncable/app/assets/javascripts/action_cable/connection.coffee @@ -23,7 +23,7 @@ class ActionCable.Connection open: => if @isActive() ActionCable.log("Attempted to open WebSocket, but existing socket is #{@getState()}") - throw new Error("Existing connection must be closed before opening") + false else ActionCable.log("Opening WebSocket, current state is #{@getState()}, subprotocols: #{protocols}") @uninstallEventHandlers() if @webSocket? diff --git a/actioncable/lib/action_cable.rb b/actioncable/lib/action_cable.rb index d353716636..c2d3550acb 100644 --- a/actioncable/lib/action_cable.rb +++ b/actioncable/lib/action_cable.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2015-2016 Basecamp, LLC +# Copyright (c) 2015-2017 Basecamp, LLC # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb index a866044f95..6739a62ba0 100644 --- a/actioncable/lib/action_cable/channel/base.rb +++ b/actioncable/lib/action_cable/channel/base.rb @@ -122,16 +122,16 @@ module ActionCable end end - protected + private # action_methods are cached and there is sometimes need to refresh # them. ::clear_action_methods! allows you to do that, so next time # you run action_methods, they will be recalculated. - def clear_action_methods! + def clear_action_methods! # :doc: @action_methods = nil end # Refresh the cached action_methods when a new action_method is added. - def method_added(name) + def method_added(name) # :doc: super clear_action_methods! end @@ -189,22 +189,22 @@ module ActionCable end end - protected + private # Called once a consumer has become a subscriber of the channel. Usually the place to setup any streams # you want this channel to be sending to the subscriber. - def subscribed + def subscribed # :doc: # Override in subclasses end # Called once a consumer has cut its cable connection. Can be used for cleaning up connections or marking # users as offline or the like. - def unsubscribed + def unsubscribed # :doc: # Override in subclasses end # Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with # the proper channel identifier marked as the recipient. - def transmit(data, via: nil) + def transmit(data, via: nil) # :doc: logger.info "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via } payload = { channel_class: self.class.name, data: data, via: via } @@ -213,33 +213,32 @@ module ActionCable end end - def ensure_confirmation_sent + def ensure_confirmation_sent # :doc: return if subscription_rejected? @defer_subscription_confirmation_counter.decrement transmit_subscription_confirmation unless defer_subscription_confirmation? end - def defer_subscription_confirmation! + def defer_subscription_confirmation! # :doc: @defer_subscription_confirmation_counter.increment end - def defer_subscription_confirmation? + def defer_subscription_confirmation? # :doc: @defer_subscription_confirmation_counter.value > 0 end - def subscription_confirmation_sent? + def subscription_confirmation_sent? # :doc: @subscription_confirmation_sent end - def reject + def reject # :doc: @reject_subscription = true end - def subscription_rejected? + def subscription_rejected? # :doc: @reject_subscription end - private def delegate_connection_identifiers connection.identifiers.each do |identifier| define_singleton_method(identifier) do diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb index e1da126d64..0a517a532d 100644 --- a/actioncable/lib/action_cable/connection/base.rb +++ b/actioncable/lib/action_cable/connection/base.rb @@ -22,7 +22,7 @@ module ActionCable # # Any cleanup work needed when the cable connection is cut. # end # - # protected + # private # def find_verified_user # User.find_by_identity(cookies.signed[:identity_id]) || # reject_unauthorized_connection @@ -133,9 +133,15 @@ module ActionCable send_async :handle_close end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected + attr_reader :websocket + attr_reader :message_buffer + + private # The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc. - def request + def request # :doc: @request ||= begin environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application ActionDispatch::Request.new(environment || env) @@ -143,14 +149,10 @@ module ActionCable end # The cookies of the request that initiated the WebSocket connection. Useful for performing authorization checks. - def cookies + def cookies # :doc: request.cookie_jar end - attr_reader :websocket - attr_reader :message_buffer - - private def encode(cable_message) @coder.encode cable_message end diff --git a/actioncable/lib/action_cable/connection/message_buffer.rb b/actioncable/lib/action_cable/connection/message_buffer.rb index 6a80770cae..4ccd322644 100644 --- a/actioncable/lib/action_cable/connection/message_buffer.rb +++ b/actioncable/lib/action_cable/connection/message_buffer.rb @@ -28,6 +28,8 @@ module ActionCable receive_buffered_messages end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :connection attr_reader :buffered_messages diff --git a/actioncable/lib/action_cable/connection/subscriptions.rb b/actioncable/lib/action_cable/connection/subscriptions.rb index 00511aead5..abf42c99d5 100644 --- a/actioncable/lib/action_cable/connection/subscriptions.rb +++ b/actioncable/lib/action_cable/connection/subscriptions.rb @@ -61,6 +61,8 @@ module ActionCable subscriptions.each { |id, channel| remove_subscription(channel) } end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :connection, :subscriptions diff --git a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb index 41afa9680a..aef549aa86 100644 --- a/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb +++ b/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb @@ -31,8 +31,8 @@ module ActionCable end end - protected - def log(type, message) + private + def log(type, message) # :doc: tag(@logger) { @logger.send type, message } end end diff --git a/actioncable/lib/action_cable/connection/web_socket.rb b/actioncable/lib/action_cable/connection/web_socket.rb index 382141b89f..03eb6e2ea8 100644 --- a/actioncable/lib/action_cable/connection/web_socket.rb +++ b/actioncable/lib/action_cable/connection/web_socket.rb @@ -32,6 +32,8 @@ module ActionCable websocket.rack_response end + # TODO Change this to private once we've dropped Ruby 2.2 support. + # Workaround for Ruby 2.2 "private attribute?" warning. protected attr_reader :websocket end diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb index e23527b84e..63a26636a0 100644 --- a/actioncable/lib/action_cable/engine.rb +++ b/actioncable/lib/action_cable/engine.rb @@ -31,7 +31,7 @@ module ActionCable self.cable = Rails.application.config_for(config_path).with_indifferent_access end - previous_connection_class = self.connection_class + previous_connection_class = connection_class self.connection_class = -> { "ApplicationCable::Connection".safe_constantize || previous_connection_class.call } options.each { |k, v| send("#{k}=", v) } diff --git a/actioncable/lib/rails/generators/channel/USAGE b/actioncable/lib/rails/generators/channel/USAGE index 6249553c22..dd109fda80 100644 --- a/actioncable/lib/rails/generators/channel/USAGE +++ b/actioncable/lib/rails/generators/channel/USAGE @@ -3,7 +3,7 @@ Description: Stubs out a new cable channel for the server (in Ruby) and client (in CoffeeScript). Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments. - Note: Turn on the cable connection in app/assets/javascript/cable.js after generating any channels. + Note: Turn on the cable connection in app/assets/javascripts/cable.js after generating any channels. Example: ======== @@ -11,4 +11,4 @@ Example: creates a Chat channel class and CoffeeScript asset: Channel: app/channels/chat_channel.rb - Assets: app/assets/javascript/channels/chat.coffee + Assets: app/assets/javascripts/channels/chat.coffee diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb index 20d807c033..984b78bc9c 100644 --- a/actioncable/lib/rails/generators/channel/channel_generator.rb +++ b/actioncable/lib/rails/generators/channel/channel_generator.rb @@ -13,7 +13,7 @@ module Rails template "channel.rb", File.join("app/channels", class_path, "#{file_name}_channel.rb") if options[:assets] - if self.behavior == :invoke + if behavior == :invoke template "assets/cable.js", "app/assets/javascripts/cable.js" end @@ -23,14 +23,14 @@ module Rails generate_application_cable_files end - protected + private def file_name @_file_name ||= super.gsub(/_channel/i, "") end # FIXME: Change these files to symlinks once RubyGems 2.5.0 is required. def generate_application_cable_files - return if self.behavior != :invoke + return if behavior != :invoke files = [ "application_cable/channel.rb", diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb index a4dfcc06f0..f3d3bc0603 100644 --- a/actioncable/test/connection/identifier_test.rb +++ b/actioncable/test/connection/identifier_test.rb @@ -53,7 +53,7 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub server = TestServer.new server.stubs(:adapter).returns(stub_everything("adapter")) diff --git a/actioncable/test/connection/multiple_identifiers_test.rb b/actioncable/test/connection/multiple_identifiers_test.rb index 67e68355c5..ca1a08f4d6 100644 --- a/actioncable/test/connection/multiple_identifiers_test.rb +++ b/actioncable/test/connection/multiple_identifiers_test.rb @@ -19,7 +19,7 @@ class ActionCable::Connection::MultipleIdentifiersTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub server = TestServer.new server.stubs(:pubsub).returns(stub_everything("pubsub")) diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb index 87484765e5..6d53e249cb 100644 --- a/actioncable/test/connection/string_identifier_test.rb +++ b/actioncable/test/connection/string_identifier_test.rb @@ -21,7 +21,7 @@ class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase end end - protected + private def open_connection_with_stubbed_pubsub @server = TestServer.new @server.stubs(:pubsub).returns(stub_everything("pubsub")) diff --git a/actioncable/test/javascript/src/unit/consumer_test.coffee b/actioncable/test/javascript/src/unit/consumer_test.coffee index cf8a592255..41445274eb 100644 --- a/actioncable/test/javascript/src/unit/consumer_test.coffee +++ b/actioncable/test/javascript/src/unit/consumer_test.coffee @@ -2,8 +2,11 @@ {consumerTest} = ActionCable.TestHelpers module "ActionCable.Consumer", -> - consumerTest "#connect", connect: false, ({consumer, server, done}) -> - server.on("connection", done) + consumerTest "#connect", connect: false, ({consumer, server, assert, done}) -> + server.on "connection", -> + assert.equal consumer.connect(), false + done() + consumer.connect() consumerTest "#disconnect", ({consumer, client, done}) -> |