前回の記事では各モジュールのself includedの中で明示的にチェックを走らせる必要がありました。これがなかなか面倒だなという事だったので、もうすこし省略された記法で書けるようにしょうとおもっていました。
そこで、探していたところ、メソッド実行を監視できるSentinelというGemを発見し
https://github.com/lucashungaro/sentinel
早速利用してみました。
require 'sentinel' class UnionRule @@UnionRule = [] def initialize(modules, action_proc) @modules = modules @proc = action_proc end def match?(modules) res = true @modules.each do |mod| if !modules.include?(mod) res = false break end end return res end def run(klass) @proc.call(klass) end def self.define_union_rule(modules, action_proc) inst = self.new(modules, action_proc) @@UnionRule << inst end def self.run_match_rules(klass) modules = klass.included_modules @@UnionRule.each do |rule| if rule.match?(modules) rule.run(klass) end end end end class ModuleObserver include Sentinel def self.notify_class_method(options, *args) UnionRule.run_match_rules(args[0]) end end module UnionableModule def self.included(base) ModuleObserver.observe(base, :included, :method_to_notify => :notify_class_method, :class_method => true) end end module Feature include UnionableModule end module AnotherFeature include UnionableModule end UnionRule.define_union_rule([Feature, AnotherFeature], lambda{|klass| puts "#{klass}" klass.class_eval do def foo() puts "#{self.class}->foo" return 10 end end }) class Moge include Feature include AnotherFeature end moge = Moge.new() puts moge.foo()
これでUnionableModuleをincludeするだけで、自動的に合成演算を定義可能な(正確には、include時に監視対象になる)Moduleにする事ができます。
実行結果は前回と同様に
Moge Moge->foo 10
とメソッドの追加が適切にできている事がわかりますね。