Created
August 15, 2010 04:21
-
-
Save lancejpollard/525066 to your computer and use it in GitHub Desktop.
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
require 'rubygems' | |
require 'active_model' | |
# use this if you want to create before and after filters around a commonly used | |
# method, but still want to define the method in subclasses. | |
module SubclassableCallbacks | |
def self.included(base) | |
base.extend ClassMethods | |
end | |
module ClassMethods | |
attr_accessor :subclassable_callbacks | |
def subclassable_callbacks(*methods) | |
@subclassable_callbacks ||= [] | |
unless methods.empty? | |
@subclassable_callbacks = @subclassable_callbacks.concat(methods).flatten.compact.map(&:to_sym).uniq | |
define_model_callbacks *methods | |
class_eval do | |
methods.each do |variable| | |
define_method "implementation_#{variable.to_s}" do; end | |
define_method variable do | |
send("_run_#{variable.to_s}_callbacks") do | |
send("implementation_#{variable.to_s}") | |
end | |
end | |
alias_method "superclass_#{variable.to_s}", variable | |
end | |
end | |
end | |
@subclassable_callbacks | |
end | |
end | |
def self.override | |
ObjectSpace.each_object(Class) do |object| | |
if object.ancestors.include?(SubclassableCallbacks) | |
object.class_eval do | |
methods = object.subclassable_callbacks | |
last_index = object.superclass.ancestors.index(SubclassableCallbacks) | |
if last_index && last_index > 0 | |
superclass_methods = object.superclass.ancestors[0..last_index - 1].map(&:subclassable_callbacks) | |
methods = methods.concat(superclass_methods).flatten.uniq | |
end | |
methods.each do |name| | |
alias_method "subclass_#{name}", "#{name}" | |
alias_method "implementation_#{name}", "subclass_#{name}" | |
alias_method "#{name}", "superclass_#{name}" | |
end | |
end | |
end | |
end | |
end | |
end | |
class BaseModel | |
extend ActiveModel::Callbacks | |
include SubclassableCallbacks | |
subclassable_callbacks :save | |
before_save { puts "[save:before]"} | |
after_save { puts "[save:after]"} | |
end | |
class SomeModel < BaseModel | |
def save | |
puts "[saving some_model...]" | |
true | |
end | |
end | |
class AnotherModel < BaseModel | |
def save | |
puts "[saving another_model...]" | |
true | |
end | |
end | |
SubclassableCallbacks.override | |
SomeModel.new.save | |
#=> [save:before] | |
#=> [saving some_model...] | |
#=> [save:after] | |
AnotherModel.new.save | |
#=> [save:before] | |
#=> [saving another_model...] | |
#=> [save:after] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment