actioncable 7.0.8.7 → 7.2.2.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -180
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +4 -4
  5. data/app/assets/javascripts/action_cable.js +30 -9
  6. data/app/assets/javascripts/actioncable.esm.js +30 -9
  7. data/app/assets/javascripts/actioncable.js +30 -9
  8. data/lib/action_cable/channel/base.rb +114 -90
  9. data/lib/action_cable/channel/broadcasting.rb +25 -16
  10. data/lib/action_cable/channel/callbacks.rb +39 -0
  11. data/lib/action_cable/channel/naming.rb +10 -7
  12. data/lib/action_cable/channel/periodic_timers.rb +7 -7
  13. data/lib/action_cable/channel/streams.rb +77 -62
  14. data/lib/action_cable/channel/test_case.rb +117 -86
  15. data/lib/action_cable/connection/authorization.rb +4 -1
  16. data/lib/action_cable/connection/base.rb +70 -42
  17. data/lib/action_cable/connection/callbacks.rb +57 -0
  18. data/lib/action_cable/connection/client_socket.rb +3 -1
  19. data/lib/action_cable/connection/identification.rb +9 -5
  20. data/lib/action_cable/connection/internal_channel.rb +7 -2
  21. data/lib/action_cable/connection/message_buffer.rb +4 -1
  22. data/lib/action_cable/connection/stream.rb +2 -2
  23. data/lib/action_cable/connection/stream_event_loop.rb +4 -4
  24. data/lib/action_cable/connection/subscriptions.rb +7 -2
  25. data/lib/action_cable/connection/tagged_logger_proxy.rb +12 -7
  26. data/lib/action_cable/connection/test_case.rb +67 -55
  27. data/lib/action_cable/connection/web_socket.rb +11 -7
  28. data/lib/action_cable/deprecator.rb +9 -0
  29. data/lib/action_cable/engine.rb +18 -8
  30. data/lib/action_cable/gem_version.rb +6 -4
  31. data/lib/action_cable/helpers/action_cable_helper.rb +21 -19
  32. data/lib/action_cable/remote_connections.rb +25 -13
  33. data/lib/action_cable/server/base.rb +29 -14
  34. data/lib/action_cable/server/broadcasting.rb +24 -16
  35. data/lib/action_cable/server/configuration.rb +27 -14
  36. data/lib/action_cable/server/connections.rb +13 -5
  37. data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
  38. data/lib/action_cable/server/worker.rb +4 -3
  39. data/lib/action_cable/subscription_adapter/async.rb +1 -1
  40. data/lib/action_cable/subscription_adapter/base.rb +2 -0
  41. data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
  42. data/lib/action_cable/subscription_adapter/inline.rb +2 -0
  43. data/lib/action_cable/subscription_adapter/postgresql.rb +4 -3
  44. data/lib/action_cable/subscription_adapter/redis.rb +7 -7
  45. data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
  46. data/lib/action_cable/subscription_adapter/test.rb +6 -5
  47. data/lib/action_cable/test_case.rb +2 -0
  48. data/lib/action_cable/test_helper.rb +89 -59
  49. data/lib/action_cable/version.rb +3 -1
  50. data/lib/action_cable.rb +30 -12
  51. data/lib/rails/generators/channel/USAGE +14 -8
  52. data/lib/rails/generators/channel/channel_generator.rb +23 -7
  53. data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
  54. metadata +27 -15
  55. data/lib/action_cable/channel.rb +0 -17
  56. data/lib/action_cable/connection.rb +0 -22
  57. data/lib/action_cable/server.rb +0 -16
  58. data/lib/action_cable/subscription_adapter.rb +0 -12
  59. /data/lib/rails/generators/channel/templates/application_cable/{channel.rb → channel.rb.tt} +0 -0
  60. /data/lib/rails/generators/channel/templates/application_cable/{connection.rb → connection.rb.tt} +0 -0
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "websocket/driver"
4
6
 
5
7
  module ActionCable
6
8
  module Connection
9
+ # # Action Cable Connection WebSocket
10
+ #
7
11
  # Wrap the real socket to minimize the externally-presented API
