anycable-rails-core 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +203 -0
- data/MIT-LICENSE +20 -0
- data/README.md +106 -0
- data/lib/action_cable/subscription_adapter/any_cable.rb +40 -0
- data/lib/action_cable/subscription_adapter/anycable.rb +10 -0
- data/lib/anycable/rails/action_cable_ext/channel.rb +51 -0
- data/lib/anycable/rails/action_cable_ext/connection.rb +90 -0
- data/lib/anycable/rails/action_cable_ext/remote_connections.rb +13 -0
- data/lib/anycable/rails/channel_state.rb +108 -0
- data/lib/anycable/rails/compatibility/rubocop/config/default.yml +14 -0
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +50 -0
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/periodical_timers.rb +29 -0
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +100 -0
- data/lib/anycable/rails/compatibility/rubocop.rb +27 -0
- data/lib/anycable/rails/compatibility.rb +63 -0
- data/lib/anycable/rails/config.rb +19 -0
- data/lib/anycable/rails/connection.rb +211 -0
- data/lib/anycable/rails/connection_factory.rb +44 -0
- data/lib/anycable/rails/connections/persistent_session.rb +40 -0
- data/lib/anycable/rails/connections/serializable_identification.rb +46 -0
- data/lib/anycable/rails/connections/session_proxy.rb +81 -0
- data/lib/anycable/rails/middlewares/executor.rb +31 -0
- data/lib/anycable/rails/middlewares/log_tagging.rb +21 -0
- data/lib/anycable/rails/rack.rb +56 -0
- data/lib/anycable/rails/railtie.rb +92 -0
- data/lib/anycable/rails/version.rb +7 -0
- data/lib/anycable/rails.rb +76 -0
- data/lib/anycable-rails.rb +3 -0
- data/lib/generators/anycable/download/USAGE +14 -0
- data/lib/generators/anycable/download/download_generator.rb +85 -0
- data/lib/generators/anycable/setup/USAGE +2 -0
- data/lib/generators/anycable/setup/setup_generator.rb +300 -0
- data/lib/generators/anycable/setup/templates/Procfile.dev.tt +6 -0
- data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +48 -0
- data/lib/generators/anycable/setup/templates/config/cable.yml.tt +11 -0
- data/lib/generators/anycable/setup/templates/config/initializers/anycable.rb.tt +9 -0
- data/lib/generators/anycable/with_os_helpers.rb +55 -0
- 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,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
|