cable

WebSockets

MVC::Keayl::Cable is an ActionCable-style abstraction for real-time messaging: a connection holds a client, channels group its subscriptions, and a pub/sub backend fans broadcasts out to everyone on a stream.

Connections

MVC::Keayl::Cable::Connection represents one client. It carries the pub/sub backend, a sink (the transport's function for sending a message to that client), and identifiers (whatever you use to identify the client, such as the current user or room):

my $conn = MVC::Keayl::Cable::Connection.new(
  pubsub      => $pubsub,
  sink        => -> $message { $websocket.send($message) },
  identifiers => %( user-id => 7 ),
);

add-subscription($channel) registers a channel and subscribes it; disconnect tears every subscription down.

Identification and authentication

Declare the identifiers a connection carries with identified-by, and authenticate in connect. set-identifier records a verified identity; reject-unauthorized-connection refuses the connection. open runs connect, catching a rejection, after which is-rejected reports the outcome. A declared identifier reads back as a method:

class AppConnection is MVC::Keayl::Cable::Connection {
  method connect {
    my $user = find-verified-user(self.identifiers<token>);
    $user ?? self.set-identifier('current-user', $user) !! self.reject-unauthorized-connection;
  }
}
AppConnection.identified-by('current-user');

$conn.open;
$conn.current-user;   # the verified identifier

Channels

Subclass MVC::Keayl::Cable::Channel. Override subscribed to start streaming, unsubscribed to clean up, and add a method per action a client can invoke:

class ChatChannel is MVC::Keayl::Cable::Channel {
  method subscribed {
    self.stream-from('room:' ~ self.connection.identifiers<room>);
  }

  method speak(%data) {
    self.broadcast-to('room:' ~ self.connection.identifiers<room>, %data<message>);
  }
}
  • stream-from($stream) subscribes the channel to a broadcasting; messages on that stream are transmitted to this connection's client.

  • transmit($data) sends data straight to this client.

  • broadcast-to($stream, $data) publishes to a stream, reaching every connection subscribed to it.

  • perform($action, %data) dispatches a client message to an action method. Only methods you define on the channel are callable, so framework methods like stream-from cannot be invoked from the wire.

unsubscribe (run for you on disconnect) drops the channel's stream subscriptions and calls unsubscribed.

Rejecting a subscription

subscribed may call reject to refuse the subscription. A rejected channel is not added to the connection, and any streams it set up are torn down:

method subscribed {
  self.reject unless self.connection.current-user.can-access(self.params<room>);
}

Streaming for a model and coders

stream-for($target) derives the stream name from a model (its broadcasting-for, namespaced by the channel) instead of a raw string. A coder encodes broadcasts and decodes received messages; JsonCoder serializes to and from JSON:

method subscribed {
  self.stream-for($room, coder => JsonCoder.new);
}

broadcast-to-target($target, $data) publishes to a model's broadcasting through the server pub/sub (set with set-cable-pubsub).

Periodic timers

periodically registers a recurring callback. run-periodic-timers fires the registered timers (the transport drives this on the configured interval):

class PresenceChannel is MVC::Keayl::Cable::Channel {
  method ping { self.transmit('ping') }
}
PresenceChannel.periodically('ping', every => 3);

Broadcasting from models

A model that does MVC::Keayl::Cable::Broadcasting::Broadcastable broadcasts updates to a stream through the configured server pub/sub. broadcast-append-to, broadcast-replace-to, and broadcast-remove-to send a payload naming the action, target, and content; broadcast-to sends an arbitrary payload:

class Post does Broadcastable { has $.id }

$post.broadcast-append-to($post, target => 'posts', content => $rendered-html);

The broadcast reaches the channels streaming that broadcasting, which transmit it to their clients.

Pub/sub backends

A backend is any MVC::Keayl::Cable::PubSub, a role with subscribe($stream, &callback), unsubscribe($id), and broadcast($stream, $message). MVC::Keayl::Cable::PubSub::InMemory is the built-in, single-process backend; it tracks subscribers per stream and fans a broadcast out to each one.

MVC::Keayl::Cable::PubSub::External shares broadcasts across processes by delegating to a networked client (Redis- or PostgreSQL-shaped). The client provides subscribe($stream, &callback), unsubscribe($id), publish($stream, $message), and subscriber-count($stream):

my $pubsub = MVC::Keayl::Cable::PubSub::External.new(client => $redis-client);

MVC::Keayl v0.9.1

Model-View-Controller web framework

Authors

  • Greg Donald

License

Artistic-2.0

Dependencies

Cro::HTTPCrypt::RandomDigest::HMACDigest::SHA1::NativeOpenSSLMIME::Base64YAMLishORM::ActiveRecord:auth<zef:gdonald>Template::HAML:auth<zef:gdonald>

Provides

  • MVC::Keayl
  • MVC::Keayl::APIController
  • MVC::Keayl::ActionText
  • MVC::Keayl::ActionText::Repository
  • MVC::Keayl::ActionText::RichTextable
  • MVC::Keayl::Adapter
  • MVC::Keayl::Adapter::Cro
  • MVC::Keayl::Adapter::Test
  • MVC::Keayl::Application
  • MVC::Keayl::Assets
  • MVC::Keayl::Assets::Serving
  • MVC::Keayl::CLI
  • MVC::Keayl::CSRF
  • MVC::Keayl::Cable::Broadcasting
  • MVC::Keayl::Cable::Channel
  • MVC::Keayl::Cable::Connection
  • MVC::Keayl::Cable::PubSub
  • MVC::Keayl::Cable::PubSub::External
  • MVC::Keayl::Cable::PubSub::InMemory
  • MVC::Keayl::Cache
  • MVC::Keayl::Caching
  • MVC::Keayl::Config
  • MVC::Keayl::Controller
  • MVC::Keayl::Cookies
  • MVC::Keayl::Credentials
  • MVC::Keayl::Dispatcher
  • MVC::Keayl::Endpoint
  • MVC::Keayl::Engine
  • MVC::Keayl::ErrorReporter
  • MVC::Keayl::ErrorReporting
  • MVC::Keayl::Errors
  • MVC::Keayl::ExceptionPage
  • MVC::Keayl::Flash
  • MVC::Keayl::HealthController
  • MVC::Keayl::Helpers::Asset
  • MVC::Keayl::Helpers::DateTime
  • MVC::Keayl::Helpers::Form
  • MVC::Keayl::Helpers::Number
  • MVC::Keayl::Helpers::Options
  • MVC::Keayl::Helpers::Tag
  • MVC::Keayl::Helpers::Text
  • MVC::Keayl::Helpers::Url
  • MVC::Keayl::HttpAuthentication
  • MVC::Keayl::I18n
  • MVC::Keayl::I18n::Locale
  • MVC::Keayl::I18n::Pluralization
  • MVC::Keayl::Job
  • MVC::Keayl::Job::GlobalID
  • MVC::Keayl::Job::QueueAdapter
  • MVC::Keayl::Job::QueueAdapter::Async
  • MVC::Keayl::Job::QueueAdapter::Database
  • MVC::Keayl::Job::QueueAdapter::Inline
  • MVC::Keayl::Job::QueueAdapter::Test
  • MVC::Keayl::Live
  • MVC::Keayl::LogEvent
  • MVC::Keayl::Logger
  • MVC::Keayl::Mail
  • MVC::Keayl::Mailbox
  • MVC::Keayl::Mailbox::Ingress
  • MVC::Keayl::Mailbox::Router
  • MVC::Keayl::Mailer
  • MVC::Keayl::Mailer::Delivery
  • MVC::Keayl::Mailer::Delivery::File
  • MVC::Keayl::Mailer::Delivery::SMTP
  • MVC::Keayl::Mailer::Delivery::Test
  • MVC::Keayl::Mailer::DeliveryJob
  • MVC::Keayl::Mailer::Preview
  • MVC::Keayl::Middleware
  • MVC::Keayl::Middleware::Database
  • MVC::Keayl::Middleware::HostAuthorization
  • MVC::Keayl::Middleware::Logger
  • MVC::Keayl::Middleware::RequestId
  • MVC::Keayl::Middleware::SSL
  • MVC::Keayl::Middleware::SecureHeaders
  • MVC::Keayl::Middleware::Static
  • MVC::Keayl::MiddlewareStack
  • MVC::Keayl::Mime
  • MVC::Keayl::Notifications
  • MVC::Keayl::PWAController
  • MVC::Keayl::ParameterFilter
  • MVC::Keayl::Parameters
  • MVC::Keayl::Params
  • MVC::Keayl::Request
  • MVC::Keayl::Response
  • MVC::Keayl::Router
  • MVC::Keayl::Routing
  • MVC::Keayl::Routing::Mount
  • MVC::Keayl::Routing::PathPattern
  • MVC::Keayl::Routing::Redirect
  • MVC::Keayl::Routing::Resources
  • MVC::Keayl::Routing::Route
  • MVC::Keayl::Routing::RouteMatch
  • MVC::Keayl::Routing::UrlHelpers
  • MVC::Keayl::SafeString
  • MVC::Keayl::Secrets
  • MVC::Keayl::Session
  • MVC::Keayl::Storage
  • MVC::Keayl::Storage::Attached
  • MVC::Keayl::Storage::Repository
  • MVC::Keayl::Storage::Service
  • MVC::Keayl::Storage::Serving
  • MVC::Keayl::Storage::Variant
  • MVC::Keayl::TestSupport
  • MVC::Keayl::View
  • MVC::Keayl::View::Context
  • MVC::Keayl::View::Handler
  • MVC::Keayl::View::Handler::HAML

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.