anycable-rails-core 1.6.0 → 1.6.2

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: ae5f5e00ae334a317de5b59685ff57de668c2f102125f48bc8309a7330787950
4
+ data.tar.gz: f15f057412a3c0a0dbf759bd8c8d47abee1bee60a9b68ca62983ced577ea9397
5
5
  SHA512:
6
- metadata.gz: d48105a43d0693bece056fe51d6367d4b4c51ff4182911e2352d07f2e461e75324d7015a512e5146064c02cf40f7546420f4f60968fed0fd819cb461bb2076d0
7
- data.tar.gz: d7859990689306126edae8e2d412ed4c6e9c4fcb135ae4db165cf02a1bcfee8ab2643ee465fbaceb4db53a2b7c2f1e0225a3ddbfaa52721012baa04683b5cbd0
6
+ metadata.gz: 8b780c7ca124e8f84d708e94ec78a27b50e9b36cbf524264cea3582bb171b037e40f4f92d8292f115810b988dfe93d45a942c779550943484bfad9810c1865f3
7
+ data.tar.gz: 916571c822234a92ec9bbb1839c06c8a0cc9a6f434c370c0b9bccd6486a450c2dc2ec88e27069d3b14bff093e245d0a83e6aed71fc7bcd1f20b298ae479edde7
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## main
4
4
 
5
+ ## 1.6.2 (2026-04-02)
6
+
7
+ - Upgrade setup generator. ([@palkan][])
8
+
9
+ ## 1.6.1 (2025-12-18)
10
+
11
+ - Ensure `bin/dist/anycable-go` has execution permission as part of `bin/anycable-go` ([@dmorgan-fa][])
12
+
5
13
  ## 1.6.0 (2025-03-18)
6
14
 
7
15
  - 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 @@ module AnyCable
9
9
  @_streams
10
10
  @parameter_filter
11
11
  @whisper_stream
12
+ @__sid__
12
13
  ]
13
14
 
