anycable-rails 1.0.7 → 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anycable/rails/connections/session_proxy"
4
+
5
+ module AnyCable
6
+ module Rails
7
+ module Connections
8
+ module PersistentSession
9
+ def handle_open
10
+ super.tap { commit_session! }
11
+ end
12
+
13
+ def handle_channel_command(*)
14
+ super.tap { commit_session! }
15
+ end
16
+
17
+ def build_rack_request(env)
18
+ return super unless socket.session
19
+
20
+ super.tap do |req|
21
+ req.env[::Rack::RACK_SESSION] =
22
+ SessionProxy.new(req.env[::Rack::RACK_SESSION], socket.session)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def commit_session!
29
+ return unless request_loaded? && request.session.respond_to?(:loaded?) && request.session.loaded?
30
+
31
+ socket.session = request.session.to_json
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ AnyCable::Rails::Connection.prepend(
39
+ AnyCable::Rails::Connections::PersistentSession
40
+ )
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Rails
5
+ module Connections
6
+ module SerializableIdentification
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def identified_by(*identifiers)
11
+ super
12
+ Array(identifiers).each do |identifier|
13
+ define_method(identifier) do
14
+ instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # Generate identifiers info.
21
+ # Converts GlobalID compatible vars to corresponding global IDs params.
22
+ def identifiers_hash
23
+ identifiers.each_with_object({}) do |id, acc|
24
+ obj = instance_variable_get("@#{id}")
25
+ next unless obj
26
+
27
+ acc[id] = AnyCable::Rails.serialize(obj)
28
+ end.compact
29
+ end
30
+
31
+ def identifiers_json
32
+ identifiers_hash.to_json
33
+ end
34
+
35
+ # Fetch identifier and deserialize if neccessary
36
+ def fetch_identifier(name)
37
+ return unless @cached_ids
38
+
39
+ @cached_ids[name] ||= @cached_ids.fetch(name) do
40
+ AnyCable::Rails.deserialize(@serialized_ids[name.to_s])
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Rails
5
+ module Connections
6
+ # Wrap `request.session` to lazily load values provided
7
+ # in the RPC call (set by the previous calls)
8
+ class SessionProxy
9
+ attr_reader :rack_session, :socket_session
10
+
11
+ def initialize(rack_session, socket_session)
12
+ @rack_session = rack_session
13
+ @socket_session = JSON.parse(socket_session).with_indifferent_access
14
+ end
15
+
16
+ %i[has_key? [] []= fetch delete dig].each do |mid|
17
+ class_eval <<~CODE, __FILE__, __LINE__ + 1
18
+ def #{mid}(*args, **kwargs, &block)
19
+ restore_key! args.first
20
+ rack_session.#{mid}(*args, **kwargs, &block)
21
+ end
22
+ CODE
23
+ end
24
+
25
+ alias_method :include?, :has_key?
26
+ alias_method :key?, :has_key?
27
+
28
+ %i[update merge! to_hash].each do |mid|
29
+ class_eval <<~CODE, __FILE__, __LINE__ + 1
30
+ def #{mid}(*args, **kwargs, &block)
31
+ restore!
32
+ rack_session.#{mid}(*args, **kwargs, &block)
33
+ end
34
+ CODE
35
+ end
36
+
37
+ alias_method :to_h, :to_hash
38
+
39
+ def keys
40
+ rack_session.keys + socket_session.keys
41
+ end
42
+
43
+ # Delegate both publuc and private methods to rack_session
44
+ def respond_to_missing?(name, include_private = false)
45
+ return false if name == :marshal_dump || name == :_dump
46
+ rack_session.respond_to?(name, include_private) || super
47
+ end
48
+
49
+ def method_missing(method, *args, &block)
50
+ if rack_session.respond_to?(method, true)
51
+ rack_session.send(method, *args, &block)
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ # This method is used by StimulusReflex to obtain `@by`
58
+ def instance_variable_get(name)
59
+ super || rack_session.instance_variable_get(name)
60
+ end
61
+
62
+ private
63
+
64
+ def restore!
65
+ socket_session.keys.each(&method(:restore_key!))
66
+ end
67
+
68
+ def restore_key!(key)
69
+ return unless socket_session.key?(key)
70
+ val = socket_session.delete(key)
71
+ rack_session[key] =
72
+ if val.is_a?(String)
73
+ GlobalID::Locator.locate(val) || val
74
+ else
75
+ val
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -9,8 +9,8 @@ module AnyCable
9
9
  #
10
10
  # See https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md
11
11
  class LogTagging < AnyCable::Middleware
12
- def call(_request, call, _method)
13
- sid = call.metadata["sid"]
12
+ def call(_method, _request, metadata)
13
+ sid = metadata["sid"]
14
14
  return yield unless sid
15
15
 
16
16
  AnyCable.logger.tagged("AnyCable sid=#{sid}") { yield }
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "anycable/rails/action_cable_ext/connection"
4
+ require "anycable/rails/action_cable_ext/channel"
5
+ require "anycable/rails/action_cable_ext/remote_connections"
6
+
3
7
  require "anycable/rails/channel_state"
8
+ require "anycable/rails/connection_factory"
4
9
 
5
10
  module AnyCable
6
11
  module Rails
@@ -15,13 +20,14 @@ module AnyCable
15
20
  AnyCable.logger = ::ActionCable.server.config.logger
16
21
 
17
22
  AnyCable.configure_server do
18
- AnyCable.logger = ActiveSupport::TaggedLogging.new(::ActionCable.server.config.logger)
23
+ server_logger = AnyCable.logger = ::ActionCable.server.config.logger
24
+ AnyCable.logger = ActiveSupport::TaggedLogging.new(server_logger) if server_logger.is_a?(::Logger)
19
25
  # Broadcast server logs to STDOUT in development
20
26
  if ::Rails.env.development? &&
21
27
  !ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
22
28
  console = ActiveSupport::Logger.new($stdout)
23
- console.formatter = ::Rails.logger.formatter
24
- console.level = ::Rails.logger.level
29
+ console.formatter = ::Rails.logger.formatter if ::Rails.logger.respond_to?(:formatter)
30
+ console.level = ::Rails.logger.level if ::Rails.logger.respond_to?(:level)
25
31
  AnyCable.logger.extend(ActiveSupport::Logger.broadcast(console))
26
32
  end
27
33
  end
@@ -43,22 +49,25 @@ module AnyCable
43
49
 
44
50
  initializer "anycable.connection_factory", after: "action_cable.set_configs" do |app|
45
51
  ActiveSupport.on_load(:action_cable) do
46
- # Add AnyCable patch method stub (we use it in ChannelState to distinguish between Action Cable and AnyCable)
47
- # NOTE: Method could be already defined if patch was loaded manually
48
- ActionCable::Connection::Base.attr_reader(:anycable_socket) unless ActionCable::Connection::Base.method_defined?(:anycable_socket)
49
-
50
52
  app.config.to_prepare do
51
- AnyCable.connection_factory = ActionCable.server.config.connection_class.call
53
+ AnyCable.connection_factory = AnyCable::Rails::ConnectionFactory.new
52
54
  end
53
55
 
54
- if AnyCable::Rails.enabled?
55
- require "anycable/rails/actioncable/connection"
56
- if AnyCable.config.persistent_session_enabled
57
- require "anycable/rails/actioncable/connection/persistent_session"
58
- end
56
+ if AnyCable.config.persistent_session_enabled?
57
+ require "anycable/rails/connections/persistent_session"
59
58
  end
60
59
  end
61
60
  end
61
+
62
+ # Since Rails 6.1
63
+ if respond_to?(:server)
64
+ server do
65
+ next unless AnyCable.config.embedded? && AnyCable::Rails.enabled?
66
+
67
+ require "anycable/cli"
68
+ AnyCable::CLI.embed!
69
+ end
70
+ end
62
71
  end
63
72
  end
64
73
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rails
5
- VERSION = "1.0.7"
5
+ VERSION = "1.3.4"
6
6
  end
7
7
  end
@@ -48,6 +48,17 @@ module AnyCable
48
48
  val
49
49
  end
50
50
  end
51
+
52
+ module Extension
53
+ def broadcast(channel, payload)
54
+ super
55
+ ::AnyCable.broadcast(channel, payload)
56
+ end
57
+ end
58
+
59
+ def extend_adapter!(adapter)
60
+ adapter.extend(Extension)
61
+ end
51
62
  end
52
63
  end
53
64
  end
@@ -8,7 +8,7 @@ module AnyCableRailsGenerators
8
8
  namespace "anycable:setup"
9
9
  source_root File.expand_path("templates", __dir__)
10
10
 
11
- DOCS_ROOT = "https://docs.anycable.io/#"
11
+ DOCS_ROOT = "https://docs.anycable.io"
12
12
  DEVELOPMENT_METHODS = %w[skip local docker].freeze
13
13
  SERVER_SOURCES = %w[skip brew binary].freeze
14
14
 
@@ -24,6 +24,12 @@ module AnyCableRailsGenerators
24
24
  class_option :skip_procfile_dev,
25
25
  type: :boolean,
26
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"
27
33
 
28
34
  include WithOSHelpers
29
35
 
