anycable-rails 1.0.0.preview2 → 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -125
  3. data/README.md +14 -34
  4. data/lib/anycable/rails.rb +36 -2
  5. data/lib/anycable/rails/actioncable/channel.rb +4 -0
  6. data/lib/anycable/rails/actioncable/connection.rb +24 -35
  7. data/lib/anycable/rails/actioncable/connection/serializable_identification.rb +42 -0
  8. data/lib/anycable/rails/actioncable/remote_connections.rb +11 -0
  9. data/lib/anycable/rails/channel_state.rb +48 -0
  10. data/lib/anycable/rails/compatibility.rb +4 -7
  11. data/lib/anycable/rails/compatibility/rubocop.rb +0 -1
  12. data/lib/anycable/rails/compatibility/rubocop/config/default.yml +3 -0
  13. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +1 -1
  14. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +4 -4
  15. data/lib/anycable/rails/railtie.rb +10 -10
  16. data/lib/anycable/rails/refinements/subscriptions.rb +5 -0
  17. data/lib/anycable/rails/session_proxy.rb +19 -2
  18. data/lib/anycable/rails/version.rb +1 -1
  19. data/lib/generators/anycable/download/USAGE +14 -0
  20. data/lib/generators/anycable/download/download_generator.rb +77 -0
  21. data/lib/generators/anycable/setup/USAGE +2 -0
  22. data/lib/{rails/generators → generators}/anycable/setup/setup_generator.rb +67 -89
  23. data/lib/{rails/generators → generators}/anycable/setup/templates/Procfile.dev +0 -0
  24. data/lib/{rails/generators → generators}/anycable/setup/templates/config/anycable.yml.tt +12 -1
  25. data/lib/generators/anycable/setup/templates/config/cable.yml.tt +11 -0
  26. data/lib/{rails/generators/anycable/setup/templates/config/initializers/anycable.rb → generators/anycable/setup/templates/config/initializers/anycable.rb.tt} +1 -1
  27. data/lib/generators/anycable/with_os_helpers.rb +55 -0
  28. metadata +23 -47
  29. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/remote_disconnect.rb +0 -31
  30. data/lib/rails/generators/anycable/setup/templates/Procfile +0 -2
  31. data/lib/rails/generators/anycable/setup/templates/bin/heroku-web +0 -7
  32. data/lib/rails/generators/anycable/setup/templates/config/cable.yml.tt +0 -11
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_cable/remote_connections"
4
+
5
+ ActionCable::RemoteConnections::RemoteConnection.include(ActionCable::Connection::SerializableIdentification)
6
+
7
+ ActionCable::RemoteConnections::RemoteConnection.prepend(Module.new do
8
+ def disconnect(reconnect: true)
9
+ ::AnyCable.broadcast_adapter.broadcast_command("disconnect", identifier: identifiers_json, reconnect: reconnect)
10
+ end
11
+ end)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Rails
5
+ module ChannelState
6
+ module ClassMethods
7
+ def state_attr_accessor(*names)
8
+ return attr_accessor(*names) unless AnyCable::Rails.enabled?
9
+
10
+ names.each do |name|
11
+ channel_state_attributes << name
12
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
13
+ def #{name}
14
+ return @#{name} if instance_variable_defined?(:@#{name})
15
+ @#{name} = AnyCable::Rails.deserialize(connection.socket.istate["#{name}"], json: true)
16
+ end
17
+
18
+ def #{name}=(val)
19
+ connection.socket.istate["#{name}"] = AnyCable::Rails.serialize(val, json: true)
20
+ instance_variable_set(:@#{name}, val)
21
+ end
22
+ RUBY
23
+ end
24
+ end
25
+
26
+ def channel_state_attributes
27
+ return @channel_state_attributes if instance_variable_defined?(:@channel_state_attributes)
28
+
29
+ @channel_state_attributes =
30
+ if superclass.respond_to?(:channel_state_attributes)
31
+ superclass.channel_state_attributes.dup
32
+ else
33
+ []
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.included(base)
39
+ base.extend ClassMethods
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ ActiveSupport.on_load(:action_cable) do
46
+ # `state_attr_accessor` must be available in Action Cable
47
+ ::ActionCable::Channel::Base.include(AnyCable::Rails::ChannelState)
48
+ end
@@ -33,6 +33,10 @@ module AnyCable
33
33
  res = yield
34
34
  diff = instance_variables - was_ivars
35
35
 
36
+ if self.class.respond_to?(:channel_state_attributes)
37
+ diff.delete_if { |ivar| self.class.channel_state_attributes.include?(:"#{ivar.to_s.sub(/^@/, "")}") }
38
+ end
39
+
36
40
  unless diff.empty?
37
41
  raise AnyCable::CompatibilityError,
38
42
  "Channel instance variables are not supported by AnyCable, " \
@@ -48,12 +52,5 @@ module AnyCable
48
52
  raise AnyCable::CompatibilityError, "Periodical timers are not supported by AnyCable"
49
53
  end
50
54
  end)
51
-
52
- ActionCable::RemoteConnections::RemoteConnection.prepend(Module.new do
53
- def disconnect
54
- raise AnyCable::CompatibilityError,
55
- "Disconnecting remote clients is not supported by AnyCable yet"
56
- end
57
- end)
58
55
  end
59
56
  end
@@ -4,7 +4,6 @@ require "rubocop"
4
4
  require "pathname"
5
5
 
6
6
  require_relative "rubocop/cops/anycable/stream_from"
7
- require_relative "rubocop/cops/anycable/remote_disconnect"
8
7
  require_relative "rubocop/cops/anycable/periodical_timers"
9
8
  require_relative "rubocop/cops/anycable/instance_vars"
10
9
 
@@ -1,11 +1,14 @@
1
1
  AnyCable/InstanceVars:
2
+ Enabled: true
2
3
  Include:
3
4
  - "**/channels/**/*.rb"
4
5
 
5
6
  AnyCable/StreamFrom:
7
+ Enabled: true
6
8
  Include:
7
9
  - "**/channels/**/*.rb"
8
10
 
9
11
  AnyCable/PeriodicalTimers:
12
+ Enabled: true
10
13
  Include:
11
14
  - "**/channels/**/*.rb"
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # end
26
26
  #
27
27
  class InstanceVars < RuboCop::Cop::Cop
28
- MSG = "Channel instance variables are not supported in AnyCable"
28
+ MSG = "Channel instance variables are not supported in AnyCable. Use `state_attr_accessor` instead"
29
29
 
30
30
  def on_class(node)
31
31
  find_nested_ivars(node) do |nested_ivar|
@@ -11,7 +11,7 @@ module RuboCop
11
11
  # # bad
12
12
  # class MyChannel < ApplicationCable::Channel
13
13
  # def follow
14
- # stream_from("all") {}
14
+ # stream_for(room) {}
15
15
  # end
16
16
  # end
17
17
  #
@@ -36,15 +36,15 @@ module RuboCop
36
36
  #
37
37
  class StreamFrom < RuboCop::Cop::Cop
38
38
  def_node_matcher :stream_from_with_block?, <<-PATTERN
39
- (block (send _ :stream_from ...) ...)
39
+ (block {(send _ :stream_from ...) (send _ :stream_for ...)} ...)
40
40
  PATTERN
41
41
 
42
42
  def_node_matcher :stream_from_with_callback?, <<-PATTERN
43
- (send _ :stream_from str_type? (block (send nil? :lambda) ...))
43
+ {(send _ :stream_from str_type? (block (send nil? :lambda) ...)) (send _ :stream_for ... (block (send nil? :lambda) ...))}
44
44
  PATTERN
45
45
 
46
46
  def_node_matcher :args_of_stream_from, <<-PATTERN
47
- (send _ :stream_from str_type? $...)
47
+ {(send _ :stream_from str_type? $...) (send _ :stream_for $...)}
48
48
  PATTERN
