This app's one real channel isn't custom
app/channels/ doesn't exist in this app. It isn't empty; find app/channels finds nothing at all. Yet the roadmap page refreshes live when an admin reorders a course, over a WebSocket, through an ActionCable channel. This lesson is about the channel that does it, and the app didn't write it.
turbo_stream_from, traced to a class
app/views/catalog/courses/index.html.erb has one line for this: <%= turbo_stream_from "catalog_roadmap" %>. The helper renders a subscription tag pointed at Turbo::StreamsChannel, a class that ships inside the turbo-rails gem:
class Turbo::StreamsChannel < ActionCable::Channel::Base
extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
include Turbo::Streams::StreamName::ClassMethods
def subscribed
if stream_name = verified_stream_name_from_params
stream_from stream_name
else
reject
end
end
end
That's four lines of logic, and each is a primitive this course covers: subscribed (lesson 5), verified_stream_name_from_params (lesson 3), stream_from (lesson 4) and reject (lesson 5). The app relies on all four every time that page loads, without a channel of its own.
The gem's doc comment is this course's syllabus
The same file's header comment shows how to build a custom channel instead:
# class CustomChannel < ActionCable::Channel::Base
# extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
# include Turbo::Streams::StreamName::ClassMethods
#
# def subscribed
# if (stream_name = verified_stream_name_from_params).present? &&
# subscription_allowed?
# stream_from stream_name
# else
# reject
# end
# end
#
# def subscription_allowed?
# # ...
# end
# end
ActionCable::Channel::Base is the parent class of every channel that runs in this app, which today means the one from the gem. Learning it means learning what Turbo::StreamsChannel already relies on, one layer below where the app's own code touches it.
What "no app/channels" means
Rails doesn't require an app to define any channels. The cable server is mounted regardless, and any channel class Rails can load, gem-provided or not, can be subscribed to. This app uses one: Turbo::StreamsChannel, loaded from turbo-rails when a subscription names it. Nothing in config/cable.yml or config/routes.rb names a channel. The wiring is: the helper renders a tag, the JavaScript subscribes, the channel class is resolved by name, and subscribed runs. The next six lessons walk that path with channels the app doesn't have yet, running each step instead of trusting a tutorial.
Key takeaways
app/channels/doesn't exist in this app, so it has no custom channel.- The one channel the app runs,
Turbo::StreamsChannel, ships in theturbo-railsgem and subclassesActionCable::Channel::Base, the same base class a custom channel would use. - Its four-line
subscribedmethod already uses the primitives this course covers:verified_stream_name_from_params,stream_fromandreject. The gem's doc comment shows the custom-channel shape the app hasn't needed. - Nothing in the app's configuration names a channel; a channel is resolved by name when a subscription asks for it, like any other Ruby class.
Keep going with lesson 2
Connections have no identity here
What identified_by does, and why this app's empty identifiers set gives every connection the same identifier.
A free account also gives you the quizzes, your progress and notes, and the discussion.