actioncable 7.0.7.2 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -121
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +4 -4
  5. data/app/assets/javascripts/action_cable.js +25 -4
  6. data/app/assets/javascripts/actioncable.esm.js +25 -4
  7. data/app/assets/javascripts/actioncable.js +25 -4
  8. data/lib/action_cable/channel/base.rb +17 -5
  9. data/lib/action_cable/channel/broadcasting.rb +3 -1
  10. data/lib/action_cable/channel/callbacks.rb +37 -0
  11. data/lib/action_cable/channel/naming.rb +4 -2
  12. data/lib/action_cable/channel/streams.rb +2 -0
  13. data/lib/action_cable/channel/test_case.rb +6 -1
  14. data/lib/action_cable/connection/authorization.rb +1 -1
  15. data/lib/action_cable/connection/base.rb +16 -3
  16. data/lib/action_cable/connection/callbacks.rb +55 -0
  17. data/lib/action_cable/connection/internal_channel.rb +3 -1
  18. data/lib/action_cable/connection/stream.rb +1 -3
  19. data/lib/action_cable/connection/stream_event_loop.rb +0 -1
  20. data/lib/action_cable/connection/subscriptions.rb +2 -0
  21. data/lib/action_cable/connection/tagged_logger_proxy.rb +6 -4
  22. data/lib/action_cable/connection/test_case.rb +3 -1
  23. data/lib/action_cable/connection/web_socket.rb +2 -0
  24. data/lib/action_cable/deprecator.rb +7 -0
  25. data/lib/action_cable/engine.rb +13 -5
  26. data/lib/action_cable/gem_version.rb +4 -4
  27. data/lib/action_cable/remote_connections.rb +11 -2
  28. data/lib/action_cable/server/base.rb +3 -0
  29. data/lib/action_cable/server/broadcasting.rb +2 -0
  30. data/lib/action_cable/server/configuration.rb +12 -2
  31. data/lib/action_cable/server/connections.rb +3 -1
  32. data/lib/action_cable/server/worker.rb +0 -1
  33. data/lib/action_cable/subscription_adapter/async.rb +0 -2
  34. data/lib/action_cable/subscription_adapter/postgresql.rb +0 -1
  35. data/lib/action_cable/subscription_adapter/redis.rb +3 -6
  36. data/lib/action_cable/subscription_adapter/test.rb +3 -5
  37. data/lib/action_cable/test_helper.rb +53 -22
  38. data/lib/action_cable/version.rb +1 -1
  39. data/lib/action_cable.rb +24 -12
  40. data/lib/rails/generators/channel/USAGE +14 -8
  41. data/lib/rails/generators/channel/channel_generator.rb +21 -7
  42. metadata +26 -14
  43. data/lib/action_cable/channel.rb +0 -17
  44. data/lib/action_cable/connection.rb +0 -22
  45. data/lib/action_cable/server.rb +0 -16
  46. data/lib/action_cable/subscription_adapter.rb +0 -12
@@ -29,30 +29,33 @@ module ActionCable
29
29
  # end
30
30
  #
31
31
  # If a block is passed, that block should cause the specified number of
32
- # messages to be broadcasted.
32
+ # messages to be broadcasted. It returns the messages that were broadcasted.
33
33
  #
34
34
  # def test_broadcasts_again
35
- # assert_broadcasts('messages', 1) do
35
+ # message = assert_broadcasts('messages', 1) do
36
36
  # ActionCable.server.broadcast 'messages', { text: 'hello' }
37
37
  # end
38
+ # assert_equal({ text: 'hello' }, message)
38
39
  #
39
- # assert_broadcasts('messages', 2) do
40
+ # messages = assert_broadcasts('messages', 2) do
40
41
  # ActionCable.server.broadcast 'messages', { text: 'hi' }
41
42
  # ActionCable.server.broadcast 'messages', { text: 'how are you?' }
42
43
  # end
44
+ # assert_equal 2, messages.length
45
+ # assert_equal({ text: 'hi' }, messages.first)
46
+ # assert_equal({ text: 'how are you?' }, messages.last)
43
47
  # end
