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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bc0b1eac8c453d0054d508826fd33e58b32684c89694fbbc393d2796db8f0c1
4
+ data.tar.gz: 82ff10fabf21471a9fdcc14e6d76d294902b89372002734538b66e1e7569ab1d
5
+ SHA512:
6
+ metadata.gz: 53cda30136d94edc95e319a0e80bed3ce19716d15dd0e69eae0fc14ffb0c45d58ec0fb466bece171736f4821a7aeeba6ec774c228e8915deb7b2eea01fed1565
7
+ data.tar.gz: 2787f8c23179f2283fe9b68982f0e3f0ea8fde5222ef97dba7294464523d8968f57f1c71f166f0d44583047747bbd40433e7603afb07c45f3b58eac98bba3cf4
data/CHANGELOG.md ADDED
@@ -0,0 +1,203 @@
1
+ # Change log
2
+
3
+ ## master
4
+
5
+ ## 1.4.0 (2023-07-07)
6
+
7
+ - Add HTTP RPC integration. ([@palkan][])
8
+
9
+ Specify `http_rpc_mounth_path` in your `anycable.yml` to enable HTTP RPC.
10
+
11
+ - Fix `anycable:download` command. ([@palkan][])
12
+
13
+ Detect MacOS arm64, create target bin path if it doesn't exist.
14
+
15
+ ## 1.3.7 (2023-02-28)
16
+
17
+ - Fix `anycable` gem dependency constraints.
18
+
19
+ - Require Ruby 2.7+.
20
+
21
+ ## 1.3.6 (2023-02-28)
22
+
23
+ - Handle `nil` streams gracefully. ([@palkan][])
24
+
25
+ - Report exceptions via the `Rails.error.report` interface. ([@palkan][])
26
+
27
+ ## 1.3.5 (2023-01-04)
28
+
29
+ - Make misconfiguration error more informative. ([@palkan][])
30
+
31
+ ## 1.3.4 (2022-06-28)
32
+
33
+ - Add support and backport for Connection command callbacks. ([@palkan][])
34
+
35
+ ## 1.3.3 (2022-04-20)
36
+
37
+ - Added `sid` (unique connection identifier) field to the `welcome` message if present. ([@palkan][])
38
+
39
+ - Fixed handling Ruby Logger incompatible loggers. ([@palkan][])
40
+
41
+ ## 1.3.2 (2022-03-04)
42
+
43
+ - Allow Ruby 2.6.
44
+
45
+ ## 1.3.1 (2022-02-28)
46
+
47
+ - Fix Action Cable Channel patch to not change methods signatures. ([@palkan][])
48
+
49
+ Otherwise it could lead to conflicts with other patches.
50
+
51
+ ## 1.3.0 (2022-02-21)
52
+
53
+ - Introduce `AnyCable::Rails.extend_adapter!` to make any pubsub adapter AnyCable-compatible. ([@palkan][])
54
+
55
+ - Refactored Action Cable patching to preserve original functionality and avoid monkey-patching collisions. ([@palkan][])
56
+
57
+ ## 1.2.1 (2022-01-31)
58
+
59
+ - Add a temporary fix to be compatible with `sentry-rails`. ([@palkan][])
60
+
61
+ See [#165](https://github.com/anycable/anycable-rails/issues/165).
62
+
63
+ - Run embedded RPC server only if `any_cable` adapter is used for Action Cable. ([@palkan][])
64
+
65
+ ## 1.2.0 (2021-12-21) 🎄
66
+
67
+ - Drop Rails 5 support.
68
+
69
+ - Drop Ruby 2.6 support.
70
+
71
+ ## 1.1.4 (2021-11-11)
72
+
73
+ - Added `Connection#state_attr_accessor`. ([@palkan][])
74
+
75
+ ## 1.1.3 (2021-10-11)
76
+
77
+ - Relax Action Cable dependency. ([@palkan][])
78
+
79
+ Action Cable 5.1 is allowed (though not recommended).
80
+
81
+ ## 1.1.2 (2021-06-23)
82
+
83
+ - Bring back dependency on `anycable` (instead of `anycable-core`). ([@palkan][])
84
+
85
+ Make it easier to get started by adding just a single gem.
86
+
87
+ ## 1.1.1 (2021-06-08)
88
+
89
+ - Updated documentation links in the generator. ([@palkan][])
90
+
91
+ ## 1.1.0 🚸 (2021-06-01)
92
+
93
+ - No changes since 1.1.0.rc1.1.
94
+
95
+ ## 1.1.0.rc1.1 (2021-05-12)
96
+
97
+ - Fixed config loading regression introduced in 1.1.0.rc1.
98
+
99
+ ## 1.1.0.rc1 (2021-05-12)
100
+
101
+ - Adding `anycable` or `grpc` gem as an explicit dependency is required.
102
+
103
+ Now, `anycable-rails` depends on `anycable-core`, which doesn't include gRPC server implementation.
104
+ You should either add `anycable` or `grpc` (>= 1.37) gem as an explicit dependency.
105
+
106
+ - Add option to embed AnyCable RPC into a Rails server process. ([@palkan][])
107
+
108
+ Set `embedded: true` in the configuration to launch RPC along with `rails s` (only for Rails 6.1+).
109
+
110
+ - **Ruby >= 2.6** is required.
111
+ - **Rails >= 6.0** is required.
112
+
113
+ ## 1.0.7 (2021-03-05)
114
+
115
+ - Ruby 3 compatibility. ([@palkan][])
116
+
117
+ ## 1.0.6 (2021-02-25)
118
+
119
+ - Keep an explicit list of instance vars to ignore in compatibility checks. ([@palkan][])
120
+
121
+ You can ignore custom vars by adding them to the list: `AnyCable::Compatibility::IGNORE_INSTANCE_VARS << :@my_var`.
122
+
123
+ ## 1.0.5 (2021-02-24)
124
+
125
+ - Fixed bug with compatibility false negatives in development. ([@palkan][])
126
+
127
+ See [#151](https://github.com/anycable/anycable-rails/issues/151).
128
+
129
+ ## 1.0.4 (2020-10-02)
130
+
131
+ - Relax Rails dependencies. ([@palkan][])
132
+
133
+ Only add `actioncable` and `globalid` as runtime dependencies, not the whole `rails`.
134
+
135
+ ## 1.0.3 (2020-09-16)
136
+
137
+ - Fixed bug with building a request object when session store is absent. ([@palkan][])
138
+
139
+ ## 1.0.2 (2020-09-08)
140
+
141
+ - Added missing channel state support to `#unsubscribed` callbacks. ([@palkan][])
142
+
143
+ ## 1.0.1 (2020-07-07)
144
+
145
+ - Fixed patching Action Cable testing classes. ([@palkan][])
146
+
147
+ ## 1.0.0 (2020-07-01)
148
+
149
+ - Support `rescue_from` in connections (Rails 6.1). ([@palkan][])
150
+
151
+ - Make AnyCable patches compatible with Action Cable testing. ([@palkan][])
152
+
153
+ - Do not add localhost `redis_url` to `anycable.yml` when Docker development method is chosen in `anycable:setup`. ([@palkan][])
154
+
155
+ - Fix connection identifiers deserialization regression. ([@palkan][])
156
+
157
+ Using non-strings or non-GlobalId-encoded objects was broken.
158
+
159
+ - Improve `anycable:setup` generator. ([@palkan][])
160
+
161
+ Update Docker snippet, do not enable persistent sessions automatically,
162
+ fix setting `config.action_cable.url` in environment configuration.
163
+
164
+ - Add `state_attr_accessor` for channels. ([@palkan][])
165
+
166
+ Just like `attr_accessor` but "persists" the state between RPC calls.
167
+
168
+ - Add `Channel#stop_stream_from` support. ([@palkan][])
169
+
170
+ - Add `RemoteConnections` support. ([@palkan][])
171
+
172
+ - Add `AnyCable::Rails.enabled?` method which returns true if Action Cable uses AnyCable adapter. ([@palkan][])
173
+
174
+ - Add `anycable:download` generator to download `anycable-go` binary. ([@palkan][])
175
+
176
+ - **Ruby 2.5+ is required**. ([@palkan][])
177
+
178
+ - Support `disconnect` messages. ([@palkan][])
179
+
180
+ Added in Rails 6 (see [PR#34194](https://github.com/rails/rails/pull/34194)).
181
+
182
+ - Add ability to persist _dirty_ `request.session` between RPC calls. ([@palkan][])
183
+
184
+ This feature emulates the Action Cable behaviour where it's possible to use `request.session` as a shared Hash-like store.
185
+ This could be used by some applications (e.g., [StimulusReflex](https://github.com/hopsoft/stimulus_reflex)-based).
186
+
187
+ You must turn this feature on by setting `persistent_session_enabled: true` in the AnyCable configuration.
188
+
189
+ - Add ability to use Rack middlewares when build a request for a connection. ([@bibendi][])
190
+
191
+ - Add set up generator to configure a Rails application by running `bin/rails g anycable:setup`. ([@bibendi][])
192
+
193
+ - Require a minimum version of Ruby when installing the gem. ([@bibendi][])
194
+
195
+ - Add ability to develop the gem with Docker. ([@bibendi][])
196
+
197
+ See [Changelog](https://github.com/anycable/anycable-rails/blob/0-6-stable/CHANGELOG.md) for versions <1.0.0.
198
+
199
+ [@palkan]: https://github.com/palkan
200
+ [@alekseyl]: https://github.com/alekseyl
201
+ [@DmitryTsepelev]: https://github.com/DmitryTsepelev
202
+ [@sponomarev]: https://github.com/sponomarev
203
+ [@bibendi]: https://github.com/bibendi
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017-2023 palkan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ [![Gem Version](https://badge.fury.io/rb/anycable-rails.svg)](https://rubygems.org/gems/anycable-rails)
2
+ [![Build](https://github.com/anycable/anycable-rails/workflows/Build/badge.svg)](https://github.com/anycable/anycable-rails/actions)
3
+ [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/rails/getting_started)
4
+
5
+ # AnyCable Rails
6
+
7
+ AnyCable allows you to use any WebSocket server (written in any language) as a replacement for built-in Rails Action Cable server.
8
+
9
+ With AnyCable you can use channels, client-side JS, broadcasting - (almost) all that you can do with Action Cable.
10
+
11
+ You can even use Action Cable in development and not be afraid of [compatibility issues](#compatibility).
12
+
13
+ 💾 [Example Application](https://github.com/anycable/anycable_rails_demo)
14
+
15
+ 📑 [Documentation](https://docs.anycable.io/rails/getting_started).
16
+
17
+ > [AnyCable Pro](https://docs.anycable.io/pro) has been launched 🚀
18
+
19
+ <a href="https://evilmartians.com/">
20
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
21
+
22
+ ## Requirements
23
+
24
+ - Ruby >= 2.6
25
+ - Rails >= 6.0 (Rails 5.1 could work but we're no longer enforce compatibility on CI)
26
+ - Redis (see [other options](https://github.com/anycable/anycable/issues/2) for broadcasting)
27
+
28
+ ## Usage
29
+
30
+ Add `anycable-rails` gem to your Gemfile:
31
+
32
+ ```ruby
33
+ gem "anycable-rails"
34
+
35
+ # when using Redis broadcast adapter
36
+ gem "redis", ">= 4.0"
37
+ ```
38
+
39
+ ### Interactive set up
40
+
41
+ After the gem was installed, you can run an interactive wizard to configure your Rails application for using with AnyCable by running a generator:
42
+
43
+ ```sh
44
+ bundle exec rails g anycable:setup
45
+ ```
46
+
47
+ ### Manual set up
48
+
49
+ Specify AnyCable subscription adapter for Action Cable:
50
+
51
+ ```yml
52
+ # config/cable.yml
53
+ development:
54
+ adapter: any_cable # or anycable
55
+
56
+ production:
57
+ adapter: any_cable
58
+ ```
59
+
60
+ and specify AnyCable WebSocket server URL:
61
+
62
+ ```ruby
63
+ # For development it's likely the localhost
64
+
65
+ # config/environments/development.rb
66
+ config.action_cable.url = "ws://localhost:8080/cable"
67
+
68
+ # For production it's likely to have a sub-domain and secure connection
69
+
70
+ # config/environments/production.rb
71
+ config.action_cable.url = "wss://ws.example.com/cable"
72
+ ```
73
+
74
+ Then, run AnyCable RPC server:
75
+
76
+ ```sh
77
+ $ bundle exec anycable
78
+
79
+ # don't forget to provide Rails env
80
+
81
+ $ RAILS_ENV=production bundle exec anycable
82
+ ```
83
+
84
+ And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/anycable-go/getting_started):
85
+
86
+ ```sh
87
+ anycable-go --host=localhost --port=8080
88
+ ```
89
+
90
+ See [documentation](https://docs.anycable.io/rails/getting_started) for more information on AnyCable + Rails usage.
91
+
92
+ ## Action Cable Compatibility
93
+
94
+ See [documentation](https://docs.anycable.io/rails/compatibility).
95
+
96
+ ## Contributing
97
+
98
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/anycable/anycable-rails](https://github.com/anycable/anycable-rails).
99
+
100
+ ## License
101
+
102
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
103
+
104
+ ## Security Contact
105
+
106
+ To report a security vulnerability, please contact us at `anycable@evilmartians.com`. We will coordinate the fix and disclosure.
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anycable-rails"
4
+
5
+ module ActionCable
6
+ module SubscriptionAdapter
7
+ # AnyCable subscription adapter delegates broadcasts
8
+ # to AnyCable
9
+ class AnyCable < Base
10
+ ACTION_CABLE_SERVER_ERROR_MESSAGE = <<~STR
11
+ Looks like you're trying to connect to Rails Action Cable server, not an AnyCable one.
12
+
13
+ Please make sure your client is configured to connect to AnyCable server.
14
+
15
+ See https://docs.anycable.io/troubleshooting
16
+ STR
17
+
18
+ def initialize(*)
19
+ end
20
+
21
+ def broadcast(channel, payload)
22
+ ::AnyCable.broadcast(channel, payload)
23
+ end
24
+
25
+ def subscribe(*)
26
+ raise NotImplementedError, ACTION_CABLE_SERVER_ERROR_MESSAGE
27
+ end
28
+
29
+ def unsubscribe(*)
30
+ raise NotImplementedError, ACTION_CABLE_SERVER_ERROR_MESSAGE
31
+ end
32
+
33
+ def shutdown
34
+ # nothing to do
35
+ # we only need this method for development,
36
+ # 'cause code reloading triggers `server.restart` -> `pubsub.shutdown`
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "any_cable"
4
+
5
+ module ActionCable
6
+ module SubscriptionAdapter
7
+ # Aliasing adapter
8
+ Anycable = AnyCable
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_cable"
4
+
5
+ ActionCable::Channel::Base.prepend(Module.new do
6
+ def subscribe_to_channel
7
+ super unless anycabled? && !@__anycable_subscribing__
8
+ end
9
+
10
+ def handle_subscribe
11
+ @__anycable_subscribing__ = true
12
+ subscribe_to_channel
13
+ ensure
14
+ @__anycable_subscribing__ = false
15
+ end
16
+
17
+ def start_periodic_timers
18
+ super unless anycabled?
19
+ end
20
+
21
+ def stop_periodic_timers
22
+ super unless anycabled?
23
+ end
24
+
25
+ def stream_from(broadcasting, _callback = nil, **)
26
+ return super unless anycabled?
27
+
28
+ broadcasting = String(broadcasting)
29
+
30
+ connection.anycable_socket.subscribe identifier, broadcasting
31
+ end
32
+
33
+ def stop_stream_from(broadcasting)
34
+ return super unless anycabled?
35
+
36
+ connection.anycable_socket.unsubscribe identifier, broadcasting
37
+ end
38
+
39
+ def stop_all_streams
40
+ return super unless anycabled?
41
+
42
+ connection.anycable_socket.unsubscribe_from_all identifier
43
+ end
44
+
45
+ private
46
+
47
+ def anycabled?
48
+ # Use instance variable check here for testing compatibility
49
+ connection.instance_variable_defined?(:@anycable_socket)
50
+ end
51
+ end)
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_cable"
4
+ require "anycable/rails/connections/serializable_identification"
5
+
6
+ ActionCable::Connection::Base.include(AnyCable::Rails::Connections::SerializableIdentification)
7
+ ActionCable::Connection::Base.prepend(Module.new do
8
+ attr_reader :anycable_socket
9
+ attr_accessor :anycable_request_builder
10
+
11
+ # In AnyCable, we lazily populate env by passing it through the middleware chain,
12
+ # so we access it via #request
13
+ def env
14
+ return super unless anycabled?
15
+
16
+ request.env
17
+ end
18
+
19
+ def anycabled?
20
+ @anycable_socket
21
+ end
22
+
23
+ private
24
+
25
+ def request
26
+ return super unless anycabled?
27
+
28
+ @request ||= anycable_request_builder.build_rack_request(@env)
29
+ end
30
+ end)
31
+
32
+ # Backport command callbacks: https://github.com/rails/rails/pull/44696
33
+ unless ActionCable::Connection::Base.respond_to?(:before_command)
34
+ ActionCable::Connection::Base.include ActiveSupport::Callbacks
35
+ ActionCable::Connection::Base.define_callbacks :command
36
+ ActionCable::Connection::Base.extend(Module.new do
37
+ def before_command(*methods, &block)
38
+ set_callback(:command, :before, *methods, &block)
39
+ end
40
+
41
+ def after_command(*methods, &block)
42
+ set_callback(:command, :after, *methods, &block)
43
+ end
44
+
45
+ def around_command(*methods, &block)
46
+ set_callback(:command, :around, *methods, &block)
47
+ end
48
+ end)
49
+
50
+ ActionCable::Connection::Base.prepend(Module.new do
51
+ def dispatch_websocket_message(websocket_message)
52
+ return super unless websocket.alive?
53
+
54
+ handle_channel_command(decode(websocket_message))
55
+ end
56
+
57
+ def handle_channel_command(payload)
58
+ run_callbacks :command do
59
+ subscriptions.execute_command payload
60
+ end
61
+ end
62
+ end)
63
+ end
64
+
65
+ # Trigger autoload
66
+ test_case_defined = false
67
+
68
+ begin
69
+ ActionCable::Connection::TestCase # rubocop:disable Lint/Void
70
+ test_case_defined = true
71
+ rescue NameError
72
+ end
73
+
74
+ # Backport: https://github.com/rails/rails/pull/45445
75
+ if test_case_defined && !ActionCable::Connection::TestConnection.method_defined?(:transmissions)
76
+ ActionCable::Connection::TestConnection.prepend(Module.new do
77
+ attr_reader :transmissions
78
+
79
+ def initialize(*)
80
+ super
81
+
82
+ @transmissions = []
83
+ @subscriptions = ActionCable::Connection::Subscriptions.new(self)
84
+ end
85
+
86
+ def transmit(cable_message)
87
+ transmissions << cable_message.with_indifferent_access
88
+ end
89
+ end)
90
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_cable/remote_connections"
4
+
5
+ ActionCable::RemoteConnections::RemoteConnection.include(AnyCable::Rails::Connections::SerializableIdentification)
6
+
7
+ ActionCable::RemoteConnections::RemoteConnection.prepend(Module.new do
8
+ def disconnect(reconnect: true)
9
+ # Legacy Action Cable functionality if case we're not fully migrated yet
10
+ super() unless AnyCable::Rails.enabled?
11
+ ::AnyCable.broadcast_adapter.broadcast_command("disconnect", identifier: identifiers_json, reconnect: reconnect)
12
+ end
13
+ end)
@@ -0,0 +1,108 @@
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
+ names.each do |name|
9
+ channel_state_attributes << name
10
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
11
+ def #{name}
12
+ return @#{name} if instance_variable_defined?(:@#{name})
13
+ @#{name} = AnyCable::Rails.deserialize(__istate__["#{name}"], json: true) if anycabled?
14
+ end
15
+
16
+ def #{name}=(val)
17
+ __istate__["#{name}"] = AnyCable::Rails.serialize(val, json: true) if anycabled?
18
+ instance_variable_set(:@#{name}, val)
19
+ end
20
+ RUBY
21
+ end
22
+ end
23
+
24
+ def channel_state_attributes
25
+ return @channel_state_attributes if instance_variable_defined?(:@channel_state_attributes)
26
+
27
+ @channel_state_attributes =
28
+ if superclass.respond_to?(:channel_state_attributes)
29
+ superclass.channel_state_attributes.dup
30
+ else
31
+ []
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.included(base)
37
+ base.extend ClassMethods
38
+ end
39
+
40
+ # Make it possible to provide istate explicitly for a channel instance
41
+ attr_writer :__istate__
42
+
43
+ def __istate__
44
+ @__istate__ ||= connection.anycable_socket.istate
45
+ end
46
+ end
47
+
48
+ module ConnectionState
49
+ module ClassMethods
50
+ def state_attr_accessor(*names)
51
+ names.each do |name|
52
+ connection_state_attributes << name
53
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
54
+ def #{name}
55
+ return @#{name} if instance_variable_defined?(:@#{name})
56
+ @#{name} = AnyCable::Rails.deserialize(__cstate__["#{name}"], json: true) if anycabled?
57
+ end
58
+
59
+ def #{name}=(val)
60
+ __cstate__["#{name}"] = AnyCable::Rails.serialize(val, json: true) if anycabled?
61
+ instance_variable_set(:@#{name}, val)
62
+ end
63
+ RUBY
64
+ end
65
+ end
66
+
67
+ def connection_state_attributes
68
+ return @connection_state_attributes if instance_variable_defined?(:@connection_state_attributes)
69
+
70
+ @connection_state_attributes =
71
+ if superclass.respond_to?(:connection_state_attributes)
72
+ superclass.connection_state_attributes.dup
73
+ else
74
+ []
75
+ end
76
+ end
77
+ end
78
+
79
+ def self.included(base)
80
+ base.extend ClassMethods
81
+ end
82
+
83
+ # Make it possible to provide istate explicitly for a connection instance
84
+ attr_writer :__cstate__
85
+
86
+ def __cstate__
87
+ @__cstate__ ||= anycable_socket.cstate
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ if ActiveSupport::VERSION::MAJOR < 6
94
+ # `state_attr_accessor` must be available in Action Cable
95
+ ActiveSupport.on_load(:action_cable) do
96
+ ::ActionCable::Connection::Base.include(AnyCable::Rails::ConnectionState)
97
+ ::ActionCable::Channel::Base.include(AnyCable::Rails::ChannelState)
98
+ end
99
+ else
100
+ # `state_attr_accessor` must be available in Action Cable
101
+ ActiveSupport.on_load(:action_cable_connection) do
102
+ ::ActionCable::Connection::Base.include(AnyCable::Rails::ConnectionState)
103
+ end
104
+
105
+ ActiveSupport.on_load(:action_cable_channel) do
106
+ ::ActionCable::Channel::Base.include(AnyCable::Rails::ChannelState)
107
+ end
108
+ end
@@ -0,0 +1,14 @@
1
+ AnyCable/InstanceVars:
2
+ Enabled: true
3
+ Include:
4
+ - "**/channels/**/*.rb"
5
+
6
+ AnyCable/StreamFrom:
7
+ Enabled: true
8
+ Include:
9
+ - "**/channels/**/*.rb"
10
+
11
+ AnyCable/PeriodicalTimers:
12
+ Enabled: true
13
+ Include:
14
+ - "**/channels/**/*.rb"