Skip to content

Instantly share code, notes, and snippets.

@cluesque
Last active March 5, 2018 17:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cluesque/08da6f4d0e24eea47eb9da8b73b5841d to your computer and use it in GitHub Desktop.
Save cluesque/08da6f4d0e24eea47eb9da8b73b5841d to your computer and use it in GitHub Desktop.
Suppressible mixin
DHH's On Writing Software (well?) [episode 2](https://www.youtube.com/watch?v=m1jOWu7woKM) discussed callbacks, specifically the practice of hiding side effects of object creation using `after_save` and `after_commit` hooks.
It implicitly conceeded that `after_commit`, lacking information about what was just committed, was flawed, and uses the common workaround of using an `after_save` to decide to do something based on changes and set an @ivar, and then in the `after_commit` acting on the @ivar found. Which is fun, but.
It also [at the end](https://youtu.be/m1jOWu7woKM?t=1151) discussed the possibility that in some use cases a caller might want to explicitly prevent side effects from happening. Think about when audited temporarily suppresses auditing, or perhaps one might want to bulk update some data, without sending the email normally sent when said data is updated.
It teased the presence of a `Suppressible` mixin. For fun, I wrote it.
Update: turns out the real one is public. It uses `thread_mattr_accessor` and `Module#extended` and is therefore much better.
https://gist.github.com/thechrisoshow/2c10bf4530528588453b4f8a6d8763fb
> Narf.new.do_stuff
Doing Stuff!
=> nil
> Narf.suppress{ Narf.new.do_stuff }
=> nil
class Narf
include Suppressible
def do_stuff
puts "Doing Stuff!" unless suppressed?
end
end
module Suppressible
extend ActiveSupport::Concern
# Supports the notion that some activities on a class
# might have some side effects and that those side effects
# might want to be temporarily suppressed by some calling code
# Useful, e.g., where an `after_save` callback sends email,
# but some bulk update wants to suppress said email
# class Widget < ActiveRecord::Base
# after_commit :send_creation_notice
#
# def send_creation_notice
# WidgetAnnouncer.announce self
# end
# end
#
# class WidgetAnnouncer
# include Suppressible
# def announce(widget)
# send_widget_email(widget) unless suppressed?
# end
# end
# Now, when you want to make a bunch of widgets and later send a bulk announcement:
# WidgetAnnouncer.suppress do
# 100.times do
# Widget.create
# end
# end
# send_bulk_widget_announcement
def suppress &block
self.class.suppress block
end
def suppressed?
self.class.suppressed?
end
module ClassMethods
def suppress &block
previous = suppressed?
begin
self.suppressed = true
yield
ensure
self.suppressed = previous
end
end
def suppressed=(value)
Thread.current["#{name.tableize}_suppressed"] = value
end
def suppressed?
!!Thread.current["#{name.tableize}_suppressed"]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment