anycable-rails-core 1.5.3 → 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 +4 -4
- data/CHANGELOG.md +31 -1
- data/README.md +10 -75
- data/lib/anycable/rails/action_cable_ext/channel.rb +20 -11
- data/lib/anycable/rails/action_cable_ext/remote_connections.rb +4 -14
- data/lib/anycable/rails/channel/presence.rb +36 -0
- data/lib/anycable/rails/channel.rb +9 -0
- data/lib/anycable/rails/compatibility/rubocop.rb +2 -25
- data/lib/anycable/rails/compatibility.rb +2 -0
- data/lib/anycable/rails/config.rb +2 -0
- data/lib/anycable/rails/connection.rb +62 -33
- data/lib/anycable/rails/connection_factory.rb +20 -1
- data/lib/anycable/rails/connections/persistent_session.rb +5 -0
- data/lib/anycable/rails/connections/serializable_identification.rb +19 -0
- data/lib/anycable/rails/connections/session_proxy.rb +2 -2
- data/lib/anycable/rails/ext/jwt.rb +1 -1
- data/lib/anycable/rails/ext/signed_streams.rb +21 -0
- data/lib/anycable/rails/ext/whisper.rb +34 -0
- data/lib/anycable/rails/ext.rb +4 -1
- data/lib/anycable/rails/helper.rb +2 -2
- data/lib/anycable/rails/next/action_cable_ext/channel.rb +36 -0
- data/lib/anycable/rails/next/action_cable_ext/connection.rb +29 -0
- data/lib/anycable/rails/next/connection/persistent_session.rb +39 -0
- data/lib/anycable/rails/next/connection.rb +226 -0
- data/lib/anycable/rails/railtie.rb +4 -8
- data/lib/anycable/rails/{compatibility/rubocop → rubocop}/cops/anycable/instance_vars.rb +1 -1
- data/lib/anycable/rails/{compatibility/rubocop → rubocop}/cops/anycable/periodical_timers.rb +3 -8
- data/lib/anycable/rails/{compatibility/rubocop → rubocop}/cops/anycable/stream_from.rb +3 -5
- data/lib/anycable/rails/rubocop.rb +27 -0
- data/lib/anycable/rails/version.rb +1 -1
- data/lib/anycable/rails.rb +2 -1
- data/lib/generators/anycable/bin/templates/bin/anycable-go.tt +5 -0
- data/lib/generators/anycable/download/download_generator.rb +16 -5
- data/lib/generators/anycable/setup/setup_generator.rb +280 -38
- data/lib/generators/anycable/setup/templates/Procfile.dev.tt +2 -2
- data/lib/generators/anycable/setup/templates/anycable.toml.tt +81 -0
- data/lib/generators/anycable/setup/templates/bin/anycable-go.tt +5 -0
- data/lib/generators/anycable/setup/templates/bin/dev +17 -0
- data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +20 -33
- data/lib/generators/anycable/setup/templates/config/cable.yml.tt +2 -5
- metadata +27 -16
- data/lib/anycable/rails/action_cable_ext/signed_streams.rb +0 -31
- /data/lib/anycable/rails/{compatibility/rubocop → rubocop}/config/default.yml +0 -0
|
@@ -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
|
|
13
|
-
DEPLOYMENT_METHODS = %w[
|
|
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 :
|
|
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
|
-
|
|
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_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.
|
|
58
|
+
|
|
59
|
+
gRPC provides better performance and scalability but requires running
|
|
60
|
+
a separate component (a gRPC server).
|
|
39
61
|
|
|
40
|
-
|
|
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).
|
|
41
64
|
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = "bin"
|
|
56
91
|
end
|
|
57
92
|
|
|
58
|
-
@
|
|
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"
|
|
59
104
|
|
|
60
|
-
|
|
105
|
+
@development = answer
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
case @development
|
|
61
109
|
when "skip"
|
|
62
|
-
|
|
110
|
+
@todos << "Install AnyCable server for local development: #{DOCS_ROOT}/anycable-go/getting_started"
|
|
63
111
|
else
|
|
64
|
-
send "install_for_#{@
|
|
112
|
+
send "install_for_#{@development}"
|
|
65
113
|
end
|
|
66
114
|
end
|
|
67
115
|
|
|
@@ -70,6 +118,8 @@ module AnyCableRailsGenerators
|
|
|
70
118
|
template "anycable.yml"
|
|
71
119
|
end
|
|
72
120
|
|
|
121
|
+
template "anycable.toml"
|
|
122
|
+
|
|
73
123
|
update_cable_yml
|
|
74
124
|
end
|
|
75
125
|
|
|
@@ -78,22 +128,112 @@ module AnyCableRailsGenerators
|
|
|
78
128
|
|
|
79
129
|
say_status :info, "🤖 Running static compatibility checks with RuboCop"
|
|
80
130
|
res = run "bundle exec rubocop -r 'anycable/rails/compatibility/rubocop' --only AnyCable/InstanceVars,AnyCable/PeriodicalTimers,AnyCable/InstanceVars"
|
|
81
|
-
|
|
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
|
|
82
137
|
end
|
|
83
138
|
|
|
84
139
|
def cable_url_info
|
|
85
|
-
|
|
86
|
-
|
|
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"
|
|
87
173
|
end
|
|
88
174
|
|
|
89
|
-
def
|
|
90
|
-
|
|
175
|
+
def action_cable_engine
|
|
176
|
+
return unless application_rb
|
|
177
|
+
return if application_rb.match?(/^require\s+['"](action_cable\/engine|rails\/all)['"]/)
|
|
178
|
+
|
|
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
|
|
91
184
|
|
|
92
|
-
|
|
185
|
+
return if found
|
|
186
|
+
|
|
187
|
+
@todos << "⚠️ Ensure Action Cable is loaded. Add `require \"action_cable/engine\"` to your `config/application.rb` file"
|
|
188
|
+
end
|
|
189
|
+
|
|
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
|
|
208
|
+
|
|
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)"
|
|
93
225
|
end
|
|
94
226
|
|
|
95
227
|
def finish
|
|
96
|
-
say_status :info, "✅ AnyCable has been configured
|
|
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
|
|
97
237
|
end
|
|
98
238
|
|
|
99
239
|
private
|
|
@@ -115,7 +255,7 @@ module AnyCableRailsGenerators
|
|
|
115
255
|
end
|
|
116
256
|
|
|
117
257
|
def local?
|
|
118
|
-
@
|
|
258
|
+
@development == "local"
|
|
119
259
|
end
|
|
120
260
|
|
|
121
261
|
def grpc?
|
|
@@ -126,6 +266,26 @@ module AnyCableRailsGenerators
|
|
|
126
266
|
@rpc_impl == "http"
|
|
127
267
|
end
|
|
128
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
|
+
|
|
129
289
|
def gemfile_lock
|
|
130
290
|
@gemfile_lock ||= begin
|
|
131
291
|
res = nil
|
|
@@ -137,6 +297,28 @@ module AnyCableRailsGenerators
|
|
|
137
297
|
end
|
|
138
298
|
end
|
|
139
299
|
|
|
300
|
+
def application_rb
|
|
301
|
+
@application_rb ||= begin
|
|
302
|
+
res = nil
|
|
303
|
+
in_root do
|
|
304
|
+
next unless File.file?("config/application.rb")
|
|
305
|
+
res = File.read("config/application.rb")
|
|
306
|
+
end
|
|
307
|
+
res
|
|
308
|
+
end
|
|
309
|
+
end
|
|
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
|
+
|
|
140
322
|
def install_for_docker
|
|
141
323
|
say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
|
|
142
324
|
|
|
@@ -159,7 +341,7 @@ module AnyCableRailsGenerators
|
|
|
159
341
|
condition: service_started
|
|
160
342
|
|
|
161
343
|
ws:
|
|
162
|
-
image: anycable/anycable-go:1.
|
|
344
|
+
image: anycable/anycable-go:1.6
|
|
163
345
|
ports:
|
|
164
346
|
- '8080:8080'
|
|
165
347
|
- '8090'
|
|
@@ -168,6 +350,7 @@ module AnyCableRailsGenerators
|
|
|
168
350
|
ANYCABLE_BROADCAST_ADAPTER: http
|
|
169
351
|
ANYCABLE_RPC_HOST: anycable:50051
|
|
170
352
|
ANYCABLE_DEBUG: ${ANYCABLE_DEBUG:-true}
|
|
353
|
+
ANYCABLE_SECRET: "anycable-local-secret"
|
|
171
354
|
|
|
172
355
|
anycable:
|
|
173
356
|
<<: *rails
|
|
@@ -208,16 +391,18 @@ module AnyCableRailsGenerators
|
|
|
208
391
|
ANYCABLE_BROADCAST_ADAPTER: http
|
|
209
392
|
ANYCABLE_RPC_HOST: http://rails:3000/_anycable
|
|
210
393
|
ANYCABLE_DEBUG: ${ANYCABLE_DEBUG:-true}
|
|
394
|
+
ANYCABLE_SECRET: "anycable-local-secret"
|
|
211
395
|
─────────────────────────────────────────
|
|
212
396
|
YML
|
|
213
397
|
end
|
|
214
398
|
end
|
|
215
399
|
|
|
216
|
-
def
|
|
400
|
+
def install_for_bin
|
|
217
401
|
unless file_exists?("bin/anycable-go")
|
|
218
402
|
generate "anycable:bin", "--version #{options[:version]}"
|
|
219
403
|
end
|
|
220
404
|
template_proc_files
|
|
405
|
+
update_bin_dev
|
|
221
406
|
true
|
|
222
407
|
end
|
|
223
408
|
|
|
@@ -230,7 +415,23 @@ module AnyCableRailsGenerators
|
|
|
230
415
|
adapter = Regexp.last_match[1]
|
|
231
416
|
next match if adapter == "test" || adapter.include?("any_cable")
|
|
232
417
|
|
|
233
|
-
match.sub(adapter,
|
|
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
|
|
234
435
|
end
|
|
235
436
|
|
|
236
437
|
File.write "config/cable.yml", new_contents
|
|
@@ -248,8 +449,6 @@ module AnyCableRailsGenerators
|
|
|
248
449
|
if file_exists?(file_name)
|
|
249
450
|
update_procfile(file_name)
|
|
250
451
|
else
|
|
251
|
-
say_status :help, "💡 We recommend using Overmind to manage multiple processes in development 👉 https://github.com/DarthSim/overmind", :yellow
|
|
252
|
-
|
|
253
452
|
return if options[:skip_procfile_dev]
|
|
254
453
|
|
|
255
454
|
template file_name
|
|
@@ -260,22 +459,38 @@ module AnyCableRailsGenerators
|
|
|
260
459
|
in_root do
|
|
261
460
|
contents = File.read(file_name)
|
|
262
461
|
|
|
263
|
-
|
|
462
|
+
if grpc?
|
|
264
463
|
unless contents.match?(/^anycable:\s/)
|
|
265
464
|
append_file file_name, "anycable: bundle exec anycable\n", force: true
|
|
266
465
|
end
|
|
267
466
|
end
|
|
268
467
|
unless contents.match?(/^ws:\s/)
|
|
269
|
-
append_file file_name, "ws: bin/anycable-go
|
|
468
|
+
append_file file_name, "ws: bin/anycable-go --port 8080", force: true
|
|
270
469
|
end
|
|
271
470
|
end
|
|
272
471
|
end
|
|
273
472
|
|
|
274
|
-
def
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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"
|
|
492
|
+
end
|
|
493
|
+
end
|
|
279
494
|
end
|
|
280
495
|
|
|
281
496
|
def file_exists?(name)
|
|
@@ -283,5 +498,32 @@ module AnyCableRailsGenerators
|
|
|
283
498
|
return File.file?(name)
|
|
284
499
|
end
|
|
285
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
|
|
286
528
|
end
|
|
287
529
|
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# AnyCable server configuration.
|
|
2
|
+
#
|
|
3
|
+
# Read more at https://docs.anycable.io/anycable-go/configuration
|
|
4
|
+
|
|
5
|
+
# Public mode disables connection authentication, pub/sub streams and broadcasts verification
|
|
6
|
+
# public = false
|
|
7
|
+
|
|
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.
|
|
12
|
+
secret = "anycable-local-secret"
|
|
13
|
+
|
|
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.
|
|
23
|
+
<%- if redis? -%>
|
|
24
|
+
broadcast_adapters = ["http", "redisx"]
|
|
25
|
+
<%- elsif nats? -%>
|
|
26
|
+
broadcast_adapters = ["http", "nats"]
|
|
27
|
+
<%- else -%>
|
|
28
|
+
broadcast_adapters = ["http"]
|
|
29
|
+
<%- end -%>
|
|
30
|
+
|
|
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
|
|
34
|
+
# pubsub_adapter = "redis" # or "nats"
|
|
35
|
+
|
|
36
|
+
[logging]
|
|
37
|
+
debug = true
|
|
38
|
+
|
|
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
|
|
42
|
+
[rpc]
|
|
43
|
+
<%- if http_rpc? -%>
|
|
44
|
+
# IMPORTANT: In production, provide the URL of the RPC service via the ANYCABLE_RPC_HOST env variable
|
|
45
|
+
host = "http://localhost:3000/_anycable"
|
|
46
|
+
<%- elsif grpc? -%>
|
|
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"
|
|
52
|
+
<%- end -%>
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Signed and public streams configuration.
|
|
56
|
+
# Read more: https://docs.anycable.io/anycable-go/signed_streams
|
|
57
|
+
[streams]
|
|
58
|
+
# Enable public (unsigned) streams
|
|
59
|
+
# public = true
|
|
60
|
+
# Enable whispering support for pub/sub streams
|
|
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
|
|
70
|
+
|
|
71
|
+
<%- if redis? -%>
|
|
72
|
+
[redis]
|
|
73
|
+
# IMPORTANT: In production, provide Redis address via the REDIS_URL or ANYCABLE_REDIS_URL env var
|
|
74
|
+
url = "redis://localhost:6379"
|
|
75
|
+
<%- end -%>
|
|
76
|
+
|
|
77
|
+
<%- if nats? -%>
|
|
78
|
+
[nats]
|
|
79
|
+
# IMPORTANT: In production, provide NATS address via the ANYCABLE_NATS_SERVERS env var
|
|
80
|
+
servers = "nats://127.0.0.1:4222"
|
|
81
|
+
<%- 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
|
|
@@ -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,53 +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 to broadcast messages to AnyCable server
|
|
19
|
-
broadcast_adapter: redis
|
|
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
|
-
<%- end -%>
|
|
27
|
-
<%- if redis? -%>
|
|
28
|
-
# You can use REDIS_URL env var to configure Redis URL.
|
|
29
|
-
# Localhost is used by default.
|
|
30
|
-
# redis_url: "redis://localhost:6379/1"
|
|
31
|
-
# Use the same channel name for WebSocket server, e.g.:
|
|
32
|
-
# $ anycable-go --redis_channel="__anycable__"
|
|
33
|
-
# redis_channel: "__anycable__"
|
|
34
|
-
<%- end -%>
|
|
35
14
|
<%- if http_rpc? -%>
|
|
36
|
-
# Use HTTP RPC mounted at the
|
|
37
|
-
# Read more
|
|
38
|
-
|
|
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
|
|
39
18
|
<%- end -%>
|
|
19
|
+
# This values MUST be the same as in your AnyCable server configuration
|
|
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"
|
|
40
26
|
|
|
41
27
|
development:
|
|
42
28
|
<<: *default
|
|
43
|
-
# WebSocket endpoint of your AnyCable server for clients to connect to
|
|
44
|
-
# Make sure you have the `action_cable_meta_tag` in your HTML layout
|
|
45
|
-
# to propogate this value to the client app
|
|
46
|
-
websocket_url: "ws://localhost:8080/cable"
|
|
47
29
|
|
|
30
|
+
# Read more about running AnyCable in test env:
|
|
31
|
+
# https://docs.anycable.io/rails/getting_started?id=testing-with-anycable
|
|
48
32
|
test:
|
|
49
33
|
<<: *default
|
|
50
34
|
|
|
51
35
|
production:
|
|
52
36
|
<<: *default
|
|
37
|
+
# Use ANYCABLE_WEBSOCKET_URL and ANYCABLE_SECRET environment variables
|
|
38
|
+
# or credentials or whatever.
|
|
53
39
|
websocket_url: ~
|
|
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
|