8
12
  class WebSocket # :nodoc:
9
13
  def initialize(env, event_target, event_loop, protocols: ActionCable::INTERNAL[:protocols])
@@ -15,23 +19,23 @@ module ActionCable
15
19
  end
16
20
 
17
21
  def alive?
18
- websocket && websocket.alive?
22
+ websocket&.alive?
19
23
  end
20
24
 
21
- def transmit(data)
22
- websocket.transmit data
25
+ def transmit(...)
26
+ websocket&.transmit(...)
23
27
  end
24
28
 
25
- def close
26
- websocket.close
29
+ def close(...)
30
+ websocket&.close(...)
27
31
  end
28
32
 
29
33
  def protocol
30
- websocket.protocol
34
+ websocket&.protocol
31
35
  end
32
36
 
33
37
  def rack_response
34
- websocket.rack_response
38
+ websocket&.rack_response
35
39
  end
36
40
 
37
41
  private
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionCable
6
+ def self.deprecator # :nodoc:
7
+ @deprecator ||= ActiveSupport::Deprecation.new
8
+ end
9
+ end
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "rails"
4
6
  require "action_cable"
5
- require "action_cable/helpers/action_cable_helper"
6
7
  require "active_support/core_ext/hash/indifferent_access"
7
8
 
8
9
  module ActionCable
@@ -11,7 +12,9 @@ module ActionCable
11
12
  config.action_cable.mount_path = ActionCable::INTERNAL[:default_mount_path]
12
13
  config.action_cable.precompile_assets = true
13
14
 
14
- config.eager_load_namespaces << ActionCable
15
+ initializer "action_cable.deprecator", before: :load_environment_config do |app|
16
+ app.deprecators[:action_cable] = ActionCable.deprecator
17
+ end
15
18
 
16
19
  initializer "action_cable.helpers" do
17
20
  ActiveSupport.on_load(:action_view) do
@@ -23,6 +26,12 @@ module ActionCable
23
26
  ActiveSupport.on_load(:action_cable) { self.logger ||= ::Rails.logger }
24
27
  end
25
28
 
29
+ initializer "action_cable.health_check_application" do
30
+ ActiveSupport.on_load(:action_cable) {
31
+ self.health_check_application = ->(env) { Rails::HealthController.action(:show).call(env) }
32
+ }
33
+ end
34
+
26
35
  initializer "action_cable.asset" do
27
36
  config.after_initialize do |app|
28
37
  if app.config.respond_to?(:assets) && app.config.action_cable.precompile_assets
@@ -39,11 +48,12 @@ module ActionCable
39
48
 
40
49
  ActiveSupport.on_load(:action_cable) do
41
50
  if (config_path = Pathname.new(app.config.paths["config/cable"].first)).exist?
42
- self.cable = Rails.application.config_for(config_path).to_h.with_indifferent_access
51
+ self.cable = app.config_for(config_path).to_h.with_indifferent_access
43
52
  end
44
53
 
45
54
  previous_connection_class = connection_class
46
55
  self.connection_class = -> { "ApplicationCable::Connection".safe_constantize || previous_connection_class.call }
56
+ self.filter_parameters += app.config.filter_parameters
47
57
 
48
58
  options.each { |k, v| send("#{k}=", v) }
49
59
  end
@@ -63,10 +73,10 @@ module ActionCable
63
73
  initializer "action_cable.set_work_hooks" do |app|
64
74
  ActiveSupport.on_load(:action_cable) do
65
75
  ActionCable::Server::Worker.set_callback :work, :around, prepend: true do |_, inner|
66
- app.executor.wrap do
67
- # If we took a while to get the lock, we may have been halted
68
- # in the meantime. As we haven't started doing any real work
69
- # yet, we should pretend that we never made it off the queue.
76
+ app.executor.wrap(source: "application.action_cable") do
77
+ # If we took a while to get the lock, we may have been halted in the meantime.
78
+ # As we haven't started doing any real work yet, we should pretend that we never
79
+ # made it off the queue.
70
80
  unless stopping?
71
81
  inner.call
72
82
  end
@@ -74,7 +84,7 @@ module ActionCable
74
84
  end
