anycable-rails-core 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +203 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +106 -0
  5. data/lib/action_cable/subscription_adapter/any_cable.rb +40 -0
  6. data/lib/action_cable/subscription_adapter/anycable.rb +10 -0
  7. data/lib/anycable/rails/action_cable_ext/channel.rb +51 -0
  8. data/lib/anycable/rails/action_cable_ext/connection.rb +90 -0
  9. data/lib/anycable/rails/action_cable_ext/remote_connections.rb +13 -0
  10. data/lib/anycable/rails/channel_state.rb +108 -0
  11. data/lib/anycable/rails/compatibility/rubocop/config/default.yml +14 -0
  12. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +50 -0
  13. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/periodical_timers.rb +29 -0
  14. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +100 -0
  15. data/lib/anycable/rails/compatibility/rubocop.rb +27 -0
  16. data/lib/anycable/rails/compatibility.rb +63 -0
  17. data/lib/anycable/rails/config.rb +19 -0
  18. data/lib/anycable/rails/connection.rb +211 -0
  19. data/lib/anycable/rails/connection_factory.rb +44 -0
  20. data/lib/anycable/rails/connections/persistent_session.rb +40 -0
  21. data/lib/anycable/rails/connections/serializable_identification.rb +46 -0
  22. data/lib/anycable/rails/connections/session_proxy.rb +81 -0
  23. data/lib/anycable/rails/middlewares/executor.rb +31 -0
  24. data/lib/anycable/rails/middlewares/log_tagging.rb +21 -0
  25. data/lib/anycable/rails/rack.rb +56 -0
  26. data/lib/anycable/rails/railtie.rb +92 -0
  27. data/lib/anycable/rails/version.rb +7 -0
  28. data/lib/anycable/rails.rb +76 -0
  29. data/lib/anycable-rails.rb +3 -0
  30. data/lib/generators/anycable/download/USAGE +14 -0
  31. data/lib/generators/anycable/download/download_generator.rb +85 -0
  32. data/lib/generators/anycable/setup/USAGE +2 -0
  33. data/lib/generators/anycable/setup/setup_generator.rb +300 -0
  34. data/lib/generators/anycable/setup/templates/Procfile.dev.tt +6 -0
  35. data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +48 -0
  36. data/lib/generators/anycable/setup/templates/config/cable.yml.tt +11 -0
  37. data/lib/generators/anycable/setup/templates/config/initializers/anycable.rb.tt +9 -0
  38. data/lib/generators/anycable/with_os_helpers.rb +55 -0
  39. metadata +128 -0
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/anycable/with_os_helpers"
4
+
5
+ module AnyCableRailsGenerators
6
+ # Entry point for interactive installation
7
+ class SetupGenerator < ::Rails::Generators::Base
8
+ namespace "anycable:setup"
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ DOCS_ROOT = "https://docs.anycable.io"
12
+ DEVELOPMENT_METHODS = %w[skip local docker].freeze
13
+ SERVER_SOURCES = %w[skip brew binary].freeze
14
+
15
+ class_option :devenv,
16
+ type: :string,
17
+ desc: "Select your development environment (options: #{DEVELOPMENT_METHODS.join(", ")})"
18
+ class_option :source,
19
+ type: :string,
20
+ desc: "Choose a way of installing AnyCable-Go server (options: #{SERVER_SOURCES.join(", ")})"
21
+ class_option :skip_heroku,
22
+ type: :boolean,
23
+ desc: "Do not copy Heroku configs"
24
+ class_option :skip_procfile_dev,
25
+ type: :boolean,
26
+ desc: "Do not create Procfile.dev"
27
+ class_option :skip_jwt,
28
+ type: :boolean,
29
+ desc: "Do not install anycable-rails-jwt"
30
+ class_option :skip_install,
31
+ type: :boolean,
32
+ desc: "Do not run bundle install when adding new gems"
33
+
34
+ include WithOSHelpers
35
+
36
+ class_option :bin_path,
37
+ type: :string,
38
+ desc: "Where to download AnyCable-Go server binary (default: #{DEFAULT_BIN_PATH})"
39
+ class_option :version,
40
+ type: :string,
41
+ desc: "Specify the AnyCable-Go version (defaults to latest release)"
42
+
43
+ def welcome
44
+ say "👋 Welcome to AnyCable interactive installer."
45
+ end
46
+
47
+ def configs
48
+ inside("config") do
49
+ template "cable.yml"
50
+ template "anycable.yml"
51
+ end
52
+ end
53
+
54
+ def cable_url
55
+ environment(nil, env: :development) do
56
+ <<~SNIPPET
57
+ # Specify AnyCable WebSocket server URL to use by JS client
58
+ config.after_initialize do
59
+ config.action_cable.url = ActionCable.server.config.url = ENV.fetch("CABLE_URL", "ws://localhost:8080/cable") if AnyCable::Rails.enabled?
60
+ end
61
+ SNIPPET
62
+ end
63
+
64
+ environment(nil, env: :production) do
65
+ <<~SNIPPET
66
+ # Specify AnyCable WebSocket server URL to use by JS client
67
+ config.after_initialize do
68
+ config.action_cable.url = ActionCable.server.config.url = ENV.fetch("CABLE_URL", "/cable") if AnyCable::Rails.enabled?
69
+ end
70
+ SNIPPET
71
+ end
72
+
73
+ say_status :info, "✅ 'config.action_cable.url' has been configured"
74
+ say_status :help, "⚠️ If you're using JS client make sure you have " \
75
+ "`action_cable_meta_tag` included before any <script> tag in your application.html"
76
+ end
77
+
78
+ def development_method
79
+ answer = DEVELOPMENT_METHODS.index(options[:devenv]) || 99
80
+
81
+ until DEVELOPMENT_METHODS[answer.to_i]
82
+ answer = ask "Which environment do you use for development? (1) Local, (2) Docker, (0) Skip"
83
+ end
84
+
85
+ case env = DEVELOPMENT_METHODS[answer.to_i]
86
+ when "skip"
87
+ say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
88
+ else
89
+ send "install_for_#{env}"
90
+ end
91
+ end
92
+
93
+ def heroku
94
+ if options[:skip_heroku].nil?
95
+ return unless yes? "Do you use Heroku for deployment? [Yn]"
96
+ elsif options[:skip_heroku]
97
+ return
98
+ end
99
+
100
+ in_root do
101
+ next unless File.file?("Procfile")
102
+
103
+ contents = File.read("Procfile")
104
+ contents.sub!(/^web: (.*)$/, %q(web: [[ "$ANYCABLE_DEPLOYMENT" == "true" ]] && bundle exec anycable --server-command="anycable-go" || \1))
105
+ File.write("Procfile", contents)
106
+ say_status :info, "✅ Procfile updated"
107
+ end
108
+
109
+ say_status :help, "️️⚠️ Please, read the required steps to configure Heroku applications 👉 #{DOCS_ROOT}/deployment/heroku", :yellow
110
+ end
111
+
112
+ def devise
113
+ in_root do
114
+ return unless File.file?("config/initializers/devise.rb")
115
+ end
116
+
117
+ inside("config/initializers") do
118
+ template "anycable.rb"
119
+ end
120
+
121
+ say_status :info, "✅ config/initializers/anycable.rb with Devise configuration has been added"
122
+ end
123
+
124
+ def stimulus_reflex
125
+ return unless stimulus_reflex?
126
+
127
+ say_status :help, "⚠️ Please, check out the documentation on using AnyCable with Stimulus Reflex: #{DOCS_ROOT}/rails/stimulus_reflex"
128
+ end
129
+
130
+ def rubocop_compatibility
131
+ return unless rubocop?
132
+
133
+ say_status :info, "🤖 Running static compatibility checks with RuboCop"
134
+ res = run "bundle exec rubocop -r 'anycable/rails/compatibility/rubocop' --only AnyCable/InstanceVars,AnyCable/PeriodicalTimers,AnyCable/InstanceVars"
135
+ say_status :help, "⚠️ Please, take a look at the icompatibilities above and fix them. See #{DOCS_ROOT}/rails/compatibility" unless res
136
+ end
137
+
138
+ def jwt
139
+ return if options[:skip_jwt]
140
+
141
+ return unless options[:skip_jwt] == false || yes?("Do you want to use JWT for authentication? [Yn]")
142
+
143
+ opts = " --skip-install" if options[:skip_install]
144
+
145
+ run "bundle add anycable-rails-jwt#{opts}"
146
+ end
147
+
148
+ def finish
149
+ say_status :info, "✅ AnyCable has been configured successfully!"
150
+ end
151
+
152
+ private
153
+
154
+ def stimulus_reflex?
155
+ !!gemfile_lock&.match?(/^\s+stimulus_reflex\b/)
156
+ end
157
+
158
+ def redis?
159
+ !!gemfile_lock&.match?(/^\s+redis\b/)
160
+ end
161
+
162
+ def nats?
163
+ !!gemfile_lock&.match?(/^\s+nats-pure\b/)
164
+ end
165
+
166
+ def webpacker?
167
+ !!gemfile_lock&.match?(/^\s+webpacker\b/)
168
+ end
169
+
170
+ def rubocop?
171
+ !!gemfile_lock&.match?(/^\s+rubocop\b/)
172
+ end
173
+
174
+ def gemfile_lock
175
+ @gemfile_lock ||= begin
176
+ res = nil
177
+ in_root do
178
+ next unless File.file?("Gemfile.lock")
179
+ res = File.read("Gemfile.lock")
180
+ end
181
+ res
182
+ end
183
+ end
184
+
185
+ def install_for_docker
186
+ # Remove localhost from configuraiton
187
+ gsub_file "config/anycable.yml", /^.*redis_url:.*localhost[^\n]+\n/, ""
188
+
189
+ say_status :help, "️️⚠️ Docker development configuration could vary", :yellow
190
+
191
+ say "Here is an example snippet for docker-compose.yml:"
192
+ say <<~YML
193
+ ─────────────────────────────────────────
194
+ ws:
195
+ image: anycable/anycable-go:1.2
196
+ ports:
197
+ - '8080:8080'
198
+ environment:
199
+ ANYCABLE_HOST: "0.0.0.0"
200
+ ANYCABLE_REDIS_URL: redis://redis:6379/0
201
+ ANYCABLE_RPC_HOST: anycable:50051
202
+ ANYCABLE_DEBUG: 1
203
+ depends_on:
204
+ redis:
205
+ condition: service_healthy
206
+
207
+ anycable:
208
+ <<: *backend
209
+ command: bundle exec anycable
210
+ environment:
211
+ <<: *backend_environment
212
+ ANYCABLE_REDIS_URL: redis://redis:6379/0
213
+ ANYCABLE_RPC_HOST: 0.0.0.0:50051
214
+ ANYCABLE_DEBUG: 1
215
+ ports:
216
+ - '50051'
217
+ depends_on:
218
+ <<: *backend_depends_on
219
+ ws:
220
+ condition: service_started
221
+ ─────────────────────────────────────────
222
+ YML
223
+ end
224
+
225
+ def install_for_local
226
+ install_server
227
+ template_proc_files
228
+ end
229
+
230
+ def install_server
231
+ answer = SERVER_SOURCES.index(options[:source]) || 99
232
+
233
+ until SERVER_SOURCES[answer.to_i]
234
+ answer = ask "How do you want to install AnyCable-Go WebSocket server? (1) Homebrew, (2) Download binary, (0) Skip"
235
+ end
236
+
237
+ case answer.to_i
238
+ when 0
239
+ say_status :help, "⚠️ Please, read this guide on how to install AnyCable-Go server 👉 #{DOCS_ROOT}/anycable-go/getting_started", :yellow
240
+ return
241
+ else
242
+ return unless send("install_from_#{SERVER_SOURCES[answer.to_i]}")
243
+ end
244
+
245
+ say_status :info, "✅ AnyCable-Go WebSocket server has been successfully installed"
246
+ end
247
+
248
+ def template_proc_files
249
+ file_name = "Procfile.dev"
250
+
251
+ if file_exists?(file_name)
252
+ append_file file_name, "anycable: bundle exec anycable\nws: anycable-go#{anycable_go_options}", force: true
253
+ else
254
+ say_status :help, "💡 We recommend using Hivemind to manage multiple processes in development 👉 https://github.com/DarthSim/hivemind", :yellow
255
+
256
+ if options[:skip_procfile_dev].nil?
257
+ return unless yes? "Do you want to create a '#{file_name}' file?"
258
+ elsif options[:skip_procfile_dev]
259
+ return
260
+ end
261
+
262
+ template file_name
263
+ end
264
+ end
265
+
266
+ def install_from_brew
267
+ run "brew install anycable-go", abort_on_failure: true
268
+ run "anycable-go -v", abort_on_failure: true
269
+ end
270
+
271
+ def install_from_binary
272
+ bin_path ||= DEFAULT_BIN_PATH if options[:devenv] # User don't want interactive mode
273
+ bin_path ||= ask "Please, enter the path to download the AnyCable-Go binary to", default: DEFAULT_BIN_PATH, path: true
274
+
275
+ generate "anycable:download", download_options(bin_path: bin_path)
276
+
277
+ true
278
+ end
279
+
280
+ def download_options(**params)
281
+ opts = options.merge(params)
282
+ [].tap do |args|
283
+ args << "--os #{opts[:os]}" if opts[:os]
284
+ args << "--cpu #{opts[:cpu]}" if opts[:cpu]
285
+ args << "--bin-path=#{opts[:bin_path]}" if opts[:bin_path]
286
+ args << "--version #{opts[:version]}" if opts[:version]
287
+ end.join(" ")
288
+ end
289
+
290
+ def anycable_go_options
291
+ redis? ? " --port=8080" : " --port=8080 --broadcast_adapter=http"
292
+ end
293
+
294
+ def file_exists?(name)
295
+ in_root do
296
+ return File.file?(name)
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,6 @@
1
+ server: bundle exec rails s
2
+ <%- if webpacker? -%>
3
+ assets: bundle exec bin/webpack-dev-server
4
+ <%- end -%>
5
+ anycable: bundle exec anycable
6
+ ws: anycable-go<%= anycable_go_options %>
@@ -0,0 +1,48 @@
1
+ # This file contains per-environment settings for AnyCable.
2
+ #
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
11
+ #
12
+ default: &default
13
+ # Turn on/off access logs ("Started..." and "Finished...")
14
+ access_logs_disabled: false
15
+ # Whether to enable gRPC level logging or not
16
+ log_grpc: false
17
+ <%- if redis? -%>
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 adapter for a quick start (since redis gem is not present in the project)
25
+ broadcast_adapter: http
26
+ <%- end -%>
27
+ # Use the same channel name for WebSocket server, e.g.:
28
+ # $ anycable-go --redis_channel="__anycable__"
29
+ redis_channel: "__anycable__"
30
+ <%- if redis? -%>
31
+ # You can use REDIS_URL env var to configure Redis URL.
32
+ # Localhost is used by default.
33
+ # redis_url: "redis://localhost:6379/1"
34
+ <%- end -%>
35
+
36
+ development:
37
+ <<: *default
38
+
39
+ test:
40
+ <<: *default
41
+
42
+ production:
43
+ <<: *default
44
+ <%- if !redis? && !nats? -%>
45
+ # Use Redis or NATS in production
46
+ broadcast_adapter: redis
47
+ # broadcast_adapter: nats
48
+ <%- end -%>
@@ -0,0 +1,11 @@
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
+ development:
5
+ adapter: <%%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>
6
+
7
+ test:
8
+ adapter: test
9
+
10
+ production:
11
+ adapter: <%%= ENV.fetch("ACTION_CABLE_ADAPTER", "any_cable") %>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add Warden middleware to AnyCable stack to allow accessing
4
+ # Devise current user via `env["warden"].user`.
5
+ #
6
+ # See <%= DOCS_ROOT %>/ruby/authentication
7
+ AnyCable::Rails::Rack.middleware.use Warden::Manager do |config|
8
+ Devise.warden_config = config
9
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCableRailsGenerators
4
+ module WithOSHelpers
5
+ OS_NAMES = %w[linux darwin freebsd win].freeze
6
+ CPU_NAMES = %w[amd64 arm64 386 arm].freeze
7
+ DEFAULT_BIN_PATH = "/usr/local/bin"
8
+
9
+ def self.included(base)
10
+ base.class_option :os,
11
+ type: :string,
12
+ desc: "Specify the OS for AnyCable-Go server binary (options: #{OS_NAMES.join(", ")})"
13
+ base.class_option :cpu,
14
+ type: :string,
15
+ desc: "Specify the CPU architecturefor AnyCable-Go server binary (options: #{CPU_NAMES.join(", ")})"
16
+
17
+ private :current_cpu, :supported_current_cpu, :supported_current_os
18
+ end
19
+
20
+ def current_cpu
21
+ case Gem::Platform.local.cpu
22
+ when "x86_64", "x64"
23
+ "amd64"
24
+ when "x86_32", "x86", "i386", "i486", "i686"
25
+ "i386"
26
+ when "aarch64", "aarch64_be", "arm64", /armv8/
27
+ "arm64"
28
+ when "arm", /armv7/, /armv6/
29
+ "arm"
30
+ else
31
+ "unknown"
32
+ end
33
+ end
34
+
35
+ def os_name
36
+ options[:os] ||
37
+ supported_current_os ||
38
+ ask("What is your OS name?", limited_to: OS_NAMES)
39
+ end
40
+
41
+ def cpu_name
42
+ options[:cpu] ||
43
+ supported_current_cpu ||
44
+ ask("What is your CPU architecture?", limited_to: CPU_NAMES)
45
+ end
46
+
47
+ def supported_current_cpu
48
+ CPU_NAMES.find(&current_cpu.method(:==))
49
+ end
50
+
51
+ def supported_current_os
52
+ OS_NAMES.find(&Gem::Platform.local.os.method(:==))
53
+ end
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anycable-rails-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ platform: ruby
6
+ authors:
7
+ - palkan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: anycable-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: actioncable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: globalid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: AnyCable integration for Rails (w/o RPC dependencies)
56
+ email:
57
+ - dementiev.vm@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - CHANGELOG.md
63
+ - MIT-LICENSE
64
+ - README.md
65
+ - lib/action_cable/subscription_adapter/any_cable.rb
66
+ - lib/action_cable/subscription_adapter/anycable.rb
67
+ - lib/anycable-rails.rb
68
+ - lib/anycable/rails.rb
69
+ - lib/anycable/rails/action_cable_ext/channel.rb
70
+ - lib/anycable/rails/action_cable_ext/connection.rb
71
+ - lib/anycable/rails/action_cable_ext/remote_connections.rb
72
+ - lib/anycable/rails/channel_state.rb
73
+ - lib/anycable/rails/compatibility.rb
74
+ - lib/anycable/rails/compatibility/rubocop.rb
75
+ - lib/anycable/rails/compatibility/rubocop/config/default.yml
76
+ - lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb
77
+ - lib/anycable/rails/compatibility/rubocop/cops/anycable/periodical_timers.rb
78
+ - lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb
79
+ - lib/anycable/rails/config.rb
80
+ - lib/anycable/rails/connection.rb
81
+ - lib/anycable/rails/connection_factory.rb
82
+ - lib/anycable/rails/connections/persistent_session.rb
83
+ - lib/anycable/rails/connections/serializable_identification.rb
84
+ - lib/anycable/rails/connections/session_proxy.rb
85
+ - lib/anycable/rails/middlewares/executor.rb
86
+ - lib/anycable/rails/middlewares/log_tagging.rb
87
+ - lib/anycable/rails/rack.rb
88
+ - lib/anycable/rails/railtie.rb
89
+ - lib/anycable/rails/version.rb
90
+ - lib/generators/anycable/download/USAGE
91
+ - lib/generators/anycable/download/download_generator.rb
92
+ - lib/generators/anycable/setup/USAGE
93
+ - lib/generators/anycable/setup/setup_generator.rb
94
+ - lib/generators/anycable/setup/templates/Procfile.dev.tt
95
+ - lib/generators/anycable/setup/templates/config/anycable.yml.tt
96
+ - lib/generators/anycable/setup/templates/config/cable.yml.tt
97
+ - lib/generators/anycable/setup/templates/config/initializers/anycable.rb.tt
98
+ - lib/generators/anycable/with_os_helpers.rb
99
+ homepage: http://github.com/anycable/anycable-rails
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ bug_tracker_uri: http://github.com/anycable/anycable-rails/issues
104
+ changelog_uri: https://github.com/anycable/anycable-rails/blob/master/CHANGELOG.md
105
+ documentation_uri: https://docs.anycable.io/#/using_with_rails
106
+ homepage_uri: https://anycable.io/
107
+ source_code_uri: http://github.com/anycable/anycable-rails
108
+ funding_uri: https://github.com/sponsors/anycable
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '2.7'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 3.4.8
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: AnyCable integration for Rails (w/o RPC dependencies)
128
+ test_files: []