Using AnyCable without Rails
AnyCable can be used without Rails, thus allowing you to use ActionCable-like functionality in your app.
Learn how to use AnyCable with Hanami in the "AnyCable off Rails: connecting Twilio streams with Hanami" blog post.
Requirements
- Ruby >= 2.7
- Redis or NATS (see broadcast adapters)
Installation
Add anycable gem to your Gemfile:
gem "anycable", "~> 1.1"
# when using Redis broadcast adapter
gem "redis", ">= 4.0"
# when using NATS broadcast adapter
gem "nats-pure", "~> 2"(and don't forget to run bundle install).
Now you need to add channels layer to your application (anycable gem only provides a CLI and a gRPC server).
You can use an existing solution (e.g., litecable) or build your own.
Lite Cable
There is a ready-to-go framework – Lite Cable – which can be used for application logic. It also supports AnyCable out-of-the-box.
Resources:
Custom Ruby framework
You can build your own framework to use as logic-handler for AnyCable.
AnyCable initiates a connection object for every request using user-provided factory:
# Specify factory
AnyCable.connection_factory = MyConnectionFactory
# And then AnyCable calls .call method on your factory
connection = factory.call(socket, **options)Where:
socket– is an object, representing client's socket (say, socket stub) (see socket.rb)optionsmay contain:identifiers: a JSON string returned byconnection.identifiers_jsonon connection (see below)subscriptions: a list of channels identifiers for the connection.
Connection interface:
class Connection
# Called on connection
def handle_open
end
# Called on disconnection
def handle_close
end
# Called on incoming message.
# Client send a JSON-encoded message of the form { "identifier": ..., "command": ..., "data" ... }.
# - identifier – channel identifier (e.g. `{"channel":"chat","id":1}`)
# - command – e.g. "subscribe", "unsubscribe", "message"
# - any additional data
def handle_channel_command(identifier, command, data)
# ...
end
# Returns any string which can be used later in .create function to initiate connection.
def identifiers_json
end
endConnection#handle_channel_command should return truthy value on success (i.e., when a subscription is confirmed, or action is called).
NOTE: connection instance is initiated on every request, so it should be stateless (except identifiers_json).
To send a message to a client, you should call socket#transmit.
For manipulating with streams use socket#subscribe, socket#unsubscribe and socket#unsubscribe_from_all.
To persist client states between RPC calls you can use socket#cstate (connection state) and socket#istate (per-channel state), which are key-value stores (keys and values must both be strings).
See test factory for example.