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-actioncableWhat it checks
Section titled “What it checks”class ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_room_5" endend
ChatChannel.broadcast_to(room, message: "hi") # info: channel existsActionCable.server.broadcast("chat_room_5", body: "hi") # info: registered streamChartChannel.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| Rule | Severity | Fires when |
|---|---|---|
plugin.actioncable.broadcast-target | info | <X>Channel.broadcast_to(...) matched a discovered channel |
plugin.actioncable.broadcast-stream | info | ActionCable.server.broadcast("...", ...) matched a registered stream_from literal |
plugin.actioncable.unknown-channel | error | the receiver ends in Channel but is not in the index (with a did-you-mean) |
plugin.actioncable.unknown-stream | warning | the literal stream name matched no stream_from registration (with a did-you-mean) |
plugin.actioncable.load-error | warning | channel 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.
Configuration
Section titled “Configuration”plugins: - gem: rigor-actioncable config: channel_search_paths: ["app/channels"] # default channel_base_classes: ["ApplicationCable::Channel", "ActionCable::Channel::Base"] # defaultLimitations
Section titled “Limitations”- Direct-superclass match only. An indirect chain (
AdminChannel < BaseChannel < ApplicationCable::Channel) needsBaseChannellisted inchannel_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_toarity 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 explicitActionCable.serverreceiver is skipped to avoid false positives on unrelated methods.
Plugin internals
Section titled “Plugin internals”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.