anycable-rails-core 1.6.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98df74fbee0c55c64bfddfba7f3523739e53585d069aba4c38e473a12a799d42
4
- data.tar.gz: b2df57d980697a1ed0f9d6ba0b8ea46b3f8af00214c6965c18fb1ab645fde209
3
+ metadata.gz: e1dc965588a4b4f798a031a398319683240bd3319f8e9ce843bc3c0a830e00b2
4
+ data.tar.gz: 4e0739d26c204dbb9c04db0f6a6772809e0578b9bdc8a5f95509511351c18fff
5
5
  SHA512:
6
- metadata.gz: d48105a43d0693bece056fe51d6367d4b4c51ff4182911e2352d07f2e461e75324d7015a512e5146064c02cf40f7546420f4f60968fed0fd819cb461bb2076d0
7
- data.tar.gz: d7859990689306126edae8e2d412ed4c6e9c4fcb135ae4db165cf02a1bcfee8ab2643ee465fbaceb4db53a2b7c2f1e0225a3ddbfaa52721012baa04683b5cbd0
6
+ metadata.gz: 030a59929994059d44a1db9d3f5a9696c8443cd11a4d54ffb2538fcf0becc1d4bcc473e80610dc6fd4f94508f3c744ea83d348da91442eb1f03806ae4f886d5f
7
+ data.tar.gz: aae67b4149566140a102932a22c7716b181745d9b5df92edd60480a5567b9a144857f8f1e23fcc540929bf5163ef0678c753ef4abccd302ed269d731a5e2852a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## main
4
4
 
5
+ - Upgrade setup generator. ([@palkan][])
6
+
7
+ - Ensure `bin/dist/anycable-go` has execution permission as part of `bin/anycable-go` ([@dmorgan-fa][])
8
+
5
9
  ## 1.6.0 (2025-03-18)
6
10
 
7
11
  - Update `anycable:download` generator to support v1.6+. ([@palkan][])
@@ -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].safe_constantize
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
@@ -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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rails
5
- VERSION = "1.6.0"
5
+ VERSION = "1.6.1"
6
6
  end
7
7
  end
@@ -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[other fly heroku anycable_plus].freeze
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 :devenv,
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
- say "AnyCable connects to your Rails server to communicate with Action Cable channels (via RPC API). Learn more from the docs 👉 #{DOCS_ROOT}/anycable-go/rpc"
38
- say ""
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
- answer = RPC_IMPL.index(options[:rpc]) || 99
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
- until RPC_IMPL[answer.to_i]
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
- @rpc_impl = RPC_IMPL[answer.to_i]
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
- answer = DEVELOPMENT_METHODS.index(options[:devenv]) || 99
51
-
52
- say ""
84
+ if DEVELOPMENT_METHODS.include?(options[:development])
85
+ @development = options[:development]
86
+ end
53
87
 
54
- until DEVELOPMENT_METHODS[answer.to_i]
55
- answer = ask "Do you want to run AnyCable server (anycable-go) locally or as a Docker container? (1) Local, (2) Docker, (0) Skip"
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
- @devenv = DEVELOPMENT_METHODS[answer.to_i]
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 @devenv
113
+ case @development
61
114
  when "skip"
62
- say_status :help, "⚠️ Please, read this guide on how to install AnyCable server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
115
+ @todos << "Install AnyCable server for local development: #{DOCS_ROOT}/anycable-go/getting_started"
63
116
  else
64
- send "install_for_#{@devenv}"
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
- say_status :help, "⚠️ Please, take a look at the icompatibilities above and fix them. See #{DOCS_ROOT}/rails/compatibility" unless res
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
- say_status :help, "⚠️ If you're using JS client make sure you have " \
88
- "`action_cable_meta_tag` or `action_cable_with_jwt_meta_tag` included in your HTML layout"
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 cable_engine_warning
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
- say_status :help, "⚠️ Ensure Action Cable is loaded.\nAdd `require \"action_cable/engine\"` to your `config/application.rb`."
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 deployment_method
99
- say_status :info, "🚢 See our deployment guide to learn how to run AnyCable in production 👉 #{DOCS_ROOT}/deployment"
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
- say_status :info, "Check out AnyCable+, our hosted AnyCable solution: https://plus.anycable.io"
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 successfully!"
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
- @devenv == "local"
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.5
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, %(<%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>))
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
- unless http_rpc?
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,5 +1,5 @@
1
- web: bin/rails s
1
+ web: bin/rails s -p 3000
2
2
  <%- unless http_rpc? -%>
3
3
  anycable: bundle exec anycable
4
4
  <%- end -%>
5
- ws: bin/anycable-go
5
+ ws: bin/anycable-go --port 8080
@@ -1,14 +1,25 @@
1
- # AnyCable server configuration (development).
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
- # Broadcasting adapters for app-to-clients messages
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
- <%- if redis? -%>
22
- pubsub_adapter = "redis"
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
- # Read more about broker: https://docs.anycable.io/anycable-go/reliable_streams
37
- [broker]
38
- adapter = "memory"
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
- <%- else -%>
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
- # Read more about AnyCable signed streams: https://docs.anycable.io/anycable-go/signed_streams
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
- # whisper = true
65
- pubsub_channel = "$pubsub"
66
- # turbo = true
67
- # cable_ready = true
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
- # Since AnyCable config is based on anyway_config (https://github.com/palkan/anyway_config), all AnyCable settings
4
- # can be set or overridden through the corresponding environment variables.
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
- <%- if redis? -%>
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 specified path of your web server
38
- # Read more about AnyCable RPC: <%= DOCS_ROOT %>/anycable-go/rpc
39
- http_rpc_mount_path: "/_anycable"
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
- # Must be the same as in your AnyCable server config
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: <%%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>
2
+ adapter: any_cable
6
3
 
7
4
  test:
8
5
  adapter: test
9
6
 
10
7
  production:
11
- adapter: <%%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>
8
+ adapter: any_cable
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable-rails-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
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-03-18 00:00:00.000000000 Z
11
+ date: 2025-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anycable-core
@@ -120,6 +120,7 @@ files:
120
120
  - lib/generators/anycable/setup/templates/Procfile.dev.tt
121
121
  - lib/generators/anycable/setup/templates/anycable.toml.tt
122
122
  - lib/generators/anycable/setup/templates/bin/anycable-go.tt
123
+ - lib/generators/anycable/setup/templates/bin/dev
123
124
  - lib/generators/anycable/setup/templates/config/anycable.yml.tt
124
125
  - lib/generators/anycable/setup/templates/config/cable.yml.tt
125
126
  - lib/generators/anycable/with_os_helpers.rb