75
85
 
76
86
  wrap = lambda do |_, inner|
77
- app.executor.wrap(&inner)
87
+ app.executor.wrap(source: "application.action_cable", &inner)
78
88
  end
79
89
  ActionCable::Channel::Base.set_callback :subscribe, :around, prepend: true, &wrap
80
90
  ActionCable::Channel::Base.set_callback :unsubscribe, :around, prepend: true, &wrap
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
- # Returns the currently loaded version of Action Cable as a <tt>Gem::Version</tt>.
6
+ # Returns the currently loaded version of Action Cable as a `Gem::Version`.
5
7
  def self.gem_version
6
8
  Gem::Version.new VERSION::STRING
7
9
  end
8
10
 
9
11
  module VERSION
10
12
  MAJOR = 7
11
- MINOR = 0
12
- TINY = 8
13
- PRE = "7"
13
+ MINOR = 2
14
+ TINY = 2
15
+ PRE = "1"
14
16
 
15
17
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
18
  end
@@ -1,35 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Helpers
5
7
  module ActionCableHelper
6
- # Returns an "action-cable-url" meta tag with the value of the URL specified in your
7
- # configuration. Ensure this is above your JavaScript tag:
8
+ # Returns an "action-cable-url" meta tag with the value of the URL specified in
9
+ # your configuration. Ensure this is above your JavaScript tag:
8
10
  #
9
- # <head>
10
- # <%= action_cable_meta_tag %>
11
- # <%= javascript_include_tag 'application', 'data-turbo-track' => 'reload' %>
12
- # </head>
11
+ # <head>
12
+ # <%= action_cable_meta_tag %>
13
+ # <%= javascript_include_tag 'application', 'data-turbo-track' => 'reload' %>
14
+ # </head>
13
15
  #
14
- # This is then used by Action Cable to determine the URL of your WebSocket server.
15
- # Your JavaScript can then connect to the server without needing to specify the
16
- # URL directly:
16
+ # This is then used by Action Cable to determine the URL of your WebSocket
17
+ # server. Your JavaScript can then connect to the server without needing to
18
+ # specify the URL directly:
17
19
  #
18
- # import Cable from "@rails/actioncable"
19
- # window.Cable = Cable
20
- # window.App = {}
21
- # App.cable = Cable.createConsumer()
20
+ # import Cable from "@rails/actioncable"
21
+ # window.Cable = Cable
22
+ # window.App = {}
23
+ # App.cable = Cable.createConsumer()
22
24
  #
23
25
  # Make sure to specify the correct server location in each of your environment
24
26
  # config files:
25
27
  #
26
- # config.action_cable.mount_path = "/cable123"
27
- # <%= action_cable_meta_tag %> would render:
28
- # => <meta name="action-cable-url" content="/cable123" />
28
+ # config.action_cable.mount_path = "/cable123"
29
+ # <%= action_cable_meta_tag %> would render:
30
+ # => <meta name="action-cable-url" content="/cable123" />
29
31
  #
30
- # config.action_cable.url = "ws://actioncable.com"
31
- # <%= action_cable_meta_tag %> would render:
32
- # => <meta name="action-cable-url" content="ws://actioncable.com" />
32
+ # config.action_cable.url = "ws://actioncable.com"
33
+ # <%= action_cable_meta_tag %> would render:
34
+ # => <meta name="action-cable-url" content="ws://actioncable.com" />
33
35
  #
34
36
  def action_cable_meta_tag
