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

Ghouse Mohamed

Ghouse Mohamed

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 behaviour better:

class Connection < ActionCable::Connection::Base
  identified_by :current_user
  before_command :set_current_user
  around_command :register_telemetry_data
  after_command :update_current_user

  private

    def set_current_user
      if request.params["user_id"].present?
        self.current_user = User.find_by(request.params["user_id"])
      end
      reject_unauthorized_connection if self.current_user.nil?
    end

    def register_telemetry_data
      self.current_user.register_telemetry({ start: true })
      yield
      self.current_user.register_telemetry({ end: true })
    end

    def update_current_user
      self.current_user.touch(:updated_at)
    end
end

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.

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.