Rails 7.1 adds callbacks for Action Cable commands at the connection level

Ghouse Mohamed

By Ghouse Mohamed

on August 2, 2022

This blog is part of our  Rails 7 series.

Action Cable allows us to add callbacks for individual channels. These are: after_subscribe, after_unsubscribe, before_subscribe, before_unsubscribe, on_subscribe, on_unsubscribe. These callbacks are registered individually for each Channel. Before Rails 7.1, there was no way in which we could register callbacks to be called before every command generically. Rails 7.1 solves this problem by providing a set of callbacks which can be registered at the connection level. And these callbacks are called for every command regardless of which channel's command is being invoked.

These are the following callbacks which can be registered:

  1. before_command: This callback is invoked before any command can be processed by the channel.
  2. around_command: This callback is generally invoked around the command. Any piece of code before yield is run before the actual command and any piece of code after yield is run after the command has been processed by the channel.
  3. after_command: This callback is invoked after the command has been processed by the channel.

Let's take a look at some example code to understand this behaivour better:

1class Connection < ActionCable::Connection::Base
2  identified_by :current_user
3  before_command :set_current_user
4  around_command :register_telemetry_data
5  after_command :update_current_user
6
7  private
8
9    def set_current_user
10      if request.params["user_id"].present?
11        self.current_user = User.find_by(request.params["user_id"])
12      end
13      reject_unauthorized_connection if self.current_user.nil?
14    end
15
16    def register_telemetry_data
17      self.current_user.register_telemetry({ start: true })
18      yield
19      self.current_user.register_telemetry({ end: true })
20    end
21
22    def update_current_user
23      self.current_user.touch(:updated_at)
24    end
25end

Here, we can expect set_current_user to be invoked before every command is processed. Similar to this, we can also expect update_current_user to be invoked after every command is processed. Where as for the register_telemetry_data, self.current_user.register_telemetry({ start: true }) is run before the command is processed and then after the command is processed, self.current_user.register_telemetry({ end: true }) is run.

Please check out this pull request for more details.