anycable-rails-core 1.5.6 โ 1.6.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -71
- data/lib/anycable/rails/action_cable_ext/channel.rb +11 -0
- data/lib/anycable/rails/channel/presence.rb +36 -0
- data/lib/anycable/rails/channel.rb +9 -0
- data/lib/anycable/rails/connection.rb +33 -58
- data/lib/anycable/rails/connections/serializable_identification.rb +14 -0
- data/lib/anycable/rails/railtie.rb +3 -2
- data/lib/anycable/rails/version.rb +1 -1
- data/lib/anycable/rails.rb +1 -0
- metadata +19 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 395f76c02adbd3f4878b55bb21bf4bd00969e4394e824fb7a23f086d8b221b1c
|
4
|
+
data.tar.gz: b300f5b2ab64aa893d462a22045587f65531bbaea8dce3a920c1fb1df67b211e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0a2f228649539c86b8e3f1a96f0c456b6ed0f933552fb93c90ef2d4a5c49261fdbdf9f3e7b650cd69b00ca38bddcf202bb6f1d730aa92ad07ec3f8504128904
|
7
|
+
data.tar.gz: dfce4029440d91a5a7f07abccca089b6b0bb759d152aa2fadad9ccb12ca0533a5d10cba6e200f398ce58c54c76c5b0fa5903c48aeacba2354dc8b5f9ad37d10f
|
data/README.md
CHANGED
@@ -1,30 +1,18 @@
|
|
1
|
-
[![Gem Version](https://badge.fury.io/rb/anycable
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/anycable.svg)](https://rubygems.org/gems/anycable)
|
2
2
|
[![Build](https://github.com/anycable/anycable-rails/workflows/Build/badge.svg)](https://github.com/anycable/anycable-rails/actions)
|
3
3
|
[![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/rails/getting_started)
|
4
4
|
|
5
|
-
# AnyCable Rails
|
5
|
+
# AnyCable SDK for Ruby on Rails
|
6
6
|
|
7
|
-
|
7
|
+
<img align="right" height="150" width="129"
|
8
|
+
title="AnyCable logo" src="https://docs.anycable.io/assets/images/logo.svg">
|
8
9
|
|
9
|
-
|
10
|
+
[AnyCable](https://github.com/anycable/anycable) is an open-source language-agnostic realtime server for reliable two-way communication over WebSockets and SSE.
|
11
|
+
This repository contains code for AnyCable Rails SKD that allows you to use AnyCable as a drop-in replacement for Action Cable.
|
10
12
|
|
11
|
-
๐พ [Example Application](https://github.com/anycable/anycable_rails_demo)
|
13
|
+
๐ [Website](https://anycable.io) ยท ๐ [Documentation](https://docs.anycable.io/rails/getting_started) ยท ๐พ [Example Rails Application](https://github.com/anycable/anycable_rails_demo)
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
> [AnyCable Pro](https://docs.anycable.io/pro) has been launched ๐
|
16
|
-
|
17
|
-
<a href="https://evilmartians.com/">
|
18
|
-
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
19
|
-
|
20
|
-
## Requirements
|
21
|
-
|
22
|
-
- Ruby >= 3.1
|
23
|
-
- Rails >= 6.0\*
|
24
|
-
|
25
|
-
\* Recent `anycable-rails` versions only work with Rails 8+; older versions compatible with Rails 6 and Rails 7 still receive fixes and minor updates (patch releases).
|
26
|
-
|
27
|
-
## Usage
|
15
|
+
## Quick Start
|
28
16
|
|
29
17
|
Add `anycable-rails` gem to your Gemfile:
|
30
18
|
|
@@ -32,62 +20,13 @@ Add `anycable-rails` gem to your Gemfile:
|
|
32
20
|
gem "anycable-rails"
|
33
21
|
```
|
34
22
|
|
35
|
-
|
36
|
-
|
37
|
-
After the gem was installed, you can run an interactive wizard to configure your Rails application for using with AnyCable by running a generator:
|
23
|
+
Then run our interactive setup command:
|
38
24
|
|
39
25
|
```sh
|
40
26
|
bundle exec rails g anycable:setup
|
41
27
|
```
|
42
28
|
|
43
|
-
|
44
|
-
|
45
|
-
Specify AnyCable subscription adapter for Action Cable:
|
46
|
-
|
47
|
-
```yml
|
48
|
-
# config/cable.yml
|
49
|
-
development:
|
50
|
-
adapter: any_cable # or anycable
|
51
|
-
|
52
|
-
production:
|
53
|
-
adapter: any_cable
|
54
|
-
```
|
55
|
-
|
56
|
-
and specify AnyCable WebSocket server URL:
|
57
|
-
|
58
|
-
```ruby
|
59
|
-
# For development it's likely the localhost
|
60
|
-
|
61
|
-
# config/environments/development.rb
|
62
|
-
config.action_cable.url = "ws://localhost:8080/cable"
|
63
|
-
|
64
|
-
# For production it's likely to have a sub-domain and secure connection
|
65
|
-
|
66
|
-
# config/environments/production.rb
|
67
|
-
config.action_cable.url = "wss://ws.example.com/cable"
|
68
|
-
```
|
69
|
-
|
70
|
-
Then, run AnyCable RPC server:
|
71
|
-
|
72
|
-
```sh
|
73
|
-
$ bundle exec anycable
|
74
|
-
|
75
|
-
# don't forget to provide Rails env
|
76
|
-
|
77
|
-
$ RAILS_ENV=production bundle exec anycable
|
78
|
-
```
|
79
|
-
|
80
|
-
And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/anycable-go/getting_started):
|
81
|
-
|
82
|
-
```sh
|
83
|
-
anycable-go --host=localhost --port=8080
|
84
|
-
```
|
85
|
-
|
86
|
-
See [documentation](https://docs.anycable.io/rails/getting_started) for more information on AnyCable + Rails usage.
|
87
|
-
|
88
|
-
## Action Cable Compatibility
|
89
|
-
|
90
|
-
See [documentation](https://docs.anycable.io/rails/compatibility).
|
29
|
+
Learn more [here](https://docs.anycable.io/rails/getting_started).
|
91
30
|
|
92
31
|
## Contributing
|
93
32
|
|
@@ -3,6 +3,17 @@
|
|
3
3
|
require "action_cable"
|
4
4
|
|
5
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
|
+
|
6
17
|
def start_periodic_timers
|
7
18
|
super unless anycabled?
|
8
19
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AnyCable
|
4
|
+
module Rails
|
5
|
+
module Channel
|
6
|
+
# Presence API for Action Cable channels (backed by AnyCable)
|
7
|
+
module Presence
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
def join_presence(stream = nil, id: user_presence_id, info: user_presence_info)
|
11
|
+
return unless anycabled?
|
12
|
+
|
13
|
+
stream ||= connection.anycable_socket.streams[:start].first || raise(ArgumentError, "Provide a stream name for presence updates")
|
14
|
+
|
15
|
+
connection.anycable_socket.presence_join(stream, id, info)
|
16
|
+
end
|
17
|
+
|
18
|
+
def leave_presence(id = user_presence_id)
|
19
|
+
return unless anycabled?
|
20
|
+
|
21
|
+
connection.anycable_socket.presence_leave(id)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def user_presence_id
|
27
|
+
connection.connection_identifier
|
28
|
+
end
|
29
|
+
|
30
|
+
def user_presence_info
|
31
|
+
# nothing
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -31,65 +31,15 @@ module AnyCable
|
|
31
31
|
end
|
32
32
|
|
33
33
|
refine ActionCable::Connection::Subscriptions do
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
def execute_rpc_command(data)
|
38
|
-
# First, verify the channel name
|
39
|
-
raise "Channel not found: #{ActiveSupport::JSON.decode(data["identifier"]).fetch("channel")}" unless subscription_class_from_identifier(data["identifier"])
|
40
|
-
|
41
|
-
if data["command"] == "subscribe"
|
42
|
-
add data
|
43
|
-
subscription = subscriptions[data["identifier"]]
|
44
|
-
return !(subscription.nil? || subscription.rejected?)
|
45
|
-
end
|
46
|
-
|
47
|
-
load(data["identifier"])
|
34
|
+
# Find or add a subscription to the list
|
35
|
+
def fetch(identifier)
|
36
|
+
add("identifier" => identifier) unless subscriptions[identifier]
|
48
37
|
|
49
|
-
|
50
|
-
|
51
|
-
remove data
|
52
|
-
when "message"
|
53
|
-
perform_action data
|
54
|
-
when "whisper"
|
55
|
-
whisper data
|
56
|
-
else
|
57
|
-
raise UnknownCommandError, data["command"]
|
38
|
+
unless subscriptions[identifier]
|
39
|
+
raise "Channel not found: #{ActiveSupport::JSON.decode(identifier).fetch("channel")}"
|
58
40
|
end
|
59
41
|
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
# Restore channels from the list of identifiers and the state
|
64
|
-
def restore(subscriptions, istate)
|
65
|
-
subscriptions.each do |id|
|
66
|
-
channel = load(id)
|
67
|
-
channel.__istate__ = ActiveSupport::JSON.decode(istate[id]) if istate[id]
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Find or create a channel for a given identifier
|
72
|
-
def load(identifier)
|
73
|
-
return subscriptions[identifier] if subscriptions[identifier]
|
74
|
-
|
75
|
-
subscription = subscription_from_identifier(identifier)
|
76
|
-
raise "Channel not found: #{ActiveSupport::JSON.decode(identifier).fetch("channel")}" unless subscription
|
77
|
-
|
78
|
-
subscriptions[identifier] = subscription
|
79
|
-
end
|
80
|
-
|
81
|
-
def subscription_class_from_identifier(id_key)
|
82
|
-
id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
|
83
|
-
id_options[:channel].safe_constantize
|
84
|
-
end
|
85
|
-
|
86
|
-
def subscription_from_identifier(id_key)
|
87
|
-
subscription_klass = subscription_class_from_identifier(id_key)
|
88
|
-
|
89
|
-
if subscription_klass && subscription_klass < ActionCable::Channel::Base
|
90
|
-
id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
|
91
|
-
subscription_klass.new(connection, id_key, id_options)
|
92
|
-
end
|
42
|
+
subscriptions[identifier]
|
93
43
|
end
|
94
44
|
end
|
95
45
|
end)
|
@@ -124,8 +74,15 @@ module AnyCable
|
|
124
74
|
conn.cached_ids = {}
|
125
75
|
conn.anycable_request_builder = self
|
126
76
|
|
77
|
+
return unless subscriptions
|
78
|
+
|
127
79
|
# Pre-initialize channels (for disconnect)
|
128
|
-
|
80
|
+
subscriptions.each do |id|
|
81
|
+
channel = conn.subscriptions.fetch(id)
|
82
|
+
next unless socket.istate[id]
|
83
|
+
|
84
|
+
channel.__istate__ = ActiveSupport::JSON.decode(socket.istate[id])
|
85
|
+
end
|
129
86
|
end
|
130
87
|
|
131
88
|
def handle_open
|
@@ -154,8 +111,26 @@ module AnyCable
|
|
154
111
|
|
155
112
|
def handle_channel_command(identifier, command, data)
|
156
113
|
conn.run_callbacks :command do
|
157
|
-
|
114
|
+
# We cannot use subscriptions#execute_command here,
|
115
|
+
# since we MUST return true of false, depending on the status
|
116
|
+
# of execution
|
117
|
+
channel = conn.subscriptions.fetch(identifier)
|
118
|
+
case command
|
119
|
+
when "subscribe"
|
120
|
+
channel.handle_subscribe
|
121
|
+
!channel.rejected?
|
122
|
+
when "unsubscribe"
|
123
|
+
conn.subscriptions.remove_subscription(channel)
|
124
|
+
true
|
125
|
+
when "message"
|
126
|
+
channel.perform_action ActiveSupport::JSON.decode(data)
|
127
|
+
true
|
128
|
+
else
|
129
|
+
false
|
130
|
+
end
|
158
131
|
end
|
132
|
+
# Support rescue_from
|
133
|
+
# https://github.com/rails/rails/commit/d2571e560c62116f60429c933d0c41a0e249b58b
|
159
134
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
160
135
|
rescue_with_handler(e) || raise
|
161
136
|
false
|
@@ -6,6 +6,20 @@ module AnyCable
|
|
6
6
|
module SerializableIdentification
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
module ConnectionGID
|
10
|
+
def connection_identifier
|
11
|
+
unless defined? @connection_identifier
|
12
|
+
@connection_identifier = connection_gid identifiers.filter_map { |id| instance_variable_get(:"@#{id}") || __send__(id) }
|
13
|
+
end
|
14
|
+
|
15
|
+
@connection_identifier
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
included do
|
20
|
+
prepend ConnectionGID
|
21
|
+
end
|
22
|
+
|
9
23
|
class_methods do
|
10
24
|
def identified_by(*identifiers)
|
11
25
|
super
|
@@ -123,11 +123,12 @@ module AnyCable
|
|
123
123
|
|
124
124
|
initializer "anycable.verify_pool_sizes" do
|
125
125
|
next if AnyCable.config.disable_rpc_pool_size_warning?
|
126
|
-
# Skip if non-gRPC server is used
|
127
|
-
next unless AnyCable.config.respond_to?(:rpc_pool_size)
|
128
126
|
|
129
127
|
# Log current db vs. gRPC pool sizes
|
130
128
|
AnyCable.configure_server do
|
129
|
+
# Skip if non-gRPC server is used
|
130
|
+
next unless AnyCable.config.respond_to?(:rpc_pool_size)
|
131
|
+
|
131
132
|
ActiveSupport.on_load(:active_record) do
|
132
133
|
db_pool_size = ::ActiveRecord::Base.connection_pool.size
|
133
134
|
rpc_pool_size = AnyCable.config.rpc_pool_size
|
data/lib/anycable/rails.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anycable-rails-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0.rc.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anycable-core
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.0.rc.1
|
20
|
+
- - "<"
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
22
|
+
version: 1.7.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
29
|
+
version: 1.6.0.rc.1
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.7.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: actioncable
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,6 +83,8 @@ files:
|
|
77
83
|
- lib/anycable/rails/action_cable_ext/channel.rb
|
78
84
|
- lib/anycable/rails/action_cable_ext/connection.rb
|
79
85
|
- lib/anycable/rails/action_cable_ext/remote_connections.rb
|
86
|
+
- lib/anycable/rails/channel.rb
|
87
|
+
- lib/anycable/rails/channel/presence.rb
|
80
88
|
- lib/anycable/rails/channel_state.rb
|
81
89
|
- lib/anycable/rails/compatibility.rb
|
82
90
|
- lib/anycable/rails/compatibility/rubocop.rb
|
@@ -131,7 +139,7 @@ metadata:
|
|
131
139
|
homepage_uri: https://anycable.io/
|
132
140
|
source_code_uri: http://github.com/anycable/anycable-rails
|
133
141
|
funding_uri: https://github.com/sponsors/anycable
|
134
|
-
post_install_message:
|
142
|
+
post_install_message:
|
135
143
|
rdoc_options: []
|
136
144
|
require_paths:
|
137
145
|
- lib
|
@@ -142,12 +150,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
150
|
version: '2.7'
|
143
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
152
|
requirements:
|
145
|
-
- - "
|
153
|
+
- - ">"
|
146
154
|
- !ruby/object:Gem::Version
|
147
|
-
version:
|
155
|
+
version: 1.3.1
|
148
156
|
requirements: []
|
149
157
|
rubygems_version: 3.4.19
|
150
|
-
signing_key:
|
158
|
+
signing_key:
|
151
159
|
specification_version: 4
|
152
160
|
summary: AnyCable integration for Rails (w/o RPC dependencies)
|
153
161
|
test_files: []
|