Created
June 15, 2011 22:17
-
-
Save kpumuk/1028273 to your computer and use it in GitHub Desktop.
Contains the FiresEvent module and extensions to ActiveRecord::Base for adding event-related after_save hooks to models.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Contains the FiresEvent module and extensions to ActiveRecord::Base for | |
# adding event-related after_save hooks to models. | |
# Adds methods to ActiveRecord::Base for working with events. | |
class ActiveRecord::Base | |
class_attribute :skip_events, :instance_writer => false | |
class << self | |
# Runs a block of code without firing any events. | |
def without_events(&block) | |
self.skip_events = true | |
ret = Thread.exclusive(&block) | |
self.skip_events = false | |
ret | |
end | |
end | |
# Returns true if events are being suppressed. | |
def skip_events? | |
@skip_events || self.class.skip_events | |
end | |
# Stops firing events until resume_events! is called. | |
def skip_events! | |
@skip_events = true | |
end | |
# Resumes firing events after a call to skip_events! | |
def resume_events! | |
@skip_events = nil | |
end | |
end | |
# Contains the fires_event method. | |
module FiresEvent | |
# Gives an ActiveRecord::Base subclass the ability to create event objects (or | |
# really, any object) via any of the ActiveRecord::Base callbacks. You provide | |
# a class (or class name string) and optionally a callback (by default | |
# +after_create+). A new instance of the given class, along with the record | |
# itself, is yielded to the given block as part of the callback. | |
# | |
# In the block, you can configure the object as you see fit. It is saved after | |
# the block finishes. Example: | |
# | |
# class Post < ActiveRecord::Base | |
# fires_event Event::PostCreated do |event, post| | |
# event.post_id = post.id | |
# end | |
# end | |
# | |
# This will create an +after_create+ hook that initializes an | |
# <tt>Event::PostCreated</tt> object, configures it according to the block, | |
# and then saves it. Any validation errors will be raised as exceptions. | |
# | |
# If you want to abort the process and suppress the event, you can call | |
# <tt>cancel_event!</tt> in the block: | |
# | |
# fires_event Event::PostCreated do |event, post| | |
# cancel_event! if post.private? | |
# event.post_id = post.id | |
# end | |
def fires_event(event_class, callback=:after_create, &block) | |
send(callback) do |object| | |
next if object.skip_events? | |
event = (event_class.kind_of?(String) ? event_class.constantize : event_class).new | |
begin | |
block.call(event, object) | |
rescue CancelException | |
else | |
event.save! | |
@after_firing_event.each { |hook| hook.call(event, object) } if @after_firing_event | |
end | |
end | |
end | |
# Aborts the process of creating an event. Should only be called within a | |
# fires_event block. | |
def cancel_event! | |
raise CancelException | |
end | |
# Saves a block that will be executed after the event is saved and has a | |
# database ID. The block will be given the saved event and the host object: | |
# | |
# fires_event Event::PostCreated do |event, post| | |
# event.post_id = post.id | |
# after_firing_event { |saved_event, post| post.update_attribute_with_validation :event_id, saved_event.id } | |
# end | |
# | |
# The block | |
def after_firing_event(&block) | |
@after_firing_event ||= [] | |
@after_firing_event << block | |
end | |
# :nodoc: | |
class CancelException < StandardError; end | |
end | |
ActiveRecord::Base.extend FiresEvent |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment