require "sup/util" module Redwood class HookManager class HookContext def initialize name @__say_id = nil @__name = name @__cache = {} end def say s if BufferManager.instantiated? @__say_id = BufferManager.say s, @__say_id BufferManager.draw_screen else log s end end def flash s if BufferManager.instantiated? BufferManager.flash s else log s end end def log s info "hook[#@__name]: #{s}" end def ask_yes_or_no q if BufferManager.instantiated? BufferManager.ask_yes_or_no q else print q gets.chomp.downcase == 'y' end end def get tag HookManager.tags[tag] end def set tag, value HookManager.tags[tag] = value end def __run __hook, __filename, __locals __binding = binding __lprocs, __lvars = __locals.partition { |k, v| v.is_a?(Proc) } eval __lvars.map { |k, v| "#{k} = __locals[#{k.inspect}];" }.join, __binding ## we also support closures for delays evaluation. unfortunately ## we have to do this via method calls, so you don't get all the ## semantics of a regular variable. not ideal. __lprocs.each do |k, v| self.class.instance_eval do define_method k do @__cache[k] ||= v.call end end end ret = eval __hook, __binding, __filename BufferManager.clear @__say_id if @__say_id @__cache = {} ret end end include Redwood::Singleton @descs = {} class << self attr_reader :descs end def initialize dir @dir = dir @hooks = {} @contexts = {} @tags = {} Dir.mkdir dir unless File.exist? dir end attr_reader :tags def run name, locals={} hook = hook_for(name) or return context = @contexts[hook] ||= HookContext.new(name) result = nil fn = fn_for name begin result = context.__run hook, fn, locals rescue Exception => e log "error running #{fn}: #{e.message}" log e.backtrace.join("\n") @hooks[name] = nil # disable it BufferManager.flash "Error running hook: #{e.message}" if BufferManager.instantiated? end result end def self.register name, desc @descs[name] = desc end def print_hooks pattern="", f=$stdout matching_hooks = HookManager.descs.sort.keep_if {|name, desc| pattern.empty? or name.match(pattern)}.map do |name, desc| <