actioncable-next 0.1.0

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 +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +17 -0
  5. data/lib/action_cable/channel/base.rb +335 -0
  6. data/lib/action_cable/channel/broadcasting.rb +50 -0
  7. data/lib/action_cable/channel/callbacks.rb +76 -0
  8. data/lib/action_cable/channel/naming.rb +28 -0
  9. data/lib/action_cable/channel/periodic_timers.rb +81 -0
  10. data/lib/action_cable/channel/streams.rb +213 -0
  11. data/lib/action_cable/channel/test_case.rb +329 -0
  12. data/lib/action_cable/connection/authorization.rb +18 -0
  13. data/lib/action_cable/connection/base.rb +165 -0
  14. data/lib/action_cable/connection/callbacks.rb +57 -0
  15. data/lib/action_cable/connection/identification.rb +51 -0
  16. data/lib/action_cable/connection/internal_channel.rb +50 -0
  17. data/lib/action_cable/connection/subscriptions.rb +124 -0
  18. data/lib/action_cable/connection/test_case.rb +294 -0
  19. data/lib/action_cable/deprecator.rb +9 -0
  20. data/lib/action_cable/engine.rb +98 -0
  21. data/lib/action_cable/gem_version.rb +19 -0
  22. data/lib/action_cable/helpers/action_cable_helper.rb +45 -0
  23. data/lib/action_cable/remote_connections.rb +82 -0
  24. data/lib/action_cable/server/base.rb +163 -0
  25. data/lib/action_cable/server/broadcasting.rb +62 -0
  26. data/lib/action_cable/server/configuration.rb +75 -0
  27. data/lib/action_cable/server/connections.rb +44 -0
  28. data/lib/action_cable/server/socket/client_socket.rb +159 -0
  29. data/lib/action_cable/server/socket/message_buffer.rb +56 -0
  30. data/lib/action_cable/server/socket/stream.rb +117 -0
  31. data/lib/action_cable/server/socket/web_socket.rb +47 -0
  32. data/lib/action_cable/server/socket.rb +180 -0
  33. data/lib/action_cable/server/stream_event_loop.rb +119 -0
  34. data/lib/action_cable/server/tagged_logger_proxy.rb +46 -0
  35. data/lib/action_cable/server/worker/active_record_connection_management.rb +23 -0
  36. data/lib/action_cable/server/worker.rb +75 -0
  37. data/lib/action_cable/subscription_adapter/async.rb +14 -0
  38. data/lib/action_cable/subscription_adapter/base.rb +39 -0
  39. data/lib/action_cable/subscription_adapter/channel_prefix.rb +30 -0
  40. data/lib/action_cable/subscription_adapter/inline.rb +40 -0
  41. data/lib/action_cable/subscription_adapter/postgresql.rb +130 -0
  42. data/lib/action_cable/subscription_adapter/redis.rb +257 -0
  43. data/lib/action_cable/subscription_adapter/subscriber_map.rb +80 -0
  44. data/lib/action_cable/subscription_adapter/test.rb +41 -0
  45. data/lib/action_cable/test_case.rb +13 -0
  46. data/lib/action_cable/test_helper.rb +163 -0
  47. data/lib/action_cable/version.rb +12 -0
  48. data/lib/action_cable.rb +81 -0
  49. data/lib/actioncable-next.rb +5 -0
  50. data/lib/rails/generators/channel/USAGE +19 -0
  51. data/lib/rails/generators/channel/channel_generator.rb +127 -0
  52. data/lib/rails/generators/channel/templates/application_cable/channel.rb.tt +4 -0
  53. data/lib/rails/generators/channel/templates/application_cable/connection.rb.tt +4 -0
  54. data/lib/rails/generators/channel/templates/channel.rb.tt +16 -0
  55. data/lib/rails/generators/channel/templates/javascript/channel.js.tt +20 -0
  56. data/lib/rails/generators/channel/templates/javascript/consumer.js.tt +6 -0
  57. data/lib/rails/generators/channel/templates/javascript/index.js.tt +1 -0
  58. data/lib/rails/generators/test_unit/channel_generator.rb +22 -0
  59. data/lib/rails/generators/test_unit/templates/channel_test.rb.tt +8 -0
  60. metadata +191 -0
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # Copyright (c) 37signals LLC
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ require "active_support"
27
+ require "active_support/rails"
28
+ require "zeitwerk"
29
+
30
+ # We compute lib this way instead of using __dir__ because __dir__ gives a real
31
+ # path, while __FILE__ honors symlinks. If the gem is stored under a symlinked
32
+ # directory, this matters.
33
+ lib = File.dirname(__FILE__)
34
+
35
+ Zeitwerk::Loader.for_gem.tap do |loader|
36
+ loader.ignore(
37
+ "#{lib}/rails", # Contains generators, templates, docs, etc.
38
+ "#{lib}/action_cable/gem_version.rb",
39
+ "#{lib}/action_cable/version.rb",
40
+ "#{lib}/action_cable/deprecator.rb",
41
+ "#{lib}/actioncable-next.rb",
42
+ )
43
+
44
+ loader.do_not_eager_load(
45
+ "#{lib}/action_cable/subscription_adapter", # Adapters are required and loaded on demand.
46
+ "#{lib}/action_cable/test_helper.rb",
47
+ Dir["#{lib}/action_cable/**/test_case.rb"]
48
+ )
49
+
50
+ loader.inflector.inflect("postgresql" => "PostgreSQL")
51
+ end.setup
52
+
53
+ # :markup: markdown
54
+ # :include: ../README.md
55
+ module ActionCable
56
+ require_relative "action_cable/version"
57
+ require_relative "action_cable/deprecator"
58
+
59
+ INTERNAL = {
60
+ message_types: {
61
+ welcome: "welcome",
62
+ disconnect: "disconnect",
63
+ ping: "ping",
64
+ confirmation: "confirm_subscription",
65
+ rejection: "reject_subscription"
66
+ },
67
+ disconnect_reasons: {
68
+ unauthorized: "unauthorized",
69
+ invalid_request: "invalid_request",
70
+ server_restart: "server_restart",
71
+ remote: "remote"
72
+ },
73
+ default_mount_path: "/cable",
74
+ protocols: ["actioncable-v1-json", "actioncable-unsupported"].freeze
75
+ }
76
+
77
+ # Singleton instance of the server
78
+ module_function def server
79
+ @server ||= ActionCable::Server::Base.new
80
+ end
81
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionCableNext
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,19 @@
1
+ Description:
2
+ Generates a new cable channel for the server (in Ruby) and client (in JavaScript).
3
+ Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments.
4
+
5
+ Examples:
6
+ `bin/rails generate channel notification`
7
+
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.
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module Rails
6
+ module Generators
7
+ class ChannelGenerator < NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ argument :actions, type: :array, default: [], banner: "method method"
11
+
12
+ class_option :assets, type: :boolean
13
+
14
+ check_class_collision suffix: "Channel"
15
+
16
+ hook_for :test_framework
17
+
18
+ def create_channel_files
19
+ create_shared_channel_files
20
+ create_channel_file
21
+
22
+ if using_javascript?
23
+ if first_setup_required?
24
+ create_shared_channel_javascript_files
25
+ import_channels_in_javascript_entrypoint
26
+
27
+ if using_importmap?
28
+ pin_javascript_dependencies
29
+ elsif using_js_runtime?
30
+ install_javascript_dependencies
31
+ end
32
+ end
33
+
34
+ create_channel_javascript_file
35
+ import_channel_in_javascript_entrypoint
36
+ end
37
+ end
38
+
39
+ private
40
+ def create_shared_channel_files
41
+ return if behavior != :invoke
42
+
43
+ copy_file "#{__dir__}/templates/application_cable/channel.rb",
44
+ "app/channels/application_cable/channel.rb"
45
+ copy_file "#{__dir__}/templates/application_cable/connection.rb",
46
+ "app/channels/application_cable/connection.rb"
47
+ end
48
+
49
+ def create_channel_file
50
+ template "channel.rb",
51
+ File.join("app/channels", class_path, "#{file_name}_channel.rb")
52
+ end
53
+
54
+ def create_shared_channel_javascript_files
55
+ template "javascript/index.js", "app/javascript/channels/index.js"
56
+ template "javascript/consumer.js", "app/javascript/channels/consumer.js"
57
+ end
58
+
59
+ def create_channel_javascript_file
60
+ channel_js_path = File.join("app/javascript/channels", class_path, "#{file_name}_channel")
61
+ js_template "javascript/channel", channel_js_path
62
+ gsub_file "#{channel_js_path}.js", /\.\/consumer/, "channels/consumer" unless using_js_runtime?
63
+ end
64
+
65
+ def import_channels_in_javascript_entrypoint
66
+ append_to_file "app/javascript/application.js",
67
+ using_js_runtime? ? %(import "./channels"\n) : %(import "channels"\n)
68
+ end
69
+
70
+ def import_channel_in_javascript_entrypoint
71
+ append_to_file "app/javascript/channels/index.js",
72
+ using_js_runtime? ? %(import "./#{file_name}_channel"\n) : %(import "channels/#{file_name}_channel"\n)
73
+ end
74
+
75
+ def install_javascript_dependencies
76
+ say "Installing JavaScript dependencies", :green
77
+ if using_bun?
78
+ run "bun add @rails/actioncable"
79
+ elsif using_node?
80
+ run "yarn add @rails/actioncable"
81
+ end
82
+ end
83
+
84
+ def pin_javascript_dependencies
85
+ append_to_file "config/importmap.rb", <<-RUBY
86
+ pin "@rails/actioncable", to: "actioncable.esm.js"
87
+ pin_all_from "app/javascript/channels", under: "channels"
88
+ RUBY
89
+ end
90
+
91
+ def file_name
92
+ @_file_name ||= super.sub(/_channel\z/i, "")
93
+ end
94
+
95
+ def first_setup_required?
96
+ !root.join("app/javascript/channels/index.js").exist?
97
+ end
98
+
99
+ def using_javascript?
100
+ @using_javascript ||= options[:assets] && root.join("app/javascript").exist?
101
+ end
102
+
103
+ def using_js_runtime?
104
+ @using_js_runtime ||= root.join("package.json").exist?
105
+ end
106
+
107
+ def using_bun?
108
+ # Cannot assume bun.lockb has been generated yet so we look for a file known to
109
+ # be generated by the jsbundling-rails gem
110
+ @using_bun ||= using_js_runtime? && root.join("bun.config.js").exist?
111
+ end
112
+
113
+ def using_node?
114
+ # Bun is the only runtime that _isn't_ node.
115
+ @using_node ||= using_js_runtime? && !root.join("bun.config.js").exist?
116
+ end
117
+
118
+ def using_importmap?
119
+ @using_importmap ||= root.join("config/importmap.rb").exist?
120
+ end
121
+
122
+ def root
123
+ @root ||= Pathname(destination_root)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Channel < ApplicationCable::Channel
3
+ def subscribed
4
+ # stream_from "some_channel"
5
+ end
6
+
7
+ def unsubscribed
8
+ # Any cleanup needed when channel is unsubscribed
9
+ end
10
+ <% actions.each do |action| -%>
11
+
12
+ def <%= action %>
13
+ end
14
+ <% end -%>
15
+ end
16
+ <% end -%>
@@ -0,0 +1,20 @@
1
+ import consumer from "./consumer"
2
+
3
+ consumer.subscriptions.create("<%= class_name %>Channel", {
4
+ connected() {
5
+ // Called when the subscription is ready for use on the server
6
+ },
7
+
8
+ disconnected() {
9
+ // Called when the subscription has been terminated by the server
10
+ },
11
+
12
+ received(data) {
13
+ // Called when there's incoming data on the websocket for this channel
14
+ }<%= actions.any? ? ",\n" : '' %>
15
+ <% actions.each do |action| -%>
16
+ <%=action %>: function() {
17
+ return this.perform('<%= action %>');
18
+ }<%= action == actions[-1] ? '' : ",\n" %>
19
+ <% end -%>
20
+ });
@@ -0,0 +1,6 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
3
+
4
+ import { createConsumer } from "@rails/actioncable"
5
+
6
+ export default createConsumer()
@@ -0,0 +1 @@
1
+ // Import all the channels to be used by Action Cable
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module TestUnit
6
+ module Generators
7
+ class ChannelGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ check_class_collision suffix: "ChannelTest"
11
+
12
+ def create_test_files
13
+ template "channel_test.rb", File.join("test/channels", class_path, "#{file_name}_channel_test.rb")
14
+ end
15
+
16
+ private
17
+ def file_name # :doc:
18
+ @_file_name ||= super.sub(/_channel\z/i, "")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ require "test_helper"
2
+
3
+ class <%= class_name %>ChannelTest < ActionCable::Channel::TestCase
4
+ # test "subscribes" do
5
+ # subscribe
6
+ # assert subscription.confirmed?
7
+ # end
8
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: actioncable-next
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pratik Naik
8
+ - David Heinemeier Hansson
9
+ - Vladimir Dementyev
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2024-09-30 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '7.0'
22
+ - - "<="
23
+ - !ruby/object:Gem::Version
24
+ version: '8.1'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: '7.0'
32
+ - - "<="
33
+ - !ruby/object:Gem::Version
34
+ version: '8.1'
35
+ - !ruby/object:Gem::Dependency
36
+ name: actionpack
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '7.0'
42
+ - - "<="
43
+ - !ruby/object:Gem::Version
44
+ version: '8.1'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '7.0'
52
+ - - "<="
53
+ - !ruby/object:Gem::Version
54
+ version: '8.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nio4r
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: websocket-driver
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 0.6.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.6.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: zeitwerk
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.6'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.6'
97
+ description: Next-gen version of Action Cable
98
+ email:
99
+ - pratiknaik@gmail.com
100
+ - david@loudthinking.com
101
+ - palkan@evilmartians.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - CHANGELOG.md
107
+ - MIT-LICENSE
108
+ - README.md
109
+ - lib/action_cable.rb
110
+ - lib/action_cable/channel/base.rb
111
+ - lib/action_cable/channel/broadcasting.rb
112
+ - lib/action_cable/channel/callbacks.rb
113
+ - lib/action_cable/channel/naming.rb
114
+ - lib/action_cable/channel/periodic_timers.rb
115
+ - lib/action_cable/channel/streams.rb
116
+ - lib/action_cable/channel/test_case.rb
117
+ - lib/action_cable/connection/authorization.rb
118
+ - lib/action_cable/connection/base.rb
119
+ - lib/action_cable/connection/callbacks.rb
120
+ - lib/action_cable/connection/identification.rb
121
+ - lib/action_cable/connection/internal_channel.rb
122
+ - lib/action_cable/connection/subscriptions.rb
123
+ - lib/action_cable/connection/test_case.rb
124
+ - lib/action_cable/deprecator.rb
125
+ - lib/action_cable/engine.rb
126
+ - lib/action_cable/gem_version.rb
127
+ - lib/action_cable/helpers/action_cable_helper.rb
128
+ - lib/action_cable/remote_connections.rb
129
+ - lib/action_cable/server/base.rb
130
+ - lib/action_cable/server/broadcasting.rb
131
+ - lib/action_cable/server/configuration.rb
132
+ - lib/action_cable/server/connections.rb
133
+ - lib/action_cable/server/socket.rb
134
+ - lib/action_cable/server/socket/client_socket.rb
135
+ - lib/action_cable/server/socket/message_buffer.rb
136
+ - lib/action_cable/server/socket/stream.rb
137
+ - lib/action_cable/server/socket/web_socket.rb
138
+ - lib/action_cable/server/stream_event_loop.rb
139
+ - lib/action_cable/server/tagged_logger_proxy.rb
140
+ - lib/action_cable/server/worker.rb
141
+ - lib/action_cable/server/worker/active_record_connection_management.rb
142
+ - lib/action_cable/subscription_adapter/async.rb
143
+ - lib/action_cable/subscription_adapter/base.rb
144
+ - lib/action_cable/subscription_adapter/channel_prefix.rb
145
+ - lib/action_cable/subscription_adapter/inline.rb
146
+ - lib/action_cable/subscription_adapter/postgresql.rb
147
+ - lib/action_cable/subscription_adapter/redis.rb
148
+ - lib/action_cable/subscription_adapter/subscriber_map.rb
149
+ - lib/action_cable/subscription_adapter/test.rb
150
+ - lib/action_cable/test_case.rb
151
+ - lib/action_cable/test_helper.rb
152
+ - lib/action_cable/version.rb
153
+ - lib/actioncable-next.rb
154
+ - lib/rails/generators/channel/USAGE
155
+ - lib/rails/generators/channel/channel_generator.rb
156
+ - lib/rails/generators/channel/templates/application_cable/channel.rb.tt
157
+ - lib/rails/generators/channel/templates/application_cable/connection.rb.tt
158
+ - lib/rails/generators/channel/templates/channel.rb.tt
159
+ - lib/rails/generators/channel/templates/javascript/channel.js.tt
160
+ - lib/rails/generators/channel/templates/javascript/consumer.js.tt
161
+ - lib/rails/generators/channel/templates/javascript/index.js.tt
162
+ - lib/rails/generators/test_unit/channel_generator.rb
163
+ - lib/rails/generators/test_unit/templates/channel_test.rb.tt
164
+ homepage: https://github.com/anycable/action_cable-next
165
+ licenses:
166
+ - MIT
167
+ metadata:
168
+ bug_tracker_uri: https://github.com/anycable/actioncable-next
169
+ changelog_uri: https://github.com/anycable/actioncable-next/blob/v0.1.0/CHANGELOG.md
170
+ source_code_uri: https://github.com/anycable/actioncable-next
171
+ rubygems_mfa_required: 'true'
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: 3.1.0
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ requirements: []
187
+ rubygems_version: 3.5.18
188
+ signing_key:
189
+ specification_version: 4
190
+ summary: Next-gen version of Action Cable
191
+ test_files: []