14
15
  ActionCable::Channel::Base.prepend(Module.new do
@@ -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.2"
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
@@ -9,11 +9,11 @@ module AnyCableRailsGenerators
9
9
  source_root File.expand_path("templates", __dir__)
10
10
 
11
11
  DOCS_ROOT = "https://docs.anycable.io"
12
- DEVELOPMENT_METHODS = %w[skip local docker].freeze
13
- DEPLOYMENT_METHODS = %w[other fly heroku anycable_plus].freeze
12
+ DEVELOPMENT_METHODS = %w[skip bin docker].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,85 @@ 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_status :info, <<~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 ""
56
+ say <<~MSG
57
+ AnyCable connects to your Rails server to communicate with Action Cable channels either via HTTP or gRPC.
39
58
 
40
- answer = RPC_IMPL.index(options[:rpc]) || 99
59
+ gRPC provides better performance and scalability but requires running
60
+ a separate component (a gRPC server).
41
61
 
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"
62
+ HTTP is a good option for a quick start or in case your deployment platform doesn't
63
+ support running multiple web services (e.g., Heroku).
64
+
65
+ If you only use Action Cable for Turbo Streams, you don't need RPC at all.
66
+
67
+ Learn more from the docs 👉 #{DOCS_ROOT}/anycable-go/rpc
68
+ MSG
69
+ say ""
70
+ end
71
+
72
+ answer = ask "Which RPC implementation would you like to use?", limited_to: %w[grpc http none], default: "grpc"
73
+
74
+ @rpc_impl = answer
75
+ return
44
76
  end
45
77
 
46
- @rpc_impl = RPC_IMPL[answer.to_i]
78
+ # no Hotwire, no custom channels
79
+ say_status :info, "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 = "bin"
56
91
  end
57
92
 
58
- @devenv = DEVELOPMENT_METHODS[answer.to_i]
93
+ unless @development
94
+ say ""
95
+ say <<~MSG
96
+ You can run AnyCable server locally (recommended for most cases) or as a Docker container (in case you develop in a containerized environment).
97
+
98
+ For a local installation, we provide a convenient binstub (`bin/anycable-go`) which automatically
99
+ installs AnyCable server for the current platform.
100
+ MSG
101
+ say ""
102
+
103
+ answer = ask "Which way to run AnyCable server locally would you prefer?", limited_to: %w[bin docker skip], default: "bin"
104
+
105
+ @development = answer
106
+ end
59
107
 
60
- case @devenv
108
+ case @development
61
109
  when "skip"
62
- say_status :help, "⚠️ Please, read this guide on how to install AnyCable server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
110
+ @todos << "Install AnyCable server for local development: #{DOCS_ROOT}/anycable-go/getting_started"
63
111
  else
64
- send "install_for_#{@devenv}"
112
+ send "install_for_#{@development}"
65
113
  end
66
114
  end
67
115
 
@@ -80,29 +128,112 @@ module AnyCableRailsGenerators
80
128
 
81
129
  say_status :info, "🤖 Running static compatibility checks with RuboCop"
82
130
  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
131
+
132
+ unless res
133
+ say_status :help, "⚠️ Please, take a look at the incompatibilities above and fix them"
134
+
135
+ @todos << "Fix Action Cable compatibility issues (listed above): #{DOCS_ROOT}/rails/compatibility"
136
+ end
84
137
  end
85
138
 
86
139
  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"
140
+ meta_tag = norpc? ? "action_cable_with_jwt_meta_tag" : "action_cable_meta_tag"
141
+
142
+ begin
143
+ app_layout = nil
144
+ inside("app/views/layouts") do
145
+ next unless File.file?("application.html.erb")
146
+ app_layout = File.read("application.html.erb")
147
+ end
148
+ return if app_layout&.include?(meta_tag)
149
+
150
+ if norpc? && app_layout&.include?("action_cable_meta_tag")
151
+ gsub_file "app/views/layouts/application.html.erb", %r{^\s+<%= action_cable_meta_tag %>.*$} do |match|
152
+ match.sub("action_cable_meta_tag", "action_cable_with_jwt_meta_tag")
153
+ end
154
+ inform_jwt_identifiers("app/views/layouts/application.html.erb")
155
+ return
156
+ end
157
+
158
+ found = false
159
+ gsub_file "app/views/layouts/application.html.erb", %r{^\s+<%= csp_meta_tag %>.*$} do |match|
160
+ found = true
161
+ match << "\n <%= #{meta_tag} %>"
162
+ end
163
+ if found
164
+ inform_jwt_identifiers("app/views/layouts/application.html.erb") if norpc?
165
+ return
166
+ end
167
+ rescue Errno::ENOENT
168
+ end
169
+
170
+ @todos << "⚠️ Ensure you have `action_cable_meta_tag`\n" \
171
+ " or `action_cable_with_jwt_meta_tag` included in your HTML layout:\n" \
172
+ " 👉 https://docs.anycable.io/rails/getting_started"
89
173
  end
90
174
 
91
- def cable_engine_warning
175
+ def action_cable_engine
92
176
  return unless application_rb
93
177
  return if application_rb.match?(/^require\s+['"](action_cable\/engine|rails\/all)['"]/)
94
178
 
95
- say_status :help, "⚠️ Ensure Action Cable is loaded.\nAdd `require \"action_cable/engine\"` to your `config/application.rb`."
179
+ found = false
180
+ gsub_file "config/application.rb", %r{^require ['"]rails['"].*$} do |match|
181
+ found = true
182
+ match << %(\nrequire "action_cable/engine")
183
+ end
184
+
185
+ return if found
186
+
187
+ @todos << "⚠️ Ensure Action Cable is loaded. Add `require \"action_cable/engine\"` to your `config/application.rb` file"
96
188
  end
97
189
 
98
- def deployment_method
99
- say_status :info, "🚢 See our deployment guide to learn how to run AnyCable in production 👉 #{DOCS_ROOT}/deployment"
190
+ def anycable_client
191
+ if hotwire? && install_js_packages
192
+ gsub_file "app/javascript/application.js", /^import "@hotwired\/turbo-rails".*$/, <<~JS
193
+ import "@hotwired/turbo"
194
+ import { createCable } from "@anycable/web"
195
+ import { start } from "@anycable/turbo-stream"
196
+
197
+ // Use extended Action Cable protocol to support reliable streams and presence
198
+ // See https://github.com/anycable/anycable-client
199
+ const cable = createCable({ protocol: 'actioncable-v1-ext-json' })
200
+ // Prevent frequent resubscriptions during morphing or navigation
201
+ start(cable, { delayedUnsubscribe: true })
202
+ JS
203
+ return
204
+ end
205
+
206
+ @todos << "⚠️ Install AnyCable JS client to use advanced features (presence, reliable streams): 👉 https://github.com/anycable/anycable-client\n"
207
+ end
100
208
 
101
- say_status :info, "Check out AnyCable+, our hosted AnyCable solution: https://plus.anycable.io"
209
+ def turbo_verifier_key
210
+ return unless hotwire?
211
+ return if application_rb.include?("config.turbo.signed_stream_verifier_key = AnyCable.config.secret")
212
+
213
+ gsub_file "config/application.rb", %r{\s+end\nend} do |match|
214
+ "\n\n" \
215
+ " # Use AnyCable secret to sign Turbo Streams\n" \
216
+ " # #{DOCS_ROOT}/guides/hotwire?id=rails-applications\n" \
217
+ " config.turbo.signed_stream_verifier_key = AnyCable.config.secret#{match}"
218
+ end
219
+ end
220
+
221
+ def deployment_method
222
+ @todos << "🚢 Learn how to run AnyCable in production: 👉 #{DOCS_ROOT}/deployment\n" \
223
+ " For the quick start, consider using AnyCable+ (https://plus.anycable.io)\n" \
224
+ " or AnyCable Thruster (https://github.com/anycable/thruster)"
102
225
  end
103
226
 
104
227
  def finish
105
- say_status :info, "✅ AnyCable has been configured successfully!"
228
+ say_status :info, "✅ AnyCable has been configured"
229
+
230
+ if @todos.any?
231
+ say ""
232
+ say "📋 Please, check the following actions required to complete the setup:\n"
233
+ @todos.each do |todo|
234
+ say "- [ ] #{todo}"
235
+ end
236
+ end
106
237
  end
107
238
 
108
239
  private
@@ -124,7 +255,7 @@ module AnyCableRailsGenerators
124
255
  end
125
256
 
126
257
  def local?
127
- @devenv == "local"
258
+ @development == "local"
128
259
  end
129
260
 
130
261
  def grpc?
@@ -135,6 +266,26 @@ module AnyCableRailsGenerators
135
266
  @rpc_impl == "http"
136
267
  end
137
268
 
269
+ def norpc?
270
+ @rpc_impl == "none"
271
+ end
272
+
273
+ def hotwire?
274
+ !!gemfile_lock&.match?(/^\s+turbo-rails\b/) &&
275
+ application_js&.match?(/^import\s+"@hotwired\/turbo/)
276
+ end
277
+
278
+ def custom_channels?
279
+ @has_custom_channels ||= begin
280
+ res = nil
281
+ in_root do
282
+ next unless File.directory?("app/channels")
283
+ res = Dir["app/channels/*_channel.rb"].any?
284
+ end
285
+ res
286
+ end
287
+ end
288
+
138
289
  def gemfile_lock
139
290
  @gemfile_lock ||= begin
140
291
  res = nil
@@ -157,6 +308,17 @@ module AnyCableRailsGenerators
157
308
  end
158
309
  end
159
310
 
311
+ def application_js
312
+ @application_js ||= begin
313
+ res = nil
314
+ in_root do
315
+ next unless File.file?("app/javascript/application.js")
316
+ res = File.read("app/javascript/application.js")
317
+ end
318
+ res
319
+ end
320
+ end
321
+
160
322
  def install_for_docker
161
323
  say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
162
324
 
@@ -179,7 +341,7 @@ module AnyCableRailsGenerators
179
341
  condition: service_started
180
342
 
181
343
  ws:
182
- image: anycable/anycable-go:1.5
344
+ image: anycable/anycable-go:1.6
183
345
  ports:
184
346
  - '8080:8080'
185
347
  - '8090'
@@ -235,11 +397,12 @@ module AnyCableRailsGenerators
235
397
  end
236
398
  end
237
399
 
238
- def install_for_local
400
+ def install_for_bin
239
401
  unless file_exists?("bin/anycable-go")
240
402
  generate "anycable:bin", "--version #{options[:version]}"
241
403
  end
242
404
  template_proc_files
405
+ update_bin_dev
243
406
  true
244
407
  end
245
408
 
@@ -252,7 +415,23 @@ module AnyCableRailsGenerators
252
415
  adapter = Regexp.last_match[1]
253
416
  next match if adapter == "test" || adapter.include?("any_cable")
254
417
 
255
- match.sub(adapter, %(<%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>))
418
+ match.sub(adapter, "any_cable")
419
+ end
420
+
421
+ # Try removing all lines contaning options for previous adapters,
422
+ # only keep aliases (<<:*), adapter and channel_prefix options.
423
+ new_clean_contents = new_contents.lines.select do |line|
424
+ line.match?(/^(\S|\s+adapter:|\s+channel_prefix:|\s+<<:)/) || line.match?(/^\s*$/)
425
+ end.join
426
+
427
+ # Verify new config
428
+ begin
429
+ clean_config = YAML.safe_load(new_clean_contents, aliases: true).deep_symbolize_keys
430
+ orig_config = YAML.safe_load(contents, aliases: true).deep_symbolize_keys
431
+
432
+ new_contents = new_clean_contents if clean_config.keys == orig_config.keys
433
+ rescue => _e
434
+ # something went wrong, keep older options
256
435
  end
257
436
 
258
437
  File.write "config/cable.yml", new_contents
@@ -270,8 +449,6 @@ module AnyCableRailsGenerators
270
449
  if file_exists?(file_name)
271
450
  update_procfile(file_name)
272
451
  else
273
- say_status :help, "💡 We recommend using Overmind to manage multiple processes in development 👉 https://github.com/DarthSim/overmind", :yellow
274
-
275
452
  return if options[:skip_procfile_dev]
276
453
 
277
454
  template file_name
@@ -282,13 +459,36 @@ module AnyCableRailsGenerators
282
459
  in_root do
283
460
  contents = File.read(file_name)
284
461
 
285
- unless http_rpc?
462
+ if grpc?
286
463
  unless contents.match?(/^anycable:\s/)
287
464
  append_file file_name, "anycable: bundle exec anycable\n", force: true
288
465
  end
289
466
  end
290
467
  unless contents.match?(/^ws:\s/)
291
- append_file file_name, "ws: bin/anycable-go", force: true
468
+ append_file file_name, "ws: bin/anycable-go --port 8080", force: true
469
+ end
470
+ end
471
+ end
472
+
473
+ def update_bin_dev
474
+ unless file_exists?("bin/dev")
475
+ template "bin/dev"
476
+ chmod "bin/dev", 0755, verbose: false # rubocop:disable Style/NumericLiteralPrefix
477
+
478
+ @todos << "Now you should use bin/dev to run your application with AnyCable services"
479
+ return
480
+ end
481
+
482
+ in_root do
483
+ contents = File.read("bin/dev")
484
+
485
+ return if contents.include?("Procfile.dev")
486
+
487
+ if contents.include?(%(exec "./bin/rails"))
488
+ template "bin/dev", force: true
489
+ chmod "bin/dev", 0755, verbose: false # rubocop:disable Style/NumericLiteralPrefix
490
+ else
491
+ @todos << "Please, check your bin/dev file and ensure it runs Procfile.dev with AnyCable services"
292
492
  end
293
493
  end
294
494
  end
@@ -298,5 +498,32 @@ module AnyCableRailsGenerators
298
498
  return File.file?(name)
299
499
  end
300
500
  end
501
+
502
+ def inform_jwt_identifiers(path)
503
+ return unless file_exists?("app/channels/application_cable/connection.rb")
504
+
505
+ in_root do
506
+ contents = File.read("app/channels/application_cable/connection.rb")
507
+
508
+ if contents.match?(%r{^\s+identified_by\s})
509
+ @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"
510
+ end
511
+ end
512
+ end
513
+
514
+ def install_js_packages
515
+ if file_exists?("config/importmap.rb") && file_exists?("bin/importmap")
516
+ run "bin/importmap pin @hotwired/turbo @anycable/web @anycable/turbo-stream"
517
+ elsif file_exists?("yarn.lock")
518
+ run "yarn add @anycable/web @anycable/turbo-stream"
519
+ elsif file_exists?("package-json.lock")
520
+ run "npm install @anycable/web @anycable/turbo-stream"
521
+ else
522
+ false
523
+ end
524
+ rescue => e
525
+ say_status :warn, "Failed to install JS packages: #{e.message}. Skipping..."
526
+ false
527
+ end
301
528
  end
302
529
  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,17 @@
1
+ #!/usr/bin/env sh
2
+
3
+ # Let the debug gem allow remote connections,
4
+ # but avoid loading until `debugger` is called
5
+ export RUBY_DEBUG_OPEN="true"
6
+ export RUBY_DEBUG_LAZY="true"
7
+
8
+ if ! command -v overmind &> /dev/null; then
9
+ if ! gem list foreman -i --silent; then
10
+ echo "Installing foreman..."
11
+ gem install foreman
12
+ fi
13
+
14
+ exec foreman start -f Procfile.dev "$@"
15
+ else
16
+ exec overmind start -f Procfile.dev "$@"
17
+ 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.2
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: 2026-04-02 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
@@ -133,6 +134,7 @@ metadata:
133
134
  homepage_uri: https://anycable.io/
134
135
  source_code_uri: http://github.com/anycable/anycable-rails
135
136
  funding_uri: https://github.com/sponsors/anycable
137
+ rubygems_mfa_required: 'true'
136
138
  post_install_message:
137
139
  rdoc_options: []
138
140
  require_paths: