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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 06a551d757afa07138babbf351024554d52b667ab35893bc995cc1fb56edd8af
4
- data.tar.gz: c630ecf586cfd0068b0d248c00dc1372ffd7d83a3abf0e76dc9ecd52ae960a7c
3
+ metadata.gz: 395f76c02adbd3f4878b55bb21bf4bd00969e4394e824fb7a23f086d8b221b1c
4
+ data.tar.gz: b300f5b2ab64aa893d462a22045587f65531bbaea8dce3a920c1fb1df67b211e
5
5
  SHA512:
6
- metadata.gz: 2d46a5b21269ef7cb9cfc562ecee2cfd9830e055dd18fb20bce468409f0a84bc3ca3dc1ba56d9d340202039de3ab93aeba2e600388b7130b99bff101cbf00448
7
- data.tar.gz: 64a083bf7d4046b5a1bfa57058f0aa1cedded844067b15f5f9ea660a8d5291bbe2c8f6db6c4a63768f2d17f67d40bb920f4abe8e55b9fbc9241f72c4a4d9c587
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-rails.svg)](https://rubygems.org/gems/anycable-rails)
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
- AnyCable allows you to use any WebSocket server (written in any language) as a replacement for built-in Rails Action Cable server.
7
+ <img align="right" height="150" width="129"
8
+ title="AnyCable logo" src="https://docs.anycable.io/assets/images/logo.svg">
8
9
 
9
- With AnyCable you can use channels, client-side JS, broadcasting - (almost) all that you can do with Action Cable.
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
- ๐Ÿ“‘ [Documentation](https://docs.anycable.io/rails/getting_started).
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
- ### Interactive set up
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
- ### Manual set up
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Rails
5
+ module Channel
6
+ autoload :Presence, "anycable/rails/channel/presence"
7
+ end
8
+ end
9
+ end
@@ -31,65 +31,15 @@ module AnyCable
31
31
  end
32
32
 
33
33
  refine ActionCable::Connection::Subscriptions do
34
- # Override the original #execute_command to pre-initialize the channel for unsubscribe/message and
35
- # return true/false to indicate successful/unsuccessful subscription.
36
- # We also must not lose any exceptions raised in the process.
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
- case data["command"]
50
- when "unsubscribe"
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
- true
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
- conn.subscriptions.restore(subscriptions, socket.istate) if subscriptions
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
- conn.subscriptions.execute_rpc_command({"command" => command, "identifier" => identifier, "data" => data})
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rails
5
- VERSION = "1.5.6"
5
+ VERSION = "1.6.0.rc.1"
6
6
  end
7
7
  end
@@ -9,6 +9,7 @@ require "globalid"
9
9
  require "active_support/core_ext/module/attribute_accessors_per_thread"
10
10
 
11
11
  require "anycable/rails/ext"
12
+ require "anycable/rails/channel"
12
13
 
13
14
  module AnyCable
14
15
  # Rails handler for AnyCable
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.5.6
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-15 00:00:00.000000000 Z
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.5.0
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.5.0
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: '0'
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: []