49
49
 
50
50
  def_node_matcher :coder_symbol?, "(pair (sym :coder) ...)"
@@ -1,23 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "anycable/rails/channel_state"
4
+
3
5
  module AnyCable
4
6
  module Rails
5
7
  class Railtie < ::Rails::Railtie # :nodoc:
6
8
  initializer "anycable.disable_action_cable_mount", after: "action_cable.set_configs" do |app|
7
- # Disable Action Cable default route when AnyCable adapter is used
8
- adapter = ::ActionCable.server.config.cable&.fetch("adapter", nil)
9
- next unless AnyCable::Rails.compatible_adapter?(adapter)
9
+ next unless AnyCable::Rails.enabled?
10
10
 
11
11
  app.config.action_cable.mount_path = nil
12
12
  end
13
13
 
14
14
  initializer "anycable.logger", after: "action_cable.logger" do |_app|
15
- AnyCable.logger = ActiveSupport::TaggedLogging.new(::ActionCable.server.config.logger)
15
+ AnyCable.logger = ::ActionCable.server.config.logger
16
16
 
17
- # Broadcast server logs to STDOUT in development
18
- if ::Rails.env.development? &&
19
- !ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, STDOUT)
20
- AnyCable.configure_server do
17
+ AnyCable.configure_server do
18
+ AnyCable.logger = ActiveSupport::TaggedLogging.new(::ActionCable.server.config.logger)
19
+ # Broadcast server logs to STDOUT in development
20
+ if ::Rails.env.development? &&
21
+ !ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, STDOUT)
21
22
  console = ActiveSupport::Logger.new(STDOUT)
22
23
  console.formatter = ::Rails.logger.formatter
23
24
  console.level = ::Rails.logger.level
@@ -42,8 +43,7 @@ module AnyCable
42
43
 
43
44
  initializer "anycable.connection_factory", after: "action_cable.set_configs" do |app|
44
45
  ActiveSupport.on_load(:action_cable) do
45
- adapter = ::ActionCable.server.config.cable&.fetch("adapter", nil)
46
- if AnyCable::Rails.compatible_adapter?(adapter)
46
+ if AnyCable::Rails.enabled?
47
47
  require "anycable/rails/actioncable/connection"
48
48
 
49
49
  if AnyCable.config.persistent_session_enabled
@@ -7,6 +7,11 @@ module AnyCable
7
7
  # Find or add a subscription to the list
8
8
  def fetch(identifier)
9
9
  add("identifier" => identifier) unless subscriptions[identifier]
10
+
11
+ unless subscriptions[identifier]
12
+ raise "Channel not found: #{ActiveSupport::JSON.decode(identifier).fetch("channel")}"
13
+ end
14
+
10
15
  subscriptions[identifier]
11
16
  end
12
17
  end
@@ -5,8 +5,6 @@ module AnyCable
5
5
  # Wrap `request.session` to lazily load values provided
6
6
  # in the RPC call (set by the previous calls)
7
7
  class SessionProxy
8
- delegate_missing_to :@rack_session
9
-
10
8
  attr_reader :rack_session, :socket_session
11
9
 
12
10
  def initialize(rack_session, socket_session)
@@ -41,6 +39,25 @@ module AnyCable
41
39
  rack_session.keys + socket_session.keys
42
40
  end
43
41
 
42
+ # Delegate both publuc and private methods to rack_session
43
+ def respond_to_missing?(name, include_private = false)
44
+ return false if name == :marshal_dump || name == :_dump
45
+ rack_session.respond_to?(name, include_private) || super
46
+ end
47
+
48
+ def method_missing(method, *args, &block)
49
+ if rack_session.respond_to?(method, true)
50
+ rack_session.send(method, *args, &block)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ # This method is used by StimulusReflex to obtain `@by`
57
+ def instance_variable_get(name)
58
+ super || rack_session.instance_variable_get(name)
59
+ end
60
+
44
61
  private
45
62
 
46
63
  def restore!
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rails
5
- VERSION = "1.0.0.preview2"
5
+ VERSION = "1.0.0.rc1"
6
6
  end
7
7
  end
@@ -0,0 +1,14 @@
1
+ Description:
2
+ Install AnyCable-Go web server.
3
+
4
+ Example:
5
+ rails generate anycable:download
6
+
7
+ This will ask:
8
+ Where to store a binary file.
9
+ This will create:
10
+ `<bin_path>/anycable-go`.
11
+
12
+ rails generate anycable:download --bin-path=/usr/local/bin
13
+
14
+ rails generate anycable:download --version=1.0.0
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/anycable/with_os_helpers"
4
+
5
+ module AnyCableRailsGenerators
6
+ # Downloads anycable-go binary
7
+ class DownloadGenerator < ::Rails::Generators::Base
8
+ namespace "anycable:download"
9
+
10
+ include WithOSHelpers
11
+
12
+ # TODO: change to latest release
13
+ VERSION = "1.0.0.preview1"
14
+
15
+ class_option :bin_path,
16
+ type: :string,
17
+ desc: "Where to download AnyCable-Go server binary (default: #{DEFAULT_BIN_PATH})"
18
+ class_option :version,
19
+ type: :string,
20
+ desc: "Specify the AnyCable-Go version (defaults to latest release)"
21
+
22
+ def download_bin
23
+ out = options[:bin_path] || DEFAULT_BIN_PATH
24
+ version = options[:version] || VERSION
25
+
26
+ download_exe(
27
+ release_url(version),
28
+ to: out,
29
+ file_name: "anycable-go"
30
+ )
31
+
32
+ true
33
+ end
34
+
35
+ private
36
+
37
+ def release_url(version)
38
+ if Gem::Version.new(version).segments.first >= 1
39
+ new_release_url(version)
40
+ else
41
+ legacy_release_url(version)
42
+ end
43
+ end
44
+
45
+ def legacy_release_url(version)
46
+ "https://github.com/anycable/anycable-go/releases/download/v#{version}/" \
47
+ "anycable-go-v#{version}-#{os_name}-#{cpu_name}"
48
+ end
49
+
50
+ def new_release_url(version)
51
+ "https://github.com/anycable/anycable-go/releases/download/v#{version}/" \
52
+ "anycable-go-#{os_name}-#{cpu_name}"
53
+ end
54
+
55
+ def download_exe(url, to:, file_name:)
56
+ file_path = File.join(to, file_name)
57
+
58
+ run "#{sudo(to)}curl -L #{url} -o #{file_path}", abort_on_failure: true
59
+ run "#{sudo(to)}chmod +x #{file_path}", abort_on_failure: true
60
+ run "#{file_path} -v", abort_on_failure: true
61
+ end
62
+
63
+ def sudo(path)
64
+ sudo = ""
65
+ unless File.writable?(path)
66
+ if yes? "Path is not writable 😕. Do you have sudo privileges?"
67
+ sudo = "sudo "
68
+ else
69
+ say_status :error, "❌ Failed to install AnyCable-Go WebSocket server", :red
70
+ raise StandardError, "Path #{path} is not writable!"
71
+ end
72
+ end
73
+
74
+ sudo
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Configures your application to work with AnyCable interactively.
@@ -1,34 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "generators/anycable/with_os_helpers"
4
+
3
5
  module AnyCableRailsGenerators
4
6
  # Entry point for interactive installation
5
7
  class SetupGenerator < ::Rails::Generators::Base
6
8
  namespace "anycable:setup"
7
9
  source_root File.expand_path("templates", __dir__)
8
10
 
9
- METHODS = %w[skip local docker].freeze
10
- # TODO(release): change to latest release
11
- SERVER_VERSION = "v1.0.0.preview1"
12
- OS_NAMES = %w[linux darwin freebsd win].freeze
13
- CPU_NAMES = %w[amd64 arm64 386 arm].freeze
11
+ DOCS_ROOT = "https://docs.anycable.io/v1/#"
12
+ DEVELOPMENT_METHODS = %w[skip local docker].freeze
14
13
  SERVER_SOURCES = %w[skip brew binary].freeze
15
- DEFAULT_BIN_PATH = "/usr/local/bin"
16
14
 
17
- class_option :method,
15
+ class_option :devenv,
18
16
  type: :string,
19
- desc: "Select your development environment (options: #{METHODS.join(", ")})"
17
+ desc: "Select your development environment (options: #{DEVELOPMENT_METHODS.join(", ")})"
20
18
  class_option :source,
21
19
  type: :string,
22
20
  desc: "Choose a way of installing AnyCable-Go server (options: #{SERVER_SOURCES.join(", ")})"
23
- class_option :bin_path,
24
- type: :string,
25
- desc: "Where to download AnyCable-Go server binary (default: #{DEFAULT_BIN_PATH})"
26
- class_option :os,
27
- type: :string,
28
- desc: "Specify the OS for AnyCable-Go server binary (options: #{OS_NAMES.join(", ")})"
29
- class_option :cpu,
30
- type: :string,
31
- desc: "Specify the CPU architecturefor AnyCable-Go server binary (options: #{CPU_NAMES.join(", ")})"
32
21
  class_option :skip_heroku,
33
22
  type: :boolean,
34
23
  desc: "Do not copy Heroku configs"
@@ -36,6 +25,15 @@ module AnyCableRailsGenerators
36
25
  type: :boolean,
37
26
  desc: "Do not create Procfile.dev"
38
27
 
28
+ include WithOSHelpers
29
+
30
+ class_option :bin_path,
31
+ type: :string,
32
+ desc: "Where to download AnyCable-Go server binary (default: #{DEFAULT_BIN_PATH})"
33
+ class_option :version,
34
+ type: :string,
35
+ desc: "Specify the AnyCable-Go version (defaults to latest release)"
36
+
39
37
  def welcome
40
38
  say "👋 Welcome to AnyCable interactive installer."
41
39
  end
@@ -51,32 +49,32 @@ module AnyCableRailsGenerators
51
49
  environment(nil, env: :development) do
52
50
  <<~SNIPPET
53
51
  # Specify AnyCable WebSocket server URL to use by JS client
54
- config.action_cable.url = ENV.fetch("CABLE_URL", "ws://localhost:3334/cable").presence
52
+ config.action_cable.url = ENV.fetch("CABLE_URL", "ws://localhost:8080/cable") if AnyCable::Rails.enabled?
55
53
  SNIPPET
56
54
  end
57
55
 
58
56
  environment(nil, env: :production) do
59
57
  <<~SNIPPET
60
58
  # Specify AnyCable WebSocket server URL to use by JS client
61
- config.action_cable.url = ENV["CABLE_URL"].presence
59
+ config.action_cable.url = ENV.fetch("CABLE_URL") if AnyCable::Rails.enabled?
62
60
  SNIPPET
63
61
  end
64
62
 
65
63
  say_status :info, "✅ 'config.action_cable.url' has been configured"
66
- say_status :help, "⚠️ If you're using JS client make sure you have " \
64
+ say_status :help, "⚠️ If you're using JS client make sure you have " \
67
65
  "`action_cable_meta_tag` included before any <script> tag in your application.html"
68
66
  end
69
67
 
70
68
  def development_method
71
- answer = METHODS.index(options[:method]) || 99
69
+ answer = DEVELOPMENT_METHODS.index(options[:devenv]) || 99
72
70
 
73
- until METHODS[answer.to_i]
71
+ until DEVELOPMENT_METHODS[answer.to_i]
74
72
  answer = ask "Which environment do you use for development? (1) Local, (2) Docker, (0) Skip"
75
73
  end
76
74
 
77
- case env = METHODS[answer.to_i]
75
+ case env = DEVELOPMENT_METHODS[answer.to_i]
78
76
  when "skip"
79
- say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 https://docs.anycable.io/#/anycable-go/getting_started", :yellow
77
+ say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
80
78
  else
81
79
  send "install_for_#{env}"
82
80
  end
@@ -84,15 +82,21 @@ module AnyCableRailsGenerators
84
82
 
85
83
  def heroku
86
84
  if options[:skip_heroku].nil?
87
- return unless yes? "Do you use Heroku for deployment?"
85
+ return unless yes? "Do you use Heroku for deployment? [Yn]"
88
86
  elsif options[:skip_heroku]
89
87
  return
90
88
  end
91
89
 
92
- template "Procfile"
93
- inside("bin") { template "heroku-web" }
90
+ in_root do
91
+ next unless File.file?("Procfile")
92
+
93
+ contents = File.read("Procfile")
94
+ contents.sub!(/^web: (.*)$/, %q(web: [[ "$ANYCABLE_DEPLOYMENT" == "true" ]] && bundle exec anycable --server-command="anycable-go" || \1))
95
+ File.write("Procfile", contents)
96
+ say_status :info, "✅ Procfile updated"
97
+ end
94
98
 
95
- say_status :help, "️️⚠️ Please, read the required steps to configure Heroku applications 👉 https://docs.anycable.io/#/deployment/heroku", :yellow
99
+ say_status :help, "️️⚠️ Please, read the required steps to configure Heroku applications 👉 #{DOCS_ROOT}/deployment/heroku", :yellow
96
100
  end
97
101
 
98
102
  def devise
@@ -107,6 +111,14 @@ module AnyCableRailsGenerators
107
111
  say_status :info, "✅ config/initializers/anycable.rb with Devise configuration has been added"
108
112
  end
109
113
 
114
+ def rubocop_compatibility
115
+ return unless rubocop?
116
+
117
+ say_status :info, "🤖 Running static compatibility checks with RuboCop"
118
+ res = run "bundle exec rubocop -r 'anycable/rails/compatibility/rubocop' --only AnyCable/InstanceVars,AnyCable/PeriodicalTimers,AnyCable/InstanceVars"
119
+ say_status :help, "⚠️ Please, take a look at the icompatibilities above and fix them. See https://docs.anycable.io/v1/#/ruby/compatibility" unless res
120
+ end
121
+
110
122
  def finish
111
123
  say_status :info, "✅ AnyCable has been configured successfully!"
112
124
  end
@@ -114,7 +126,15 @@ module AnyCableRailsGenerators
114
126
  private
115
127
 
116
128
  def stimulus_reflex?
117
- !gemfile_lock&.match?(/^\s+stimulus_reflex\b/).nil?
129
+ !!gemfile_lock&.match?(/^\s+stimulus_reflex\b/)
130
+ end
131
+
132
+ def redis?
133
+ !!gemfile_lock&.match?(/^\s+redis\b/)
134
+ end
135
+
136
+ def rubocop?
137
+ !!gemfile_lock&.match?(/^\s+rubocop\b/)
118
138
  end
119
139
 
120
140
  def gemfile_lock
@@ -129,24 +149,23 @@ module AnyCableRailsGenerators
129
149
  end
130
150
 
131
151
  def install_for_docker
132
- say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
152
+ say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
133
153
 
134
154
  say "Here is an example snippet for docker-compose.yml:"
135
155
  say <<~YML
136
156
  ─────────────────────────────────────────
137
- anycable-ws:
138
- image: anycable/anycable-go:v0.6.4
157
+ ws:
158
+ image: anycable/anycable-go:1.0.0.preview1
139
159
  ports:
140
- - '3334:3334'
160
+ - '8080:8080'
141
161
  environment:
142
- PORT: 3334
143
162
  ANYCABLE_REDIS_URL: redis://redis:6379/0