@@ -59,7 +65,7 @@ module AnyCableRailsGenerators
59
65
  <<~SNIPPET
60
66
  # Specify AnyCable WebSocket server URL to use by JS client
61
67
  config.after_initialize do
62
- config.action_cable.url = ActionCable.server.config.url = ENV.fetch("CABLE_URL") if AnyCable::Rails.enabled?
68
+ config.action_cable.url = ActionCable.server.config.url = ENV.fetch("CABLE_URL", "/cable") if AnyCable::Rails.enabled?
63
69
  end
64
70
  SNIPPET
65
71
  end
@@ -118,7 +124,7 @@ module AnyCableRailsGenerators
118
124
  def stimulus_reflex
119
125
  return unless stimulus_reflex?
120
126
 
121
- say_status :help, "⚠️ Please, check out the documentation on using AnyCable with Stimulus Reflex: https://docs.anycable.io/#/rails/stimulus_reflex"
127
+ say_status :help, "⚠️ Please, check out the documentation on using AnyCable with Stimulus Reflex: #{DOCS_ROOT}/rails/stimulus_reflex"
122
128
  end
123
129
 
124
130
  def rubocop_compatibility
@@ -126,7 +132,17 @@ module AnyCableRailsGenerators
126
132
 
127
133
  say_status :info, "🤖 Running static compatibility checks with RuboCop"
128
134
  res = run "bundle exec rubocop -r 'anycable/rails/compatibility/rubocop' --only AnyCable/InstanceVars,AnyCable/PeriodicalTimers,AnyCable/InstanceVars"
129
- say_status :help, "⚠️ Please, take a look at the icompatibilities above and fix them. See https://docs.anycable.io/#/rails/compatibility" unless res
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}"
130
146
  end
131
147
 
132
148
  def finish
@@ -172,7 +188,7 @@ module AnyCableRailsGenerators
172
188
  say <<~YML
173
189
  ─────────────────────────────────────────
174
190
  ws:
175
- image: anycable/anycable-go:1.0
191
+ image: anycable/anycable-go:1.2
176
192
  ports:
177
193
  - '8080:8080'
178
194
  environment:
@@ -28,7 +28,7 @@ default: &default
28
28
  broadcast_adapter: http
29
29
  <%- end -%>
30
30
  # Use the same channel name for WebSocket server, e.g.:
31
- # $ anycable-go --redis-channel="__anycable__"
31
+ # $ anycable-go --redis_channel="__anycable__"
32
32
  redis_channel: "__anycable__"
33
33
  <%- if redis? -%>
34
34
  # You can use REDIS_URL env var to configure Redis URL.
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-05 00:00:00.000000000 Z
11
+ date: 2022-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anycable
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.0
19
+ version: 1.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.0.0
26
+ version: 1.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: actioncable
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5'
33
+ version: '6.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5'
40
+ version: '6.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: globalid
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: activerecord
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: ammeter
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -164,12 +150,9 @@ files:
164
150
  - lib/action_cable/subscription_adapter/anycable.rb
165
151
  - lib/anycable-rails.rb
166
152
  - lib/anycable/rails.rb
167
- - lib/anycable/rails/actioncable/channel.rb
168
- - lib/anycable/rails/actioncable/connection.rb
169
- - lib/anycable/rails/actioncable/connection/persistent_session.rb
170
- - lib/anycable/rails/actioncable/connection/serializable_identification.rb
171
- - lib/anycable/rails/actioncable/remote_connections.rb
172
- - lib/anycable/rails/actioncable/testing.rb
153
+ - lib/anycable/rails/action_cable_ext/channel.rb
154
+ - lib/anycable/rails/action_cable_ext/connection.rb
155
+ - lib/anycable/rails/action_cable_ext/remote_connections.rb
173
156
  - lib/anycable/rails/channel_state.rb
174
157
  - lib/anycable/rails/compatibility.rb
175
158
  - lib/anycable/rails/compatibility/rubocop.rb
@@ -178,12 +161,15 @@ files:
178
161
  - lib/anycable/rails/compatibility/rubocop/cops/anycable/periodical_timers.rb
179
162
  - lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb
180
163
  - lib/anycable/rails/config.rb
164
+ - lib/anycable/rails/connection.rb
165
+ - lib/anycable/rails/connection_factory.rb
166
+ - lib/anycable/rails/connections/persistent_session.rb
167
+ - lib/anycable/rails/connections/serializable_identification.rb
168
+ - lib/anycable/rails/connections/session_proxy.rb
181
169
  - lib/anycable/rails/middlewares/executor.rb
182
170
  - lib/anycable/rails/middlewares/log_tagging.rb
