Skip to content

Instantly share code, notes, and snippets.

@betamatt
Created September 16, 2014 19:18
Show Gist options
  • Save betamatt/27b9801fb7a2af00d3fc to your computer and use it in GitHub Desktop.
Save betamatt/27b9801fb7a2af00d3fc to your computer and use it in GitHub Desktop.
Deferred ActiveRecord touches
# When using this, be careful about your testing transactions.
# ActiveRecord after_commit hooks don't play nice with transactional fixtures
# consider adding the test_after_commit gem to your test group
ActiveRecord::Base.class_eval do
DEFERRED_KEY = "deferred"
def touch_with_defer(name = nil)
raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
defer_until_commit([self.class.name, id, name, "touch"].map(&:to_s).join(":")) { touch_without_defer(name) }
end
alias_method_chain :touch, :defer
private
def defer_until_commit(key, &block)
logger.debug "block deferred until commit with key #{key}"
if set = Thread.current[DEFERRED_KEY] ||= {}
logger.debug "skipping a duplicate deferral of key #{key}" if set[key].present?
set[key] ||= block
end
end
after_commit do
logger.debug "Flushing deferred queue"
while (set = Thread.current[DEFERRED_KEY]).present?
# Remove keys from the hash to be safely re-entrant
set.keys.each do |k|
logger.debug "undefer block with key #{k}"
set.delete(k).call
end
end
end
after_rollback do
Thread.current[DEFERRED_KEY] = nil
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment