hooks in a mixin
chikamichi opened this issue · 6 comments
Hi, taking my example as code base, I turned the Cat class into a module :
require 'hooks'
# That's the generic cat. It does pretty much nothing.
# It's not really a *cat* by the way, just a Ruby class.
# Don't forget to document this properly, cats are literate coders.
module Cat
include Hooks
@likes = 'food'
define_hook :after_dinner # void default implementation
def dinner_time!
puts "It's dinner time!"
run_hook :after_dinner, @likes
end
end
# But here's *my* REAL cat. It's dumb and hangry. It's a cat.
# MY CAT.
class MyCat
include Cat
def initialize(likes = nil)
@likes = likes ? likes : 'food'
end
after_dinner do |what|
puts "I had a great time. I'd like some more #{what}!"
end
after_dinner :want_some_more
def want_some_more(what)
puts "No, really, is there some more #{@likes}?"
end
end
# dumb cat
Cat.new.dinner_time! # => It's dinner time!
# dumber cat
my_cat = MyCat.new('fish')
my_cat.dinner_time! # => It's dinner time!
# => I had a great time. I'd like some more fish!
# => No, really, is there some more fish?
Running the script shouts:
tmp_hooks.rb:27:in `<class:MyCat>': undefined method `after_dinner' for MyCat:Class (NoMethodError)
from tmp_hooks.rb:20:in `<main>'
#define_hook
doesn't work in a module since it is calling superclass
to inherit callbacks from ancestor classes. Right now, the Hooks module is not designed to be placed in modules but classes, only. Does it make sense to define hooks in modules and then mix them into classes?
Well, that'd be quite useful IMO, mixins are everywhere. I guess we could introspect a little the ancestors chain to handle both cases. I don't have much time right now (really must handle the ruby-lang.org refacto right now!), but I'll come back to it one day :)
it does work in module. On both instance and class level, which ActiveSupport/callbacks can't do. Thanks.
require 'rubygems'
require 'active_support/concern'
require 'hooks'
module Mod1
extend ActiveSupport::Concern
included do
define_hook :after_save
after_save :log
define_hook :after_create
after_create :log
after_create :send_email
end
def log
puts 'instance log'
end
module ClassMethods
def log
puts "class log"
end
def send_email
puts "send email"
end
end
end
class Cat
include Hooks
include Mod1
def save
puts 'save'
run_hook :after_save
end
class << self
def create
puts 'create'
run_hook :after_create
end
end
end
Cat.new.save
Cat.create
This should be closed, as @dorren pointed out. You don't need ActiveSupport::Concern
either, you can use the built-in Module.included
method.
module Cat
def self.included(base)
base.send :include, Hooks
base.define_hook :after_save
base.define_hook :after_create
end
end
Ok, awesome! :D Is anyone keen to write a test for using hooks with a module? Is the module then to be included in an object or how do you guys use this?