144
- ANYCABLE_RPC_HOST: anycable-rpc:50051
163
+ ANYCABLE_RPC_HOST: anycable:50051
145
164
  depends_on:
146
165
  - anycable-rpc
147
166
  - redis
148
167
 
149
- anycable-rpc:
168
+ anycable:
150
169
  <<: *backend
151
170
  command: bundle exec anycable
152
171
  environment:
@@ -173,7 +192,7 @@ module AnyCableRailsGenerators
173
192
 
174
193
  case answer.to_i
175
194
  when 0
176
- say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 https://docs.anycable.io/#/anycable-go/getting_started", :yellow
195
+ say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
177
196
  return
178
197
  else
179
198
  return unless send("install_from_#{SERVER_SOURCES[answer.to_i]}")
@@ -206,63 +225,22 @@ module AnyCableRailsGenerators
206
225
  end
207
226
 
208
227
  def install_from_binary
209
- out = options[:bin_path] if options[:bin_path]
210
- out ||= "/usr/local/bin" if options[:method] # User don't want interactive mode
211
- out ||= ask "Please, enter the path to download the AnyCable-Go binary to", default: DEFAULT_BIN_PATH, path: true
228
+ bin_path ||= DEFAULT_BIN_PATH if options[:devenv] # User don't want interactive mode
229
+ bin_path ||= ask "Please, enter the path to download the AnyCable-Go binary to", default: DEFAULT_BIN_PATH, path: true
212
230
 
213
- os_name = options[:os] ||
214
- OS_NAMES.find(&Gem::Platform.local.os.method(:==)) ||
215
- ask("What is your OS name?", limited_to: OS_NAMES)
216
-
217
- cpu_name = options[:cpu] ||
218
- CPU_NAMES.find(&current_cpu.method(:==)) ||
219
- ask("What is your CPU architecture?", limited_to: CPU_NAMES)
220
-
221
- download_exe(
222
- "https://github.com/anycable/anycable-go/releases/download/#{SERVER_VERSION}/" \
223
- "anycable-go-#{os_name}-#{cpu_name}",
224
- to: out,
225
- file_name: "anycable-go"
226
- )
231
+ generate "anycable:download", download_options(bin_path: bin_path)
227
232
 
228
233
  true
229
234
  end
230
235
 
231
- def download_exe(url, to:, file_name:)
232
- file_path = File.join(to, file_name)
233
-
234
- run "#{sudo(to)}curl -L #{url} -o #{file_path}", abort_on_failure: true
235
- run "#{sudo(to)}chmod +x #{file_path}", abort_on_failure: true
236
- run "#{file_path} -v", abort_on_failure: true
237
- end
238
-
239
- def sudo!(path)
240
- sudo = ""
241
- unless File.writable?(path)
242
- if yes? "Path is not writable 😕. Do you have sudo privileges?"
243
- sudo = "sudo "
244
- else
245
- say_status :error, "❌ Failed to install AnyCable-Go WebSocket server", :red
246
- raise StandardError, "Path #{path} is not writable!"
247
- end
248
- end
249
-
250
- sudo
251
- end
252
-
253
- def current_cpu
254
- case Gem::Platform.local.cpu
255
- when "x86_64", "x64"
256
- "amd64"
257
- when "x86_32", "x86", "i386", "i486", "i686"
258
- "i386"
259
- when "aarch64", "aarch64_be", /armv8/
260
- "arm64"
261
- when "arm", /armv7/, /armv6/
262
- "arm"
263
- else
264
- "unknown"
265
- end
236
+ def download_options(**params)
237
+ opts = options.merge(params)
238
+ [].tap do |args|
239
+ args << "--os #{opts[:os]}" if opts[:os]
240
+ args << "--cpu #{opts[:cpu]}" if opts[:cpu]
241
+ args << "--bin-path=#{opts[:bin_path]}" if opts[:bin_path]
242
+ args << "--version #{opts[:version]}" if opts[:version]
243
+ end.join(" ")
266
244
  end
267
245
  end
268
246
  end