Skip to content

Commit

Permalink
feat: stream_from(..., whisper: true)
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Apr 1, 2024
1 parent 25a8be3 commit c710549
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## master

- Allow specifying the _whispering_ stream via `#stream_from(..., whisper: true)`. ([@palkan][])

You can use specify the stream to use for _whispering_ (client-initiated broadcasts) by adding `whisper: true` to the `#stream_from` (or `#stream_for`) method call.

NOTE: This feature is only supported when using AnyCable server and ignored otherwise.

## 1.5.0.rc.1

- Support passing objects to `ActionCable.server.broadcast`. ([@palkan][])
Expand Down
39 changes: 39 additions & 0 deletions docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,43 @@ Auto-batching uses [Rails executor](https://guides.rubyonrails.org/threading_and

This feature is only supported when using AnyCable.

## Whispering

AnyCable supports _whispering_, or client-initiated broadcasts. A typical use-case for whispering is sending typing notifications in messaging apps or sharing cursor positions. Here is an example client-side code leveraging whispers (using [AnyCable JS][anycable-client]):

```js
let channel = cable.subscribeTo("ChatChannel", {id: 42});

channel.on("message", (msg) => {
if (msg.event === "typing") {
console.log(`user ${msg.name} is typing`);
}
})

// publishing whispers
const { user } = getCurrentUser();

channel.whisper({event: "typing", name})
```

You MUST explicitly enable whispers in your channel class as follows:

```ruby
class ChatChannel < ApplicationCable::Channel
def subscribed
room = Chat::Room.find(params[:id])

stream_for room, whisper: true
end
end
```

Adding `whisper: true` to the stream subscription enables **sending** broadcasts for this client; all subscribed client receive whispers (as regular broadcasts).

**IMPORTANT:** There can be only one whisper stream per channel subscription (since from the protocol perspective clients don't know about streams).

**NOTE:** This feature requires AnyCable server and it's ignored otherwise.

## Helpers

AnyCable provides a few helpers you can use in your views:
Expand Down Expand Up @@ -102,3 +139,5 @@ module ApplicationCable
end
end
```

[anycable-client]: https://github.com/anycable/anycable-client
10 changes: 9 additions & 1 deletion lib/anycable/rails/action_cable_ext/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,20 @@ def stop_periodic_timers
super unless anycabled?
end

def stream_from(broadcasting, _callback = nil, **)
def stream_from(broadcasting, _callback = nil, **opts)
whispering = opts.delete(:whisper)
return super unless anycabled?

broadcasting = String(broadcasting)

connection.anycable_socket.subscribe identifier, broadcasting
if whispering
connection.anycable_socket.whisper identifier, broadcasting
end
end

def stream_for(model, callback = nil, **opts, &block)
stream_from(broadcasting_for(model), callback || block, **opts)
end

def stop_stream_from(broadcasting)
Expand Down
2 changes: 1 addition & 1 deletion lib/anycable/rails/compatibility.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module Compatibility # :nodoc:
]

ActionCable::Channel::Base.prepend(Module.new do
def stream_from(broadcasting, callback = nil, coder: nil)
def stream_from(broadcasting, callback = nil, coder: nil, **)
if coder.present? && coder != ActiveSupport::JSON
raise AnyCable::CompatibilityError, "Custom coders are not supported by AnyCable"
end
Expand Down
2 changes: 1 addition & 1 deletion spec/dummy/app/channels/test_channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def subscribed
if current_user.secret != "123"
reject
else
stream_from "test"
stream_from "test", whisper: true
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/integrations/rpc/subscriptions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
expect(subject.streams).to eq ["test"]
expect(subject.stop_streams).to eq false
expect(subject.transmissions.first).to include("confirm_subscription")
expect(subject.istate["$w"]).to eq "test"
end
end

Expand Down

0 comments on commit c710549

Please sign in to comment.