35
37
  tag "meta", name: "action-cable-url", content: (
@@ -1,24 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/module/redefine_method"
4
6
 
5
7
  module ActionCable
8
+ # # Action Cable Remote Connections
9
+ #
6
10
  # If you need to disconnect a given connection, you can go through the
7
11
  # RemoteConnections. You can find the connections you're looking for by
8
12
  # searching for the identifier declared on the connection. For example:
9
13
  #
10
- # module ApplicationCable
11
- # class Connection < ActionCable::Connection::Base
12
- # identified_by :current_user
13
- # ....
14
+ # module ApplicationCable
15
+ # class Connection < ActionCable::Connection::Base
16
+ # identified_by :current_user
17
+ # ....
18
+ # end
14
19
  # end
15
- # end
16
20
  #
17
- # ActionCable.server.remote_connections.where(current_user: User.find(1)).disconnect
21
+ # ActionCable.server.remote_connections.where(current_user: User.find(1)).disconnect
22
+ #
23
+ # This will disconnect all the connections established for `User.find(1)`,
24
+ # across all servers running on all machines, because it uses the internal
25
+ # channel that all of these servers are subscribed to.
26
+ #
27
+ # By default, server sends a "disconnect" message with "reconnect" flag set to
28
+ # true. You can override it by specifying the `reconnect` option:
18
29
  #
19
- # This will disconnect all the connections established for
20
- # <tt>User.find(1)</tt>, across all servers running on all machines, because
21
- # it uses the internal channel that all of these servers are subscribed to.
30
+ # ActionCable.server.remote_connections.where(current_user: User.find(1)).disconnect(reconnect: false)
22
31
  class RemoteConnections
23
32
  attr_reader :server
24
33
 
@@ -31,8 +40,11 @@ module ActionCable
31
40
  end
32
41
 
33
42
  private
34
- # Represents a single remote connection found via <tt>ActionCable.server.remote_connections.where(*)</tt>.
35
- # Exists solely for the purpose of calling #disconnect on that connection.
43
+ # # Action Cable Remote Connection
44
+ #
45
+ # Represents a single remote connection found via
46
+ # `ActionCable.server.remote_connections.where(*)`. Exists solely for the
47
+ # purpose of calling #disconnect on that connection.
36
48
  class RemoteConnection
37
49
  class InvalidIdentifiersError < StandardError; end
38
50
 
@@ -44,8 +56,8 @@ module ActionCable
44
56
  end
45
57
 
46
58
  # Uses the internal channel to disconnect the connection.
47
- def disconnect
48
- server.broadcast internal_channel, { type: "disconnect" }
59
+ def disconnect(reconnect: true)
60
+ server.broadcast internal_channel, { type: "disconnect", reconnect: reconnect }
49
61
  end
50
62
 
51
63
  # Returns all the identifiers that were applied to this connection.
@@ -1,13 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "monitor"
4
6
 
5
7
  module ActionCable
6
8
  module Server
7
- # A singleton ActionCable::Server instance is available via ActionCable.server. It's used by the Rack process that starts the Action Cable server, but
8
- # is also used by the user to reach the RemoteConnections object, which is used for finding and disconnecting connections across all servers.
9
+ # # Action Cable Server Base
10
+ #
11
+ # A singleton ActionCable::Server instance is available via ActionCable.server.
12
+ # It's used by the Rack process that starts the Action Cable server, but is also
13
+ # used by the user to reach the RemoteConnections object, which is used for
14
+ # finding and disconnecting connections across all servers.
9
15
  #
10
- # Also, this is the server instance used for broadcasting. See Broadcasting for more information.
16
+ # Also, this is the server instance used for broadcasting. See Broadcasting for
17
+ # more information.
11
18
  class Base
12
19
  include ActionCable::Server::Broadcasting
13
20
  include ActionCable::Server::Connections
@@ -29,11 +36,13 @@ module ActionCable
29
36
 
30
37
  # Called by Rack to set up the server.
31
38
  def call(env)
39
+ return config.health_check_application.call(env) if env["PATH_INFO"] == config.health_check_path
32
40
  setup_heartbeat_timer
33
41
  config.connection_class.call.new(self, env).process
34
42
  end
35
43
 
36
- # Disconnect all the connections identified by +identifiers+ on this server or any others via RemoteConnections.
44
+ # Disconnect all the connections identified by `identifiers` on this server or
45
+ # any others via RemoteConnections.
37
46
  def disconnect(identifiers)
38
47
  remote_connections.where(identifiers).disconnect
39
48
  end
@@ -63,17 +72,22 @@ module ActionCable
63
72
  @event_loop || @mutex.synchronize { @event_loop ||= ActionCable::Connection::StreamEventLoop.new }
64
73
  end
65
74
 
66
- # The worker pool is where we run connection callbacks and channel actions. We do as little as possible on the server's main thread.
67
- # The worker pool is an executor service that's backed by a pool of threads working from a task queue. The thread pool size maxes out
68
- # at 4 worker threads by default. Tune the size yourself with <tt>config.action_cable.worker_pool_size</tt>.
75
+ # The worker pool is where we run connection callbacks and channel actions. We
76
+ # do as little as possible on the server's main thread. The worker pool is an
77
+ # executor service that's backed by a pool of threads working from a task queue.
78
+ # The thread pool size maxes out at 4 worker threads by default. Tune the size
79
+ # yourself with `config.action_cable.worker_pool_size`.
69
80
  #
70
- # Using Active Record, Redis, etc within your channel actions means you'll get a separate connection from each thread in the worker pool.
71
- # Plan your deployment accordingly: 5 servers each running 5 Puma workers each running an 8-thread worker pool means at least 200 database
72
- # connections.
81
+ # Using Active Record, Redis, etc within your channel actions means you'll get a
82
+ # separate connection from each thread in the worker pool. Plan your deployment
83
+ # accordingly: 5 servers each running 5 Puma workers each running an 8-thread
84
+ # worker pool means at least 200 database connections.
73
85
  #
74
- # Also, ensure that your database connection pool size is as least as large as your worker pool size. Otherwise, workers may oversubscribe
75
- # the database connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
76
- # database connection pool instead.
86
+ # Also, ensure that your database connection pool size is as least as large as
87
+ # your worker pool size. Otherwise, workers may oversubscribe the database
88
+ # connection pool and block while they wait for other workers to release their
89
+ # connections. Use a smaller worker pool or a larger database connection pool
90
+ # instead.
77
91
  def worker_pool
78
92
  @worker_pool || @mutex.synchronize { @worker_pool ||= ActionCable::Server::Worker.new(max_size: config.worker_pool_size) }
79
93
  end
@@ -83,7 +97,8 @@ module ActionCable
83
97
  @pubsub || @mutex.synchronize { @pubsub ||= config.pubsub_adapter.new(self) }
84
98
  end
85
99
 
86
- # All of the identifiers applied to the connection class associated with this server.
100
+ # All of the identifiers applied to the connection class associated with this
101
+ # server.
87
102
  def connection_identifiers
88
103
  config.connection_class.call.identifiers
89
104
  end
@@ -1,32 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Server
5
- # Broadcasting is how other parts of your application can send messages to a channel's subscribers. As explained in Channel, most of the time, these
6
- # broadcastings are streamed directly to the clients subscribed to the named broadcasting. Let's explain with a full-stack example:
7
+ # # Action Cable Server Broadcasting
8
+ #
9
+ # Broadcasting is how other parts of your application can send messages to a
10
+ # channel's subscribers. As explained in Channel, most of the time, these
11
+ # broadcastings are streamed directly to the clients subscribed to the named
12
+ # broadcasting. Let's explain with a full-stack example:
7
13
  #
8
- # class WebNotificationsChannel < ApplicationCable::Channel
9
- # def subscribed
10
- # stream_from "web_notifications_#{current_user.id}"
14
+ # class WebNotificationsChannel < ApplicationCable::Channel
15
+ # def subscribed
16
+ # stream_from "web_notifications_#{current_user.id}"
17
+ # end
11
18
  # end
12
- # end
13
19
  #
14
- # # Somewhere in your app this is called, perhaps from a NewCommentJob:
15
- # ActionCable.server.broadcast \
16
- # "web_notifications_1", { title: "New things!", body: "All that's fit for print" }
20
+ # # Somewhere in your app this is called, perhaps from a NewCommentJob:
21
+ # ActionCable.server.broadcast \
22
+ # "web_notifications_1", { title: "New things!", body: "All that's fit for print" }
17
23
  #
18
- # # Client-side CoffeeScript, which assumes you've already requested the right to send web notifications:
19
- # App.cable.subscriptions.create "WebNotificationsChannel",
20
- # received: (data) ->
21
- # new Notification data['title'], body: data['body']
24
+ # # Client-side CoffeeScript, which assumes you've already requested the right to send web notifications:
25
+ # App.cable.subscriptions.create "WebNotificationsChannel",
26
+ # received: (data) ->
27
+ # new Notification data['title'], body: data['body']
22
28
  module Broadcasting
23
- # Broadcast a hash directly to a named <tt>broadcasting</tt>. This will later be JSON encoded.
29
+ # Broadcast a hash directly to a named `broadcasting`. This will later be JSON
30
+ # encoded.
24
31
  def broadcast(broadcasting, message, coder: ActiveSupport::JSON)
25
32
  broadcaster_for(broadcasting, coder: coder).broadcast(message)
26
33
  end
27
34
 
28
- # Returns a broadcaster for a named <tt>broadcasting</tt> that can be reused. Useful when you have an object that
29
- # may need multiple spots to transmit to a specific broadcasting over and over.
35
+ # Returns a broadcaster for a named `broadcasting` that can be reused. Useful
36
+ # when you have an object that may need multiple spots to transmit to a specific
37
+ # broadcasting over and over.
30
38
  def broadcaster_for(broadcasting, coder: ActiveSupport::JSON)
31
39
  Broadcaster.new(self, String(broadcasting), coder: coder)
32
40
  end
@@ -1,15 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
5
+ require "rack"
6
+
3
7
  module ActionCable
4
8
  module Server
5
- # An instance of this configuration object is available via ActionCable.server.config, which allows you to tweak Action Cable configuration
6
- # in a Rails config initializer.
9
+ # # Action Cable Server Configuration
10
+ #
11
+ # An instance of this configuration object is available via
12
+ # ActionCable.server.config, which allows you to tweak Action Cable
13
+ # configuration in a Rails config initializer.
7
14
  class Configuration
8
15
  attr_accessor :logger, :log_tags
9
16
  attr_accessor :connection_class, :worker_pool_size
10
- attr_accessor :disable_request_forgery_protection, :allowed_request_origins, :allow_same_origin_as_host
17
+ attr_accessor :disable_request_forgery_protection, :allowed_request_origins, :allow_same_origin_as_host, :filter_parameters
11
18
  attr_accessor :cable, :url, :mount_path
12
19
  attr_accessor :precompile_assets
20
+ attr_accessor :health_check_path, :health_check_application
13
21
 
14
22
  def initialize
15
23
  @log_tags = []
@@ -19,30 +27,35 @@ module ActionCable
19
27
 
20
28
  @disable_request_forgery_protection = false
21
29
  @allow_same_origin_as_host = true
30
+ @filter_parameters = []
31
+
32
+ @health_check_application = ->(env) {
33
+ [200, { Rack::CONTENT_TYPE => "text/html", "date" => Time.now.httpdate }, []]
34
+ }
22
35
  end
23
36
 
24
- # Returns constant of subscription adapter specified in config/cable.yml.
25
- # If the adapter cannot be found, this will default to the Redis adapter.
26
- # Also makes sure proper dependencies are required.
37
+ # Returns constant of subscription adapter specified in config/cable.yml. If the
38
+ # adapter cannot be found, this will default to the Redis adapter. Also makes
39
+ # sure proper dependencies are required.
27
40
  def pubsub_adapter
28
41
  adapter = (cable.fetch("adapter") { "redis" })
29
42
 
30
43
  # Require the adapter itself and give useful feedback about
31
- # 1. Missing adapter gems and
32
- # 2. Adapter gems' missing dependencies.
44
+ # 1. Missing adapter gems and
45
+ # 2. Adapter gems' missing dependencies.
33
46
  path_to_adapter = "action_cable/subscription_adapter/#{adapter}"
34
47
  begin
35
48
  require path_to_adapter
36
49
  rescue LoadError => e
37
- # We couldn't require the adapter itself. Raise an exception that
38
- # points out config typos and missing gems.
50
+ # We couldn't require the adapter itself. Raise an exception that points out
51
+ # config typos and missing gems.
39
52
  if e.path == path_to_adapter
40
- # We can assume that a non-builtin adapter was specified, so it's
41
- # either misspelled or missing from Gemfile.
53
+ # We can assume that a non-builtin adapter was specified, so it's either
54
+ # misspelled or missing from Gemfile.
42
55
  raise e.class, "Could not load the '#{adapter}' Action Cable pubsub adapter. Ensure that the adapter is spelled correctly in config/cable.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
43
56
 
44
- # Bubbled up from the adapter require. Prefix the exception message
45
- # with some guidance about how to address it and reraise.
57
+ # Bubbled up from the adapter require. Prefix the exception message with some
58
+ # guidance about how to address it and reraise.
46
59
  else
47
60
  raise e.class, "Error loading the '#{adapter}' Action Cable pubsub adapter. Missing a gem it depends on? #{e.message}", e.backtrace
48
61
  end
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Server
5
- # Collection class for all the connections that have been established on this specific server. Remember, usually you'll run many Action Cable servers, so
6
- # you can't use this collection as a full list of all of the connections established against your application. Instead, use RemoteConnections for that.
7
+ # # Action Cable Server Connections
8
+ #
9
+ # Collection class for all the connections that have been established on this
10
+ # specific server. Remember, usually you'll run many Action Cable servers, so
11
+ # you can't use this collection as a full list of all of the connections
12
+ # established against your application. Instead, use RemoteConnections for that.
7
13
  module Connections # :nodoc:
8
14
  BEAT_INTERVAL = 3
9
15
 
@@ -19,12 +25,14 @@ module ActionCable
19
25
  connections.delete connection
20
26
  end
21
27
 
22
- # WebSocket connection implementations differ on when they'll mark a connection as stale. We basically never want a connection to go stale, as you
23
- # then can't rely on being able to communicate with the connection. To solve this, a 3 second heartbeat runs on all connections. If the beat fails, we automatically
28
+ # WebSocket connection implementations differ on when they'll mark a connection
29
+ # as stale. We basically never want a connection to go stale, as you then can't
30
+ # rely on being able to communicate with the connection. To solve this, a 3
31
+ # second heartbeat runs on all connections. If the beat fails, we automatically
24
32
  # disconnect.
25
33
  def setup_heartbeat_timer
26
34
  @heartbeat_timer ||= event_loop.timer(BEAT_INTERVAL) do
27
- event_loop.post { connections.map(&:beat) }
35
+ event_loop.post { connections.each(&:beat) }
28
36
  end
29
37
  end
30
38
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Server
5
7
  class Worker
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/callbacks"
4
6
  require "active_support/core_ext/module/attribute_accessors_per_thread"
5
- require "action_cable/server/worker/active_record_connection_management"
6
7
  require "concurrent"
7
8
 
8
9
  module ActionCable
@@ -26,8 +27,8 @@ module ActionCable
26
27
  )