44
48
  #
45
49
  def assert_broadcasts(stream, number, &block)
46
50
  if block_given?
47
- original_count = broadcasts_size(stream)
48
- _assert_nothing_raised_or_warn("assert_broadcasts", &block)
49
- new_count = broadcasts_size(stream)
50
- actual_count = new_count - original_count
51
+ new_messages = new_broadcasts_from(broadcasts(stream), stream, "assert_broadcasts", &block)
52
+
53
+ actual_count = new_messages.size
54
+ assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
51
55
  else
52
- actual_count = broadcasts_size(stream)
56
+ actual_count = broadcasts(stream).size
57
+ assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
53
58
  end
54
-
55
- assert_equal number, actual_count, "#{number} broadcasts to #{stream} expected, but #{actual_count} were sent"
56
59
  end
57
60
 
58
61
  # Asserts that no messages have been sent to the stream.
@@ -79,6 +82,22 @@ module ActionCable
79
82
  assert_broadcasts stream, 0, &block
80
83
  end
81
84
 
85
+ # Returns the messages that are broadcasted in the block.
86
+ #
87
+ # def test_broadcasts
88
+ # messages = capture_broadcasts('messages') do
89
+ # ActionCable.server.broadcast 'messages', { text: 'hi' }
90
+ # ActionCable.server.broadcast 'messages', { text: 'how are you?' }
91
+ # end
92
+ # assert_equal 2, messages.length
93
+ # assert_equal({ text: 'hi' }, messages.first)
94
+ # assert_equal({ text: 'how are you?' }, messages.last)
95
+ # end
96
+ #
97
+ def capture_broadcasts(stream, &block)
98
+ new_broadcasts_from(broadcasts(stream), stream, "capture_broadcasts", &block).map { |m| ActiveSupport::JSON.decode(m) }
99
+ end
100
+
82
101
  # Asserts that the specified message has been sent to the stream.
83
102
  #
84
103
  # def test_assert_transmitted_message
@@ -103,20 +122,22 @@ module ActionCable
103
122
 
104
123
  new_messages = broadcasts(stream)
105
124
  if block_given?
106
- old_messages = new_messages
107
- clear_messages(stream)
108
-
109
- _assert_nothing_raised_or_warn("assert_broadcast_on", &block)
110
- new_messages = broadcasts(stream)
111
- clear_messages(stream)
112
-
113
- # Restore all sent messages
114
- (old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
125
+ new_messages = new_broadcasts_from(new_messages, stream, "assert_broadcast_on", &block)
115
126
  end
116
127
 
117
128
  message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }
118
129
 
119
- assert message, "No messages sent with #{data} to #{stream}"
130
+ error_message = "No messages sent with #{data} to #{stream}"
131
+
132
+ if new_messages.any?
133
+ error_message = new_messages.inject("#{error_message}\nMessage(s) found:\n") do |error_message, new_message|
134
+ error_message + "#{ActiveSupport::JSON.decode(new_message)}\n"
135
+ end
136
+ else
137
+ error_message = "#{error_message}\nNo message found for #{stream}"
138
+ end
139
+
140
+ assert message, error_message
120
141
  end
121
142
 
122
143
  def pubsub_adapter # :nodoc:
@@ -126,8 +147,18 @@ module ActionCable
126
147
  delegate :broadcasts, :clear_messages, to: :pubsub_adapter
127
148
 
128
149
  private
