Skip to content

rigor-actioncable

Validates ActionCable broadcast call sites against a statically discovered channel index: <X>Channel.broadcast_to(record, data) checks that the channel class exists, and ActionCable.server.broadcast("stream_name", data) checks that the literal stream name was registered with stream_from in some channel. It reads source only — no actioncable runtime dependency.

It ships bundled in rigortype. Activate it under plugins::

plugins:
- rigor-actioncable
app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_room_5"
end
end
ChatChannel.broadcast_to(room, message: "hi") # info: channel exists
ActionCable.server.broadcast("chat_room_5", body: "hi") # info: registered stream
ChartChannel.broadcast_to(room, message: "hi") # error: no channel (did you mean ChatChannel?)
ActionCable.server.broadcast("chat_room_42", body: "hi") # warning: no such stream registration
RuleSeverityFires when
plugin.actioncable.broadcast-targetinfo<X>Channel.broadcast_to(...) matched a discovered channel
plugin.actioncable.broadcast-streaminfoActionCable.server.broadcast("...", ...) matched a registered stream_from literal
plugin.actioncable.unknown-channelerrorthe receiver ends in Channel but is not in the index (with a did-you-mean)
plugin.actioncable.unknown-streamwarningthe literal stream name matched no stream_from registration (with a did-you-mean)
plugin.actioncable.load-errorwarningchannel discovery failed (parse/read error) — once per file

The unknown-stream check is suppressed when any discovered channel registers a dynamic stream (stream_from interpolated_string or stream_for record) — the absence of a literal match doesn’t prove the stream is invalid. Non-Channel receivers and non-literal stream arguments pass through silently.

plugins:
- gem: rigor-actioncable
config:
channel_search_paths: ["app/channels"] # default
channel_base_classes: ["ApplicationCable::Channel", "ActionCable::Channel::Base"] # default
  • Direct-superclass match only. An indirect chain (AdminChannel < BaseChannel < ApplicationCable::Channel) needs BaseChannel listed in channel_base_classes.
  • Action methods are indexed, not validated. Channel actions are invoked client-side via subscription.perform("action", data); Rigor does not analyse JavaScript, so the action index is informational.
  • broadcast_to arity is not checked — it accepts any record + any data hash.
  • Indirect stream_from (registered inside a helper method rather than directly in the channel body) is out of scope.
  • Bare broadcast(...) without an explicit ActionCable.server receiver is skipped to avoid false positives on unrelated methods.

The channel discoverer / index and the contract surfaces this plugin exercises are in the plugin’s README. To write a plugin, see examples/ and the rigor-plugin-author skill.

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.