hotwired / turbo-rails

Use Turbo in your Ruby on Rails app
https://turbo.hotwired.dev
MIT License
2.03k stars 305 forks source link

Refresh broadcasts generated without changes #610

Open aprescott opened 3 months ago

aprescott commented 3 months ago

Suppose User broadcasts refreshes:

class User < ApplicationRecord
  broadcasts_refreshes
end

Currently, a broadcast is generated by User.last.save!, even though there are no changes to the model or the database. With many save! calls across models that aren't actually changed, this can result in lots of seemingly wasteful jobs.

There seems to be clear solution to this that doesn't involve reimplementing broadcasts_refreshes and setting up the same *_commit callbacks manually:

For example, there's no callback mechanism (like a sort of around_commit) to wrap the code in suppressing_turbo_broadcasts {}. Moreover, User.last.touch should fire a change, so inspecting, e.g., has_changes_to_save? isn't quite correct. There's a gem, ar_transaction_changes, which helps with this.

It'd be great to have a simple way to do this!

A sort-of workaround using the ar_transaction_changes gem, but it feels a little hacky (also note it relies on Rails 7.1's callback ordering behavior):

def self.broadcasts_refreshes_when_changed
  after_update_commit :suppress_broadcasts_if_no_changes
  broadcasts_refreshes
  after_update_commit :restore_broadcasts
end

def suppress_broadcasts_if_no_changes
  @__original_turbo_broadcasts_suppressed = self.class.suppressed_turbo_broadcasts

  self.class.suppressed_turbo_broadcasts = true if transaction_changed_attributes.blank?
end

def restore_broadcasts
  self.class.suppressed_turbo_broadcasts = @__original_turbo_broadcasts_suppressed
end