183
171
  - lib/anycable/rails/rack.rb
184
172
  - lib/anycable/rails/railtie.rb
185
- - lib/anycable/rails/refinements/subscriptions.rb
186
- - lib/anycable/rails/session_proxy.rb
187
173
  - lib/anycable/rails/version.rb
188
174
  - lib/generators/anycable/download/USAGE
189
175
  - lib/generators/anycable/download/download_generator.rb
@@ -203,7 +189,8 @@ metadata:
203
189
  documentation_uri: https://docs.anycable.io/#/using_with_rails
204
190
  homepage_uri: https://anycable.io/
205
191
  source_code_uri: http://github.com/anycable/anycable-rails
206
- post_install_message:
192
+ funding_uri: https://github.com/sponsors/anycable
193
+ post_install_message:
207
194
  rdoc_options: []
208
195
  require_paths:
209
196
  - lib
@@ -211,15 +198,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
211
198
  requirements:
212
199
  - - ">="
213
200
  - !ruby/object:Gem::Version
214
- version: '2.5'
201
+ version: '2.6'
215
202
  required_rubygems_version: !ruby/object:Gem::Requirement
216
203
  requirements:
217
204
  - - ">="
218
205
  - !ruby/object:Gem::Version
219
206
  version: '0'
220
207
  requirements: []
221
- rubygems_version: 3.0.6
222
- signing_key:
208
+ rubygems_version: 3.3.7
209
+ signing_key:
223
210
  specification_version: 4
224
211
  summary: Rails adapter for AnyCable
225
212
  test_files: []
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "action_cable/channel"
4
-
5
- module ActionCable
6
- module Channel
7
- class Base # :nodoc:
8
- alias_method :handle_subscribe, :subscribe_to_channel
9
-
10
- public :handle_subscribe, :subscription_rejected?
11
-
12
- # Action Cable calls this method from inside the Subscriptions#add
13
- # method, which we use to initialize the channel instance.
14
- # We don't want to invoke `subscribed` callbacks every time we do that.
15
- def subscribe_to_channel
16
- # noop
17
- end
18
-
19
- def start_periodic_timers
20
- # noop
21
- end
22
-
23
- def stop_periodic_timers
24
- # noop
25
- end
26
-
27
- def stream_from(broadcasting, _callback = nil, _options = {})
28
- connection.socket.subscribe identifier, broadcasting
29
- end
30
-
31
- def stop_stream_from(broadcasting)
32
- connection.socket.unsubscribe identifier, broadcasting
33
- end
34
-
35
- def stop_all_streams
36
- connection.socket.unsubscribe_from_all identifier
37
- end
38
- end
39
- end
40
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module Connection
5
- module PersistentSession
6
- def handle_open
7
- super.tap { commit_session! }
8
- end
9
-
10
- def handle_channel_command(*)
11
- super.tap { commit_session! }
12
- end
13
-
14
- def build_rack_request
15
- return super unless socket.session
16
-
17
- super.tap do |req|
18
- req.env[::Rack::RACK_SESSION] =
19
- AnyCable::Rails::SessionProxy.new(req.env[::Rack::RACK_SESSION], socket.session)
20
- end
21
- end
22
-
23
- def commit_session!
24
- return unless request_loaded? && request.session.respond_to?(:loaded?) && request.session.loaded?
25
-
26
- socket.session = request.session.to_json
27
- end
28
- end
29
- end
30
- end
31
-
32
- ::ActionCable::Connection::Base.prepend(
33
- ::ActionCable::Connection::PersistentSession
34
- )
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionCable
4
- module Connection
5
- module SerializableIdentification
6
- extend ActiveSupport::Concern
7
-
8
- class_methods do
9
- def identified_by(*identifiers)
10
- super
11
- Array(identifiers).each do |identifier|
12
- define_method(identifier) do
13
- instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
14
- end
15
- end
16
- end
17
- end
18
-
19
- # Generate identifiers info.
20
- # Converts GlobalID compatible vars to corresponding global IDs params.
21
- def identifiers_hash
22
- identifiers.each_with_object({}) do |id, acc|
23
- obj = instance_variable_get("@#{id}")
24
- next unless obj
25
-
26
- acc[id] = AnyCable::Rails.serialize(obj)
27
- end.compact
28
- end
29
-
30
- def identifiers_json
31
- identifiers_hash.to_json
32
- end
33
-
34
- # Fetch identifier and deserialize if neccessary
35
- def fetch_identifier(name)
36
- @cached_ids[name] ||= @cached_ids.fetch(name) do
37
- AnyCable::Rails.deserialize(ids[name.to_s])
38
- end
39
- end
40
- end
41
- end
42
- end