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:
- before_command: This callback is invoked before any command can be processed by the channel.
- 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.
- 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:
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.