yrb-actioncable
An ActionCable companion for Y.js clients
Installation
Add this line to your application's Gemfile:
gem "y-rb_actioncable"
And then execute:
$ bundle
Or install it yourself as:
$ gem install y-rb_actioncable
Usage
yrb-actioncable
provides a module that can be used to extend the capabilities
of a regular channel with a real-time sync mechanism. yrb-actioncable
implements the same protocol as
y-websocket
.
It will make sure that a newly connected client will be provided with the current state and also syncs changes from the client to the server.
sequenceDiagram
Client->>Server: Subscribe to channel
Server->>Client: Successfully subscribed to channel
Server->>Client: Send server-side document state-vector
Client->>Server: Respond with update based on server-side state-vector
Client->>Server: Send client-side document state-vector
Server->>Client: Respond with update based on client-side state-vector
Client->>Server: Send incremental updates from client
Server->>Client: Send incremental updates from server (broadcast)
In order to use the above described protocol, someone can simply include the
Sync
module. There are three methods we need to use:
- Initiate the connection with initial sync steps:
initiate
- Integrate any incoming changes:
integrate
- Broadcast incoming updates to all clients:
sync_to
# app/channels/sync_channel.rb
class SyncChannel < ApplicationCable::Channel
include Y::Actioncable::Sync
def subscribed
# initiate sync & subscribe to updates, with optional persistence mechanism
sync_from("document-1")
end
def receive()
# broadcast update to all connected clients on all servers
sync_to("document-1", )
end
end
⚠️ Attention: This integration and API eventually change before the final
release. The goal for yrb-actioncable
is simplicity and ease-of-use, and the
current implementation requires at least some knowledge about internals.
Persistence
We can also implement a persistence mechanism with yrb-actioncable
via hooks.
This is a quite common use case, and therefore it is relatively simple to add.
Load document
In order to instantiate the document with some state, yrb-actioncable
expects
the load
method to be called with a block that returns a full state update for
the document. Internally it just calls Y::Doc#sync(update)
.
class SyncChannel < ApplicationCable::Channel
include Y::Actioncable::Sync
def initialize(connection, identifier, params = nil)
super
load { |id| load_doc(id) }
end
end
Persist document
It is usually desirable to persist updates as soon as they arrive. The method
persist
expects a block that can process a document full state update for the
given ID.
class SyncChannel < ApplicationCable::Channel
include Y::Actioncable::Sync
def subscribed
stream_for(session, coder: ActiveSupport::JSON) do ||
persist { |id, update| save_doc(id, update) }
end
end
end
Example implementation for load_doc
and save_doc
We eventually provide storage providers that are easy to use, e.g.
yrb-redis
, but you can also implement
your own store methods.
def load_doc(id)
data = REDIS.get(id)
data = data.unpack("C*") unless data.nil?
data
end
def save_doc(id, update)
REDIS.set(id, update.pack("C*"))
end
License
The gem is available as open source under the terms of the MIT License.