129
- def broadcasts_size(channel)
130
- broadcasts(channel).size
150
+ def new_broadcasts_from(current_messages, stream, assertion, &block)
151
+ old_messages = current_messages
152
+ clear_messages(stream)
153
+
154
+ _assert_nothing_raised_or_warn(assertion, &block)
155
+ new_messages = broadcasts(stream)
156
+ clear_messages(stream)
157
+
158
+ # Restore all sent messages
159
+ (old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
160
+
161
+ new_messages
131
162
  end
132
163
  end
133
164
  end
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActionCable
6
- # 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+.
7
7
  def self.version
8
8
  gem_version
9
9
  end
data/lib/action_cable.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2015-2022 Basecamp, LLC
4
+ # Copyright (c) 37signals LLC
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -25,10 +25,29 @@
25
25
 
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
- require "action_cable/version"
28
+ require "zeitwerk"
29
29
 
30
+ Zeitwerk::Loader.for_gem.tap do |loader|
31
+ loader.ignore(
32
+ "#{__dir__}/rails", # Contains generators, templates, docs, etc.
33
+ "#{__dir__}/action_cable/gem_version.rb",
34
+ "#{__dir__}/action_cable/deprecator.rb",
35
+ )
36
+
37
+ loader.do_not_eager_load(
38
+ "#{__dir__}/action_cable/subscription_adapter", # Adapters are required and loaded on demand.
39
+ "#{__dir__}/action_cable/test_helper.rb",
40
+ Dir["#{__dir__}/action_cable/**/test_case.rb"]
41
+ )
42
+
43
+ loader.inflector.inflect("postgresql" => "PostgreSQL")
44
+ end.setup
45
+
46
+ # :markup: markdown
47
+ # :include: actioncable/README.md
30
48
  module ActionCable
31
- extend ActiveSupport::Autoload
49
+ require_relative "action_cable/version"
50
+ require_relative "action_cable/deprecator"
32
51
 
33
52
  INTERNAL = {
34
53
  message_types: {
@@ -41,7 +60,8 @@ module ActionCable
41
60
  disconnect_reasons: {
42
61
  unauthorized: "unauthorized",
43
62
  invalid_request: "invalid_request",
44
- server_restart: "server_restart"
63
+ server_restart: "server_restart",
64
+ remote: "remote"
45
65
  },
46
66
  default_mount_path: "/cable",
47
67
  protocols: ["actioncable-v1-json", "actioncable-unsupported"].freeze
@@ -51,12 +71,4 @@ module ActionCable
51
71
  module_function def server
52
72
  @server ||= ActionCable::Server::Base.new
53
73
  end
54
-
55
- autoload :Server
56
- autoload :Connection
57
- autoload :Channel
58
- autoload :RemoteConnections
59
- autoload :SubscriptionAdapter
60
- autoload :TestHelper
61
- autoload :TestCase
62
74
  end
@@ -1,13 +1,19 @@
1
1
  Description:
2
- ============
3
2
  Generates a new cable channel for the server (in Ruby) and client (in JavaScript).
4
3
  Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments.
5
4
 
6
- Example:
7
- ========
8
- bin/rails generate channel Chat speak
5
+ Examples:
6
+ `bin/rails generate channel notification`
9
7
 
10
- creates a Chat channel class, test and JavaScript asset:
11
- Channel: app/channels/chat_channel.rb
12
- Test: test/channels/chat_channel_test.rb
13
- Assets: $JAVASCRIPT_PATH/channels/chat_channel.js
8
+ creates a notification channel class, test and JavaScript asset:
9
+ Channel: app/channels/notification_channel.rb
10
+ Test: test/channels/notification_channel_test.rb
11
+ Assets: $JAVASCRIPT_PATH/channels/notification_channel.js
12
+
13
+ `bin/rails generate channel chat speak`
14
+
15
+ creates a chat channel with a speak action.
16
+
17
+ `bin/rails generate channel comments --no-assets`
18
+
19
+ creates a comments channel without JavaScript assets.
@@ -24,7 +24,7 @@ module Rails
24
24
 
25
25
  if using_importmap?
26
26
  pin_javascript_dependencies
27
- elsif using_node?
27
+ elsif using_js_runtime?
28
28
  install_javascript_dependencies
29
29
  end
30
30
  end
@@ -57,22 +57,26 @@ module Rails
57
57
  def create_channel_javascript_file
58
58
  channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
59
59
  js_template "javascript/channel", channel_js_path
60
- gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_node?
60
+ gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_js_runtime?
61
61
  end
62
62
 
63
63
  def import_channels_in_javascript_entrypoint
64
64
  append_to_file "app/javascript/application.js",
65
- using_node? ? %(import "./channels"\n) : %(import "channels"\n)
65
+ using_js_runtime? ? %(import "./channels"\n) : %(import "channels"\n)
66
66
  end
67
67
 
68
68
  def import_channel_in_javascript_entrypoint
69
69
  append_to_file "app/javascript/channels/index.js",
70
- using_node? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n)
70
+ using_js_runtime? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n)
71
71
  end
72
72
 
73
73
  def install_javascript_dependencies
74
74
  say "Installing JavaScript dependencies", :green
75
- run "yarn add @rails/actioncable"
75
+ if using_bun?
76
+ run "bun add @rails/actioncable"
77
+ elsif using_node?
78
+ run "yarn add @rails/actioncable"
79
+ end
76
80
  end
77
81
 
78
82
  def pin_javascript_dependencies
@@ -82,7 +86,6 @@ pin_all_from "app/javascript/channels", under: "channels"
82
86
  RUBY
83
87
  end
84
88
 
85
-
86
89
  def file_name
87
90
  @_file_name ||= super.sub(/_channel\z/i, "")
88
91
  end
@@ -95,8 +98,19 @@ pin_all_from "app/javascript/channels", under: "channels"
95
98
  @using_javascript ||= options[:assets] && root.join("app/javascript").exist?
96
99
  end
97
100
 
101
+ def using_js_runtime?
102
+ @using_js_runtime ||= root.join("package.json").exist?
103
+ end
104
+
105
+ def using_bun?
106
+ # Cannot assume bun.lockb has been generated yet so we look for
107
+ # a file known to be generated by the jsbundling-rails gem
108
+ @using_bun ||= using_js_runtime? && root.join("bun.config.js").exist?
109
+ end
110
+
98
111
  def using_node?
99
- @using_node ||= root.join("package.json").exist?
112
+ # Bun is the only runtime that _isn't_ node.
113
+ @using_node ||= using_js_runtime? && !root.join("bun.config.js").exist?
100
114
  end
101
115
 
102
116
  def using_importmap?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actioncable
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.0.7.2
4
+ version: 7.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pratik Naik
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-08-22 00:00:00.000000000 Z
12
+ date: 2023-10-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -17,28 +17,28 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 7.0.7.2
20
+ version: 7.1.1
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 7.0.7.2
27
+ version: 7.1.1
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: actionpack
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - '='
33
33
  - !ruby/object:Gem::Version
34
- version: 7.0.7.2
34
+ version: 7.1.1
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - '='
40
40
  - !ruby/object:Gem::Version
41
- version: 7.0.7.2
41
+ version: 7.1.1
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: nio4r
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: 0.6.1
70
+ - !ruby/object:Gem::Dependency
71
+ name: zeitwerk
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.6'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.6'
70
84
  description: Structure many real-time application concerns into channels over a single
71
85
  WebSocket connection.
72
86
  email:
@@ -83,7 +97,6 @@ files:
83
97
  - app/assets/javascripts/actioncable.esm.js
84
98
  - app/assets/javascripts/actioncable.js
85
99
  - lib/action_cable.rb
86
- - lib/action_cable/channel.rb
87
100
  - lib/action_cable/channel/base.rb
88
101
  - lib/action_cable/channel/broadcasting.rb
89
102
  - lib/action_cable/channel/callbacks.rb
@@ -91,9 +104,9 @@ files:
91
104
  - lib/action_cable/channel/periodic_timers.rb
92
105
  - lib/action_cable/channel/streams.rb
93
106
  - lib/action_cable/channel/test_case.rb
94
- - lib/action_cable/connection.rb
95
107
  - lib/action_cable/connection/authorization.rb
96
108
  - lib/action_cable/connection/base.rb
109
+ - lib/action_cable/connection/callbacks.rb
97
110
  - lib/action_cable/connection/client_socket.rb
98
111
  - lib/action_cable/connection/identification.rb
99
112
  - lib/action_cable/connection/internal_channel.rb
@@ -104,18 +117,17 @@ files:
104
117
  - lib/action_cable/connection/tagged_logger_proxy.rb
105
118
  - lib/action_cable/connection/test_case.rb
106
119
  - lib/action_cable/connection/web_socket.rb
120
+ - lib/action_cable/deprecator.rb
107
121
  - lib/action_cable/engine.rb
108
122
  - lib/action_cable/gem_version.rb
109
123
  - lib/action_cable/helpers/action_cable_helper.rb
110
124
  - lib/action_cable/remote_connections.rb
111
- - lib/action_cable/server.rb
112
125
  - lib/action_cable/server/base.rb
113
126
  - lib/action_cable/server/broadcasting.rb
114
127
  - lib/action_cable/server/configuration.rb
115
128
  - lib/action_cable/server/connections.rb
116
129
  - lib/action_cable/server/worker.rb
117
130
  - lib/action_cable/server/worker/active_record_connection_management.rb
118
- - lib/action_cable/subscription_adapter.rb
119
131
  - lib/action_cable/subscription_adapter/async.rb
120
132
  - lib/action_cable/subscription_adapter/base.rb
121
133
  - lib/action_cable/subscription_adapter/channel_prefix.rb
@@ -142,10 +154,10 @@ licenses:
142
154
  - MIT
143
155
  metadata:
144
156
  bug_tracker_uri: https://github.com/rails/rails/issues
145
- changelog_uri: https://github.com/rails/rails/blob/v7.0.7.2/actioncable/CHANGELOG.md
146
- documentation_uri: https://api.rubyonrails.org/v7.0.7.2/
157
+ changelog_uri: https://github.com/rails/rails/blob/v7.1.1/actioncable/CHANGELOG.md
158
+ documentation_uri: https://api.rubyonrails.org/v7.1.1/
147
159
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
148
- source_code_uri: https://github.com/rails/rails/tree/v7.0.7.2/actioncable
160
+ source_code_uri: https://github.com/rails/rails/tree/v7.1.1/actioncable
149
161
  rubygems_mfa_required: 'true'
150
162
  post_install_message:
151
163
  rdoc_options: []
@@ -162,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
174
  - !ruby/object:Gem::Version
163
175
  version: '0'
164
176
  requirements: []
165
- rubygems_version: 3.3.3
177
+ rubygems_version: 3.4.18
166
178
  signing_key:
167
179
  specification_version: 4
168
180
  summary: WebSocket framework for Rails.
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module Channel
5
- extend ActiveSupport::Autoload
6
-
7
- eager_autoload do
8
- autoload :Base
9
- autoload :Broadcasting
10
- autoload :Callbacks
11
- autoload :Naming
12
- autoload :PeriodicTimers
13
- autoload :Streams
14
- autoload :TestCase
15
- end
16
- end
17
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module Connection
5
- extend ActiveSupport::Autoload
6
-
7
- eager_autoload do
8
- autoload :Authorization
9
- autoload :Base
10
- autoload :ClientSocket
11
- autoload :Identification
12
- autoload :InternalChannel
13
- autoload :MessageBuffer
14
- autoload :Stream
15
- autoload :StreamEventLoop
16
- autoload :Subscriptions
17
- autoload :TaggedLoggerProxy
18
- autoload :TestCase
19
- autoload :WebSocket
20
- end
21
- end
22
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module Server
5
- extend ActiveSupport::Autoload
6
-
7
- eager_autoload do
8
- autoload :Base
9
- autoload :Broadcasting
10
- autoload :Connections
11
- autoload :Configuration
12
-
13
- autoload :Worker
14
- end
15
- end
16
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module SubscriptionAdapter
5
- extend ActiveSupport::Autoload
6
-
7
- autoload :Base
8
- autoload :Test
9
- autoload :SubscriberMap
10
- autoload :ChannelPrefix
11
- end
12
- end