27
28
  end
28
29
 
29
- # Stop processing work: any work that has not already started
30
- # running will be discarded from the queue
30
+ # Stop processing work: any work that has not already started running will be
31
+ # discarded from the queue
31
32
  def halt
32
33
  @executor.shutdown
33
34
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_cable/subscription_adapter/inline"
3
+ # :markup: markdown
4
4
 
5
5
  module ActionCable
6
6
  module SubscriptionAdapter
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module SubscriptionAdapter
5
7
  class Base
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module SubscriptionAdapter
5
7
  module ChannelPrefix # :nodoc:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module SubscriptionAdapter
5
7
  class Inline < Base # :nodoc:
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  gem "pg", "~> 1.1"
4
6
  require "pg"
5
- require "thread"
6
7
  require "openssl"
7
8
 
8
9
  module ActionCable
@@ -35,8 +36,8 @@ module ActionCable
35
36
 
36
37
  def with_subscriptions_connection(&block) # :nodoc:
37
38
  ar_conn = ActiveRecord::Base.connection_pool.checkout.tap do |conn|
38
- # Action Cable is taking ownership over this database connection, and
39
- # will perform the necessary cleanup tasks
39
+ # Action Cable is taking ownership over this database connection, and will
40
+ # perform the necessary cleanup tasks
40
41
  ActiveRecord::Base.connection_pool.remove(conn)
41
42
  end
42
43
  pg_conn = ar_conn.raw_connection