anycable-rails-core 1.6.0.rc.3 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -3
- data/lib/anycable/rails/action_cable_ext/remote_connections.rb +1 -1
- data/lib/anycable/rails/config.rb +2 -0
- data/lib/anycable/rails/connection.rb +5 -1
- data/lib/anycable/rails/railtie.rb +1 -0
- data/lib/anycable/rails/rubocop/cops/anycable/periodical_timers.rb +2 -7
- data/lib/anycable/rails/socket_id_tracking.rb +2 -2
- data/lib/anycable/rails/version.rb +1 -1
- data/lib/generators/anycable/bin/templates/bin/anycable-go.tt +5 -0
- data/lib/generators/anycable/setup/setup_generator.rb +268 -33
- data/lib/generators/anycable/setup/templates/Procfile.dev.tt +2 -2
- data/lib/generators/anycable/setup/templates/anycable.toml.tt +40 -42
- data/lib/generators/anycable/setup/templates/bin/anycable-go.tt +5 -0
- data/lib/generators/anycable/setup/templates/bin/dev +20 -0
- data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +18 -35
- data/lib/generators/anycable/setup/templates/config/cable.yml.tt +2 -5
- metadata +9 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1dc965588a4b4f798a031a398319683240bd3319f8e9ce843bc3c0a830e00b2
|
|
4
|
+
data.tar.gz: 4e0739d26c204dbb9c04db0f6a6772809e0578b9bdc8a5f95509511351c18fff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 030a59929994059d44a1db9d3f5a9696c8443cd11a4d54ffb2538fcf0becc1d4bcc473e80610dc6fd4f94508f3c744ea83d348da91442eb1f03806ae4f886d5f
|
|
7
|
+
data.tar.gz: aae67b4149566140a102932a22c7716b181745d9b5df92edd60480a5567b9a144857f8f1e23fcc540929bf5163ef0678c753ef4abccd302ed269d731a5e2852a
|
data/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## main
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Upgrade setup generator. ([@palkan][])
|
|
6
|
+
|
|
7
|
+
- Ensure `bin/dist/anycable-go` has execution permission as part of `bin/anycable-go` ([@dmorgan-fa][])
|
|
8
|
+
|
|
9
|
+
## 1.6.0 (2025-03-18)
|
|
6
10
|
|
|
7
11
|
- Update `anycable:download` generator to support v1.6+. ([@palkan][])
|
|
8
12
|
|
|
@@ -10,8 +14,6 @@
|
|
|
10
14
|
|
|
11
15
|
It generates a meta tag with the AnyCable JWT token (w/o the URL).
|
|
12
16
|
|
|
13
|
-
## 1.6.0.rc.2 (2025-02-10)
|
|
14
|
-
|
|
15
17
|
- Add Presence API. ([@palkan][])
|
|
16
18
|
|
|
17
19
|
## 1.5.5 (2024-12-12)
|
|
@@ -6,7 +6,7 @@ ActionCable::RemoteConnections::RemoteConnection.include(AnyCable::Rails::Connec
|
|
|
6
6
|
ActionCable::RemoteConnections::RemoteConnection.prepend(Module.new do
|
|
7
7
|
def disconnect(reconnect: true)
|
|
8
8
|
# Legacy Action Cable functionality if case we're not fully migrated yet
|
|
9
|
-
super unless AnyCable::Rails.enabled?
|
|
9
|
+
return super unless AnyCable::Rails.enabled?
|
|
10
10
|
::AnyCable.broadcast_adapter.broadcast_command("disconnect", identifier: identifiers_json, reconnect: reconnect)
|
|
11
11
|
end
|
|
12
12
|
end)
|
|
@@ -9,6 +9,7 @@ require "anyway/rails"
|
|
|
9
9
|
# - `access_logs_disabled` (defaults to true) — whether to print Started/Finished logs
|
|
10
10
|
# - `persistent_session_enabled` (defaults to false) — whether to store session changes in the connection state
|
|
11
11
|
# - `embedded` (defaults to false) — whether to run RPC server inside a Rails server process
|
|
12
|
+
# - `http_rpc` (default to false) - whether to mount HTTP RPC server or not
|
|
12
13
|
# - `http_rpc_mount_path` (defaults to nil) — path to mount HTTP RPC server
|
|
13
14
|
# - `batch_broadcasts` (defaults to false) — whether to batch broadcasts automatically for code wrapped with Rails executor
|
|
14
15
|
# - `jwt_param` (defaults to 'jid') — the name of the JWT authentication query paramter or header
|
|
@@ -18,6 +19,7 @@ AnyCable::Config.attr_config(
|
|
|
18
19
|
persistent_session_enabled: false,
|
|
19
20
|
embedded: false,
|
|
20
21
|
jwt_param: "jid",
|
|
22
|
+
http_rpc: false,
|
|
21
23
|
http_rpc_mount_path: nil,
|
|
22
24
|
batch_broadcasts: false,
|
|
23
25
|
socket_id_header: "X-Socket-ID",
|
|
@@ -80,7 +80,11 @@ module AnyCable
|
|
|
80
80
|
|
|
81
81
|
def subscription_class_from_identifier(id_key)
|
|
82
82
|
id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
|
|
83
|
-
id_options[:channel]
|
|
83
|
+
if id_options[:channel] == "$pubsub"
|
|
84
|
+
PubSubChannel
|
|
85
|
+
else
|
|
86
|
+
id_options[:channel].safe_constantize
|
|
87
|
+
end
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
def subscription_from_identifier(id_key)
|
|
@@ -113,6 +113,7 @@ module AnyCable
|
|
|
113
113
|
initializer "anycable.routes" do
|
|
114
114
|
config.after_initialize do |app|
|
|
115
115
|
config = AnyCable.config
|
|
116
|
+
config.http_rpc_mount_path ||= "/_anycable" if config.http_rpc?
|
|
116
117
|
unless config.http_rpc_mount_path.nil?
|
|
117
118
|
app.routes.prepend do
|
|
118
119
|
mount AnyCable::HTTRPC::Server.new => config.http_rpc_mount_path, :internal => true
|
|
@@ -15,14 +15,9 @@ module RuboCop
|
|
|
15
15
|
#
|
|
16
16
|
class PeriodicalTimers < RuboCop::Cop::Base
|
|
17
17
|
MSG = "Periodical Timers are not supported in AnyCable"
|
|
18
|
+
RESTRICT_ON_SEND = %i[periodically].freeze
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
(send _ :periodically ...)
|
|
21
|
-
PATTERN
|
|
22
|
-
|
|
23
|
-
def on_send(node)
|
|
24
|
-
add_offense(node) if calls_periodically?(node)
|
|
25
|
-
end
|
|
20
|
+
alias_method :on_send, :add_offense
|
|
26
21
|
end
|
|
27
22
|
end
|
|
28
23
|
end
|
|
@@ -11,8 +11,8 @@ module AnyCable
|
|
|
11
11
|
|
|
12
12
|
private
|
|
13
13
|
|
|
14
|
-
def anycable_tracking_socket_id(&)
|
|
15
|
-
Rails.with_socket_id(request.headers[AnyCable.config.socket_id_header], &)
|
|
14
|
+
def anycable_tracking_socket_id(&block)
|
|
15
|
+
Rails.with_socket_id(request.headers[AnyCable.config.socket_id_header], &block)
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -10,6 +10,11 @@ if [ ! -f ./bin/dist/anycable-go ]; then
|
|
|
10
10
|
./bin/rails g anycable:download --version=$version --bin-path=./bin/dist
|
|
11
11
|
fi
|
|
12
12
|
|
|
13
|
+
if [ -x ./bin/dist/anycable-go ]; then
|
|
14
|
+
echo "Setting execution permission for AnyCable server"
|
|
15
|
+
chmod +x ./bin/dist/anycable-go
|
|
16
|
+
fi
|
|
17
|
+
|
|
13
18
|
curVersion=$(./bin/dist/anycable-go -v)
|
|
14
19
|
|
|
15
20
|
if [[ "$version" != "latest" ]]; then
|
|
@@ -10,10 +10,10 @@ module AnyCableRailsGenerators
|
|
|
10
10
|
|
|
11
11
|
DOCS_ROOT = "https://docs.anycable.io"
|
|
12
12
|
DEVELOPMENT_METHODS = %w[skip local docker].freeze
|
|
13
|
-
DEPLOYMENT_METHODS = %w[
|
|
13
|
+
DEPLOYMENT_METHODS = %w[skip thruster fly heroku anycable_plus].freeze
|
|
14
14
|
RPC_IMPL = %w[none grpc http].freeze
|
|
15
15
|
|
|
16
|
-
class_option :
|
|
16
|
+
class_option :development,
|
|
17
17
|
type: :string,
|
|
18
18
|
desc: "Select your development environment (options: #{DEVELOPMENT_METHODS.reverse.join(", ")})"
|
|
19
19
|
class_option :rpc,
|
|
@@ -31,37 +31,90 @@ module AnyCableRailsGenerators
|
|
|
31
31
|
say ""
|
|
32
32
|
say "👋 Welcome to AnyCable interactive installer. We'll guide you through the process of installing AnyCable for your Rails application. Buckle up!"
|
|
33
33
|
say ""
|
|
34
|
+
@todos = []
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def rpc_implementation
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if RPC_IMPL.include?(options[:rpc])
|
|
39
|
+
@rpc_impl = options[:rpc]
|
|
40
|
+
return
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if hotwire? && !custom_channels?
|
|
44
|
+
say <<~MSG
|
|
45
|
+
⚡️ Hotwire application has been detected, installing AnyCable in a standalone mode.
|
|
46
|
+
MSG
|
|
47
|
+
@rpc_impl = "none"
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if custom_channels?
|
|
52
|
+
answer = RPC_IMPL.index(options[:rpc]) || 99
|
|
53
|
+
|
|
54
|
+
unless RPC_IMPL[answer.to_i]
|
|
55
|
+
say <<~MSG
|
|
56
|
+
AnyCable connects to your Rails server to communicate with Action Cable channels either via HTTP or gRPC.
|
|
39
57
|
|
|
40
|
-
|
|
58
|
+
gRPC provides better performance and scalability but requires running
|
|
59
|
+
a separate component (a gRPC server).
|
|
60
|
+
|
|
61
|
+
HTTP is a good option for a quick start or in case your deployment platform doesn't
|
|
62
|
+
support running multiple web services (e.g., Heroku).
|
|
63
|
+
|
|
64
|
+
If you only use Action Cable for Turbo Streams, you don't need RPC at all.
|
|
65
|
+
|
|
66
|
+
Learn more from the docs 👉 #{DOCS_ROOT}/anycable-go/rpc
|
|
67
|
+
MSG
|
|
68
|
+
say ""
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
until RPC_IMPL[answer.to_i]
|
|
72
|
+
answer = ask "Which RPC implementation would you like to use? (1) gRPC, (2) HTTP, (0) None"
|
|
73
|
+
end
|
|
41
74
|
|
|
42
|
-
|
|
43
|
-
answer = ask "Do you want to use gRPC or HTTP for AnyCable RPC? (1) gRPC, (2) HTTP, (0) None"
|
|
75
|
+
@rpc_impl = RPC_IMPL[answer.to_i]
|
|
44
76
|
end
|
|
45
77
|
|
|
46
|
-
|
|
78
|
+
# no Hotwire, no custom channels
|
|
79
|
+
say "Looks like you don't have any real-time functionality yet. Let's start with a miminal AnyCable setup!"
|
|
80
|
+
@rpc_impl = "none"
|
|
47
81
|
end
|
|
48
82
|
|
|
49
83
|
def development_method
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
84
|
+
if DEVELOPMENT_METHODS.include?(options[:development])
|
|
85
|
+
@development = options[:development]
|
|
86
|
+
end
|
|
53
87
|
|
|
54
|
-
|
|
55
|
-
|
|
88
|
+
# Fast-track for local development
|
|
89
|
+
if file_exists?("bin/dev") && file_exists?("Procfile.dev")
|
|
90
|
+
@development = "local"
|
|
56
91
|
end
|
|
57
92
|
|
|
58
|
-
@
|
|
93
|
+
unless @development
|
|
94
|
+
say <<~MSG
|
|
95
|
+
You can run AnyCable server locally (recommended for most cases) or as a Docker container (in case you develop in a containerized environment).
|
|
96
|
+
|
|
97
|
+
For a local installation, we provide a convenient binstub (`bin/anycable-go`) which automatically
|
|
98
|
+
installs AnyCable server for the current platform.
|
|
99
|
+
MSG
|
|
100
|
+
say ""
|
|
101
|
+
|
|
102
|
+
answer = DEVELOPMENT_METHODS.index(options[:development]) || 99
|
|
103
|
+
|
|
104
|
+
until DEVELOPMENT_METHODS[answer.to_i]
|
|
105
|
+
answer = ask <<~MSG
|
|
106
|
+
Which way to run AnyCable server locally would you prefer? (1) Binstub, (2) Docker, (0) Skip
|
|
107
|
+
MSG
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@development = DEVELOPMENT_METHODS[answer.to_i]
|
|
111
|
+
end
|
|
59
112
|
|
|
60
|
-
case @
|
|
113
|
+
case @development
|
|
61
114
|
when "skip"
|
|
62
|
-
|
|
115
|
+
@todos << "Install AnyCable server for local development: #{DOCS_ROOT}/anycable-go/getting_started"
|
|
63
116
|
else
|
|
64
|
-
send "install_for_#{@
|
|
117
|
+
send "install_for_#{@development}"
|
|
65
118
|
end
|
|
66
119
|
end
|
|
67
120
|
|
|
@@ -80,29 +133,112 @@ module AnyCableRailsGenerators
|
|
|
80
133
|
|
|
81
134
|
say_status :info, "🤖 Running static compatibility checks with RuboCop"
|
|
82
135
|
res = run "bundle exec rubocop -r 'anycable/rails/compatibility/rubocop' --only AnyCable/InstanceVars,AnyCable/PeriodicalTimers,AnyCable/InstanceVars"
|
|
83
|
-
|
|
136
|
+
|
|
137
|
+
unless res
|
|
138
|
+
say_status :help, "⚠️ Please, take a look at the icompatibilities above and fix them"
|
|
139
|
+
|
|
140
|
+
@todos << "Fix Action Cable compatibility issues (listed above): #{DOCS_ROOT}/rails/compatibility"
|
|
141
|
+
end
|
|
84
142
|
end
|
|
85
143
|
|
|
86
144
|
def cable_url_info
|
|
87
|
-
|
|
88
|
-
|
|
145
|
+
meta_tag = norpc? ? "action_cable_with_jwt_meta_tag" : "action_cable_meta_tag"
|
|
146
|
+
|
|
147
|
+
begin
|
|
148
|
+
app_layout = nil
|
|
149
|
+
inside("app/views/layouts") do
|
|
150
|
+
next unless File.file?("application.html.erb")
|
|
151
|
+
app_layout = File.read("application.html.erb")
|
|
152
|
+
end
|
|
153
|
+
return if app_layout&.include?(meta_tag)
|
|
154
|
+
|
|
155
|
+
if norpc? && app_layout&.include?("action_cable_meta_tag")
|
|
156
|
+
gsub_file "app/views/layouts/application.html.erb", %r{^\s+<%= action_cable_meta_tag %>.*$} do |match|
|
|
157
|
+
match.sub("action_cable_meta_tag", "action_cable_with_jwt_meta_tag")
|
|
158
|
+
end
|
|
159
|
+
inform_jwt_identifiers("app/views/layouts/application.html.erb")
|
|
160
|
+
return
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
found = false
|
|
164
|
+
gsub_file "app/views/layouts/application.html.erb", %r{^\s+<%= csp_meta_tag %>.*$} do |match|
|
|
165
|
+
found = true
|
|
166
|
+
match << "\n <%= #{meta_tag} %>"
|
|
167
|
+
end
|
|
168
|
+
if found
|
|
169
|
+
inform_jwt_identifiers("app/views/layouts/application.html.erb") if norpc?
|
|
170
|
+
return
|
|
171
|
+
end
|
|
172
|
+
rescue Errno::ENOENT
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
@todos << "⚠️ Ensure you have `action_cable_meta_tag`\n" \
|
|
176
|
+
" or `action_cable_with_jwt_meta_tag` included in your HTML layout:\n" \
|
|
177
|
+
" 👉 https://docs.anycable.io/rails/getting_started"
|
|
89
178
|
end
|
|
90
179
|
|
|
91
|
-
def
|
|
180
|
+
def action_cable_engine
|
|
92
181
|
return unless application_rb
|
|
93
182
|
return if application_rb.match?(/^require\s+['"](action_cable\/engine|rails\/all)['"]/)
|
|
94
183
|
|
|
95
|
-
|
|
184
|
+
found = false
|
|
185
|
+
gsub_file "config/application.rb", %r{^require ['"]rails['"].*$} do |match|
|
|
186
|
+
found = true
|
|
187
|
+
match << %(\nrequire "action_cable/engine")
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
return if found
|
|
191
|
+
|
|
192
|
+
@todos << "⚠️ Ensure Action Cable is loaded. Add `require \"action_cable/engine\"` to your `config/application.rb` file"
|
|
96
193
|
end
|
|
97
194
|
|
|
98
|
-
def
|
|
99
|
-
|
|
195
|
+
def anycable_client
|
|
196
|
+
if hotwire? && install_js_packages
|
|
197
|
+
gsub_file "app/javascript/application.js", /^import "@hotwired\/turbo-rails".*$/, <<~JS
|
|
198
|
+
import "@hotwired/turbo"
|
|
199
|
+
import { createCable } from "@anycable/web"
|
|
200
|
+
import { start } from "@anycable/turbo-stream"
|
|
201
|
+
|
|
202
|
+
// Use extended Action Cable protocol to support reliable streams and presence
|
|
203
|
+
// See https://github.com/anycable/anycable-client
|
|
204
|
+
const cable = createCable({ protocol: 'actioncable-v1-ext-json' })
|
|
205
|
+
// Prevent frequent resubscriptions during morphing or navigation
|
|
206
|
+
start(cable, { delayedUnsubscribe: true })
|
|
207
|
+
JS
|
|
208
|
+
return
|
|
209
|
+
end
|
|
100
210
|
|
|
101
|
-
|
|
211
|
+
@todos << "⚠️ Install AnyCable JS client to use advanced features (presence, reliable streams): 👉 https://github.com/anycable/anycable-client\n"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def turbo_verifier_key
|
|
215
|
+
return unless hotwire?
|
|
216
|
+
return if application_rb.include?("config.turbo.signed_stream_verifier_key = AnyCable.config.secret")
|
|
217
|
+
|
|
218
|
+
gsub_file "config/application.rb", %r{\s+end\nend} do |match|
|
|
219
|
+
"\n\n" \
|
|
220
|
+
" # Use AnyCable secret to sign Turbo Streams\n" \
|
|
221
|
+
" # #{DOCS_ROOT}/guides/hotwire?id=rails-applications\n" \
|
|
222
|
+
" config.turbo.signed_stream_verifier_key = AnyCable.config.secret#{match}"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def deployment_method
|
|
227
|
+
@todos << "🚢 Learn how to run AnyCable in production: 👉 #{DOCS_ROOT}/deployment\n" \
|
|
228
|
+
" For the quick start, consider using AnyCable+ (https://plus.anycable.io)\n" \
|
|
229
|
+
" or AnyCable Thruster (https://github.com/anycable/thruster)"
|
|
102
230
|
end
|
|
103
231
|
|
|
104
232
|
def finish
|
|
105
|
-
say_status :info, "✅ AnyCable has been configured
|
|
233
|
+
say_status :info, "✅ AnyCable has been configured"
|
|
234
|
+
|
|
235
|
+
if @todos.any?
|
|
236
|
+
say ""
|
|
237
|
+
say "📋 Please, check the following actions required to complete the setup:\n"
|
|
238
|
+
@todos.each do |todo|
|
|
239
|
+
say "- [ ] #{todo}"
|
|
240
|
+
end
|
|
241
|
+
end
|
|
106
242
|
end
|
|
107
243
|
|
|
108
244
|
private
|
|
@@ -124,7 +260,7 @@ module AnyCableRailsGenerators
|
|
|
124
260
|
end
|
|
125
261
|
|
|
126
262
|
def local?
|
|
127
|
-
@
|
|
263
|
+
@development == "local"
|
|
128
264
|
end
|
|
129
265
|
|
|
130
266
|
def grpc?
|
|
@@ -135,6 +271,26 @@ module AnyCableRailsGenerators
|
|
|
135
271
|
@rpc_impl == "http"
|
|
136
272
|
end
|
|
137
273
|
|
|
274
|
+
def norpc?
|
|
275
|
+
@rpc_impl == "none"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def hotwire?
|
|
279
|
+
!!gemfile_lock&.match?(/^\s+turbo-rails\b/) &&
|
|
280
|
+
application_js&.match?(/^import\s+"@hotwired\/turbo/)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def custom_channels?
|
|
284
|
+
@has_custom_channels ||= begin
|
|
285
|
+
res = nil
|
|
286
|
+
in_root do
|
|
287
|
+
next unless File.directory?("app/channels")
|
|
288
|
+
res = Dir["app/channels/*_channel.rb"].any?
|
|
289
|
+
end
|
|
290
|
+
res
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
138
294
|
def gemfile_lock
|
|
139
295
|
@gemfile_lock ||= begin
|
|
140
296
|
res = nil
|
|
@@ -157,6 +313,17 @@ module AnyCableRailsGenerators
|
|
|
157
313
|
end
|
|
158
314
|
end
|
|
159
315
|
|
|
316
|
+
def application_js
|
|
317
|
+
@application_js ||= begin
|
|
318
|
+
res = nil
|
|
319
|
+
in_root do
|
|
320
|
+
next unless File.file?("app/javascript/application.js")
|
|
321
|
+
res = File.read("app/javascript/application.js")
|
|
322
|
+
end
|
|
323
|
+
res
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
160
327
|
def install_for_docker
|
|
161
328
|
say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
|
|
162
329
|
|
|
@@ -179,7 +346,7 @@ module AnyCableRailsGenerators
|
|
|
179
346
|
condition: service_started
|
|
180
347
|
|
|
181
348
|
ws:
|
|
182
|
-
image: anycable/anycable-go:1.
|
|
349
|
+
image: anycable/anycable-go:1.6
|
|
183
350
|
ports:
|
|
184
351
|
- '8080:8080'
|
|
185
352
|
- '8090'
|
|
@@ -240,6 +407,7 @@ module AnyCableRailsGenerators
|
|
|
240
407
|
generate "anycable:bin", "--version #{options[:version]}"
|
|
241
408
|
end
|
|
242
409
|
template_proc_files
|
|
410
|
+
update_bin_dev
|
|
243
411
|
true
|
|
244
412
|
end
|
|
245
413
|
|
|
@@ -252,7 +420,23 @@ module AnyCableRailsGenerators
|
|
|
252
420
|
adapter = Regexp.last_match[1]
|
|
253
421
|
next match if adapter == "test" || adapter.include?("any_cable")
|
|
254
422
|
|
|
255
|
-
match.sub(adapter,
|
|
423
|
+
match.sub(adapter, "any_cable")
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Try removing all lines contaning options for previous adapters,
|
|
427
|
+
# only keep aliases (<<:*), adapter and channel_prefix options.
|
|
428
|
+
new_clean_contents = new_contents.lines.select do |line|
|
|
429
|
+
line.match?(/^(\S|\s+adapter:|\s+channel_prefix:|\s+<<:)/) || line.match?(/^\s*$/)
|
|
430
|
+
end.join
|
|
431
|
+
|
|
432
|
+
# Verify new config
|
|
433
|
+
begin
|
|
434
|
+
clean_config = YAML.safe_load(new_clean_contents, aliases: true).deep_symbolize_keys
|
|
435
|
+
orig_config = YAML.safe_load(contents, aliases: true).deep_symbolize_keys
|
|
436
|
+
|
|
437
|
+
new_contents = new_clean_contents if clean_config.keys == orig_config.keys
|
|
438
|
+
rescue => _e
|
|
439
|
+
# something went wrong, keep older options
|
|
256
440
|
end
|
|
257
441
|
|
|
258
442
|
File.write "config/cable.yml", new_contents
|
|
@@ -270,8 +454,6 @@ module AnyCableRailsGenerators
|
|
|
270
454
|
if file_exists?(file_name)
|
|
271
455
|
update_procfile(file_name)
|
|
272
456
|
else
|
|
273
|
-
say_status :help, "💡 We recommend using Overmind to manage multiple processes in development 👉 https://github.com/DarthSim/overmind", :yellow
|
|
274
|
-
|
|
275
457
|
return if options[:skip_procfile_dev]
|
|
276
458
|
|
|
277
459
|
template file_name
|
|
@@ -282,13 +464,36 @@ module AnyCableRailsGenerators
|
|
|
282
464
|
in_root do
|
|
283
465
|
contents = File.read(file_name)
|
|
284
466
|
|
|
285
|
-
|
|
467
|
+
if grpc?
|
|
286
468
|
unless contents.match?(/^anycable:\s/)
|
|
287
469
|
append_file file_name, "anycable: bundle exec anycable\n", force: true
|
|
288
470
|
end
|
|
289
471
|
end
|
|
290
472
|
unless contents.match?(/^ws:\s/)
|
|
291
|
-
append_file file_name, "ws: bin/anycable-go", force: true
|
|
473
|
+
append_file file_name, "ws: bin/anycable-go --port 8080", force: true
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def update_bin_dev
|
|
479
|
+
unless file_exists?("bin/dev")
|
|
480
|
+
template "bin/dev"
|
|
481
|
+
chmod "bin/dev", 0755, verbose: false # rubocop:disable Style/NumericLiteralPrefix
|
|
482
|
+
|
|
483
|
+
@todos << "Now you should use bin/dev to run your application with AnyCable services"
|
|
484
|
+
return
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
in_root do
|
|
488
|
+
contents = File.read("bin/dev")
|
|
489
|
+
|
|
490
|
+
return if contents.include?("Procfile.dev")
|
|
491
|
+
|
|
492
|
+
if contents.include?(%(exec "./bin/rails"))
|
|
493
|
+
template "bin/dev", force: true
|
|
494
|
+
chmod "bin/dev", 0755, verbose: false # rubocop:disable Style/NumericLiteralPrefix
|
|
495
|
+
else
|
|
496
|
+
@todos << "Please, check your bin/dev file and ensure it runs Procfile.dev with AnyCable services"
|
|
292
497
|
end
|
|
293
498
|
end
|
|
294
499
|
end
|
|
@@ -298,5 +503,35 @@ module AnyCableRailsGenerators
|
|
|
298
503
|
return File.file?(name)
|
|
299
504
|
end
|
|
300
505
|
end
|
|
506
|
+
|
|
507
|
+
def inform_jwt_identifiers(path)
|
|
508
|
+
return unless file_exists?("app/channels/application_cable/connection.rb")
|
|
509
|
+
|
|
510
|
+
in_root do
|
|
511
|
+
contents = File.read("app/channels/application_cable/connection.rb")
|
|
512
|
+
|
|
513
|
+
if contents.match?(%r{^\s+identified_by\s})
|
|
514
|
+
@todos << "⚠️ Please, provide the correct connection identifiers to the #action_cable_with_jwt_meta_tag in #{path}. Read more: 👉 #{DOCS_ROOT}/rails/authentication?id=jwt-authentication"
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def install_js_packages
|
|
520
|
+
if file_exists?("config/importmap.rb") && file_exists?("bin/importmap")
|
|
521
|
+
run "bin/importmap pin @hotwired/turbo @anycable/web @anycable/turbo-stream"
|
|
522
|
+
true
|
|
523
|
+
elsif file_exists?("yarn.lock")
|
|
524
|
+
run "yarn add @anycable/web @anycable/turbo-stream"
|
|
525
|
+
true
|
|
526
|
+
elsif file_exists?("package-json.lock")
|
|
527
|
+
run "npm install @anycable/web @anycable/turbo-stream"
|
|
528
|
+
true
|
|
529
|
+
else
|
|
530
|
+
false
|
|
531
|
+
end
|
|
532
|
+
rescue => e
|
|
533
|
+
say_status :warn, "Failed to install JS packages: #{e.message}. Skipping..."
|
|
534
|
+
false
|
|
535
|
+
end
|
|
301
536
|
end
|
|
302
537
|
end
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
# AnyCable server configuration
|
|
1
|
+
# AnyCable server configuration.
|
|
2
2
|
#
|
|
3
3
|
# Read more at https://docs.anycable.io/anycable-go/configuration
|
|
4
4
|
|
|
5
5
|
# Public mode disables connection authentication, pub/sub streams and broadcasts verification
|
|
6
6
|
# public = false
|
|
7
7
|
|
|
8
|
-
# The application secret key
|
|
8
|
+
# The application secret key: it's used to verify JWT tokens, signed streams, etc.
|
|
9
|
+
# In development, it MUST match the value in the `config/anycable.yml`.
|
|
10
|
+
# IMPORTANT: Make sure you provided a secret in production via the ANYCABLE_SECRET env var
|
|
11
|
+
# if you use this configuration file.
|
|
9
12
|
secret = "anycable-local-secret"
|
|
10
13
|
|
|
11
|
-
#
|
|
14
|
+
# Enable AnyCable power features such as reliable streams, presence and resumable sessions.
|
|
15
|
+
# Read more:
|
|
16
|
+
# - https://docs.anycable.io/anycable-go/reliable_streams
|
|
17
|
+
# - https://docs.anycable.io/anycable-go/presence
|
|
18
|
+
presets = ["broker"]
|
|
19
|
+
|
|
20
|
+
# Broadcasting adapters for app-to-clients messages.
|
|
21
|
+
# You one of the specified adapters in the config/anycable.yml.
|
|
22
|
+
# Read more in https://docs.anycable.io/anycable-go/broadcasting.
|
|
12
23
|
<%- if redis? -%>
|
|
13
24
|
broadcast_adapters = ["http", "redisx"]
|
|
14
25
|
<%- elsif nats? -%>
|
|
@@ -17,67 +28,54 @@ broadcast_adapters = ["http", "nats"]
|
|
|
17
28
|
broadcast_adapters = ["http"]
|
|
18
29
|
<%- end -%>
|
|
19
30
|
|
|
20
|
-
# Pub/sub adapter for inter-node communication
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<%- elsif nats? -%>
|
|
24
|
-
pubsub_adapter = "nats"
|
|
25
|
-
<%- else -%>
|
|
31
|
+
# Pub/sub adapter for inter-node communication in a production cluster
|
|
32
|
+
# IMPORTANT: In production, when running AnyCable in a cluster mode,
|
|
33
|
+
# make sure to enable pub/sub adapter via the ANYCABLE_PUBSUB_ADAPTER env var
|
|
26
34
|
# pubsub_adapter = "redis" # or "nats"
|
|
27
|
-
<%- end -%>
|
|
28
|
-
|
|
29
|
-
[server]
|
|
30
|
-
host = "localhost"
|
|
31
|
-
port = 8080
|
|
32
35
|
|
|
33
36
|
[logging]
|
|
34
37
|
debug = true
|
|
35
38
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
history_ttl = 300
|
|
40
|
-
history_limit = 100
|
|
41
|
-
sessions_ttl = 300
|
|
42
|
-
|
|
39
|
+
# RPC is used to authorized subscriptions and perform channel actions.
|
|
40
|
+
# You only need it if you use custom Action Cable channel classes.
|
|
41
|
+
# Read more: https://docs.anycable.io/anycable-go/rpc
|
|
43
42
|
[rpc]
|
|
44
43
|
<%- if http_rpc? -%>
|
|
44
|
+
# IMPORTANT: In production, provide the URL of the RPC service via the ANYCABLE_RPC_HOST env variable
|
|
45
45
|
host = "http://localhost:3000/_anycable"
|
|
46
|
-
<%-
|
|
46
|
+
<%- elsif grpc? -%>
|
|
47
47
|
host = "localhost:50051"
|
|
48
|
+
<%- else -%>
|
|
49
|
+
# AnyCable is running in a standalone mode.
|
|
50
|
+
# Read more: https://docs.anycable.io/anycable-go/getting_started?id=standalone-mode-pubsub-only
|
|
51
|
+
implementation = "none"
|
|
48
52
|
<%- end -%>
|
|
49
|
-
# Specify HTTP headers that must be proxied to the RPC service
|
|
50
|
-
proxy_headers = ["cookie"]
|
|
51
|
-
# RPC concurrency (max number of concurrent RPC requests)
|
|
52
|
-
concurrency = 28
|
|
53
53
|
|
|
54
|
-
# Read more about AnyCable JWT: https://docs.anycable.io/anycable-go/jwt_identification
|
|
55
|
-
[jwt]
|
|
56
|
-
# param = "jid"
|
|
57
|
-
# force = true
|
|
58
54
|
|
|
59
|
-
#
|
|
55
|
+
# Signed and public streams configuration.
|
|
56
|
+
# Read more: https://docs.anycable.io/anycable-go/signed_streams
|
|
60
57
|
[streams]
|
|
61
58
|
# Enable public (unsigned) streams
|
|
62
59
|
# public = true
|
|
63
60
|
# Enable whispering support for pub/sub streams
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
#
|
|
61
|
+
whisper = true
|
|
62
|
+
#
|
|
63
|
+
# Authorize Turbo Streams at the AnyCable server side.
|
|
64
|
+
# IMPORTANT: Make sure your Turbo verifier key is the same as AnyCable secret.
|
|
65
|
+
# Add this line to your Rails configuration (if missing):
|
|
66
|
+
#
|
|
67
|
+
# config.turbo.signed_stream_verifier_key = AnyCable.config.secret
|
|
68
|
+
#
|
|
69
|
+
turbo = true
|
|
68
70
|
|
|
69
|
-
[redis]
|
|
70
71
|
<%- if redis? -%>
|
|
72
|
+
[redis]
|
|
73
|
+
# IMPORTANT: In production, provide Redis address via the REDIS_URL or ANYCABLE_REDIS_URL env var
|
|
71
74
|
url = "redis://localhost:6379"
|
|
72
|
-
<%- else -%>
|
|
73
|
-
# url = "redis://localhost:6379"
|
|
74
75
|
<%- end -%>
|
|
75
76
|
|
|
76
77
|
<%- if nats? -%>
|
|
77
78
|
[nats]
|
|
79
|
+
# IMPORTANT: In production, provide NATS address via the ANYCABLE_NATS_SERVERS env var
|
|
78
80
|
servers = "nats://127.0.0.1:4222"
|
|
79
81
|
<%- end -%>
|
|
80
|
-
|
|
81
|
-
[http_broadcast]
|
|
82
|
-
port = 8090
|
|
83
|
-
path = "/_broadcast"
|
|
@@ -10,6 +10,11 @@ if [ ! -f ./bin/dist/anycable-go ]; then
|
|
|
10
10
|
./bin/rails g anycable:download --version=$version --bin-path=./bin/dist
|
|
11
11
|
fi
|
|
12
12
|
|
|
13
|
+
if [ -x ./bin/dist/anycable-go ]; then
|
|
14
|
+
echo "Setting execution permission for AnyCable server"
|
|
15
|
+
chmod +x ./bin/dist/anycable-go
|
|
16
|
+
fi
|
|
17
|
+
|
|
13
18
|
curVersion=$(./bin/dist/anycable-go -v)
|
|
14
19
|
|
|
15
20
|
if [[ "$version" != "latest" ]]; then
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
|
|
3
|
+
# Default to port 3000 if not specified
|
|
4
|
+
export PORT="${PORT:-3000}"
|
|
5
|
+
|
|
6
|
+
# Let the debug gem allow remote connections,
|
|
7
|
+
# but avoid loading until `debugger` is called
|
|
8
|
+
export RUBY_DEBUG_OPEN="true"
|
|
9
|
+
export RUBY_DEBUG_LAZY="true"
|
|
10
|
+
|
|
11
|
+
if ! command -v overmind &> /dev/null; then
|
|
12
|
+
if ! gem list foreman -i --silent; then
|
|
13
|
+
echo "Installing foreman..."
|
|
14
|
+
gem install foreman
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
exec foreman start -f Procfile.dev "$@"
|
|
18
|
+
else
|
|
19
|
+
exec overmind start -f Procfile.dev "$@"
|
|
20
|
+
fi
|
|
@@ -1,57 +1,40 @@
|
|
|
1
|
-
# This file contains per-environment settings for AnyCable
|
|
1
|
+
# This file contains per-environment settings for AnyCable Rails integration
|
|
2
|
+
# (NOT the AnyCable server).
|
|
2
3
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# E.g., `rpc_host` is overridden by ANYCABLE_RPC_HOST, `debug` by ANYCABLE_DEBUG etc.
|
|
6
|
-
#
|
|
7
|
-
# Note that AnyCable recognizes REDIS_URL env variable for Redis pub/sub adapter. If you want to
|
|
8
|
-
# use another Redis instance for AnyCable, provide ANYCABLE_REDIS_URL variable.
|
|
9
|
-
#
|
|
10
|
-
# Read more about AnyCable configuration here: <%= DOCS_ROOT %>/ruby/configuration
|
|
4
|
+
# The settings specified here would be overriden by the corresponding values in Rails credentials (e.g., `anycable.secret`), or environment variables (e.g., ANYCABLE_SECRET), or any other configuration
|
|
5
|
+
# sources supported by Anyway Config (https://github.com/palkan/anyway_config).
|
|
11
6
|
#
|
|
12
7
|
default: &default
|
|
13
8
|
# Turn on/off access logs ("Started..." and "Finished...")
|
|
14
9
|
access_logs_disabled: false
|
|
15
10
|
# Whether to enable gRPC level logging or not
|
|
16
11
|
log_grpc: false
|
|
17
|
-
|
|
18
|
-
# Use Redis Streams to broadcast messages to AnyCable server
|
|
19
|
-
broadcast_adapter: redisx
|
|
20
|
-
<%- elsif nats? -%>
|
|
21
|
-
# Use NATS to broadcast messages to AnyCable server
|
|
22
|
-
broadcast_adapter: nats
|
|
23
|
-
<%- else -%>
|
|
24
|
-
# Use HTTP broadcaster
|
|
12
|
+
# Use HTTP broadcaster by default
|
|
25
13
|
broadcast_adapter: http
|
|
26
|
-
http_broadcast_url: "http://localhost:8090/_broadcast"
|
|
27
|
-
<%- end -%>
|
|
28
|
-
<%- if redis? -%>
|
|
29
|
-
# You can use REDIS_URL env var to configure Redis URL.
|
|
30
|
-
# Localhost is used by default.
|
|
31
|
-
# redis_url: "redis://localhost:6379/1"
|
|
32
|
-
# Use the same channel name for WebSocket server, e.g.:
|
|
33
|
-
# $ anycable-go --redis_channel="__anycable__"
|
|
34
|
-
# redis_channel: "__anycable__"
|
|
35
|
-
<%- end -%>
|
|
36
14
|
<%- if http_rpc? -%>
|
|
37
|
-
# Use HTTP RPC mounted at the
|
|
38
|
-
# Read more
|
|
39
|
-
|
|
15
|
+
# Use HTTP RPC mounted at the "/_anycable" path of your web server
|
|
16
|
+
# Read more: https://docs.anycable.io/anycable-go/rpc?id=rpc-over-http
|
|
17
|
+
http_rpc: true
|
|
40
18
|
<%- end -%>
|
|
41
|
-
#
|
|
19
|
+
# This values MUST be the same as in your AnyCable server configuration
|
|
42
20
|
secret: "anycable-local-secret"
|
|
21
|
+
# WebSocket endpoint of your AnyCable server for clients to connect to
|
|
22
|
+
# IMPORTANT: Make sure you have the `action_cable_meta_tag` in your HTML layout
|
|
23
|
+
# to propogate this value to the client app.
|
|
24
|
+
# Make sure your AnyCable server runs on port 8080 in development.
|
|
25
|
+
websocket_url: "ws://localhost:8080/cable"
|
|
43
26
|
|
|
44
27
|
development:
|
|
45
28
|
<<: *default
|
|
46
|
-
# WebSocket endpoint of your AnyCable server for clients to connect to
|
|
47
|
-
# Make sure you have the `action_cable_meta_tag` in your HTML layout
|
|
48
|
-
# to propogate this value to the client app
|
|
49
|
-
websocket_url: "ws://localhost:8080/cable"
|
|
50
29
|
|
|
30
|
+
# Read more about running AnyCable in test env:
|
|
31
|
+
# https://docs.anycable.io/rails/getting_started?id=testing-with-anycable
|
|
51
32
|
test:
|
|
52
33
|
<<: *default
|
|
53
34
|
|
|
54
35
|
production:
|
|
55
36
|
<<: *default
|
|
37
|
+
# Use ANYCABLE_WEBSOCKET_URL and ANYCABLE_SECRET environment variables
|
|
38
|
+
# or credentials or whatever.
|
|
56
39
|
websocket_url: ~
|
|
57
40
|
secret: ~
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
# Make it possible to switch adapters by passing the ACTION_CABLE_ADAPTER env variable.
|
|
2
|
-
# For example, you can use it fallback to the standard Action Cable in staging/review
|
|
3
|
-
# environments (by setting `ACTION_CABLE_ADAPTER=redis`).
|
|
4
1
|
development:
|
|
5
|
-
adapter:
|
|
2
|
+
adapter: any_cable
|
|
6
3
|
|
|
7
4
|
test:
|
|
8
5
|
adapter: test
|
|
9
6
|
|
|
10
7
|
production:
|
|
11
|
-
adapter:
|
|
8
|
+
adapter: any_cable
|
metadata
CHANGED
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: anycable-rails-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- palkan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: anycable-core
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: 1.6.0.rc.1
|
|
20
|
-
- - "<"
|
|
17
|
+
- - "~>"
|
|
21
18
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: 1.
|
|
19
|
+
version: 1.6.0
|
|
23
20
|
type: :runtime
|
|
24
21
|
prerelease: false
|
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
23
|
requirements:
|
|
27
|
-
- - "
|
|
24
|
+
- - "~>"
|
|
28
25
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 1.6.0
|
|
30
|
-
- - "<"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: 1.7.0
|
|
26
|
+
version: 1.6.0
|
|
33
27
|
- !ruby/object:Gem::Dependency
|
|
34
28
|
name: actioncable
|
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -126,6 +120,7 @@ files:
|
|
|
126
120
|
- lib/generators/anycable/setup/templates/Procfile.dev.tt
|
|
127
121
|
- lib/generators/anycable/setup/templates/anycable.toml.tt
|
|
128
122
|
- lib/generators/anycable/setup/templates/bin/anycable-go.tt
|
|
123
|
+
- lib/generators/anycable/setup/templates/bin/dev
|
|
129
124
|
- lib/generators/anycable/setup/templates/config/anycable.yml.tt
|
|
130
125
|
- lib/generators/anycable/setup/templates/config/cable.yml.tt
|
|
131
126
|
- lib/generators/anycable/with_os_helpers.rb
|
|
@@ -150,9 +145,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
150
145
|
version: '2.7'
|
|
151
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
147
|
requirements:
|
|
153
|
-
- - "
|
|
148
|
+
- - ">="
|
|
154
149
|
- !ruby/object:Gem::Version
|
|
155
|
-
version:
|
|
150
|
+
version: '0'
|
|
156
151
|
requirements: []
|
|
157
152
|
rubygems_version: 3.4.19
|
|
158
153
|
signing_key:
|