]> git.cworth.org Git - sup/blobdiff - lib/sup/hook.rb
bugfix: clear cached lambda hook locals after hook call
[sup] / lib / sup / hook.rb
index 47be1166df803cfabadc813bd09a0b63b1c02044..8a51cfebc14396d4dcd3c0f9cd1af1669479911f 100644 (file)
@@ -1,29 +1,20 @@
 module Redwood
 
 class HookManager
-  ## there's probably a better way to do this, but to evaluate a hook
-  ## with a bunch of pre-set "local variables" i define a function
-  ## per variable and then instance_evaluate the code.
-  ##
-  ## how does rails do it, when you pass :locals into a partial?
-  ##
-  ## i don't bother providing setters, since i'm pretty sure the
-  ## charade will fall apart pretty quickly with respect to scoping.
-  ## this is basically fail-fast.
   class HookContext
     def initialize name
+      @__say_id = nil
       @__name = name
-      @__locals = {}
-    end
-
-    attr_writer :__locals
-
-    def method_missing m, *a
-      @__locals[m] || super
+      @__cache = {}
     end
 
     def say s
-      @__say_id = BufferManager.say s, @__say_id
+      if BufferManager.instantiated?
+        @__say_id = BufferManager.say s, @__say_id
+        BufferManager.draw_screen
+      else
+        log s
+      end
     end
 
     def log s
@@ -31,15 +22,40 @@ class HookManager
     end
 
     def ask_yes_or_no q
-      BufferManager.ask_yes_or_no q
+      if BufferManager.instantiated?
+        BufferManager.ask_yes_or_no q
+      else
+        print q
+        gets.chomp.downcase == 'y'
+      end
     end
 
-    def __binding 
-      binding
+    def get tag
+      HookManager.tags[tag]
     end
 
-    def __cleanup
+    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
 
@@ -50,29 +66,29 @@ class HookManager
     @hooks = {}
     @descs = {}
     @contexts = {}
-    
+    @tags = {}
+
     Dir.mkdir dir unless File.exists? dir
 
     self.class.i_am_the_instance self
   end
 
+  attr_reader :tags
+
   def run name, locals={}
     hook = hook_for(name) or return
     context = @contexts[hook] ||= HookContext.new(name)
-    context.__locals = locals
-    
+
+    result = nil
     begin
-      result = context.instance_eval @hooks[name], fn_for(name)
-      if result.is_a? String
-        log "got return value: #{result.inspect}"
-        BufferManager.flash result 
-      end
+      result = context.__run hook, fn_for(name), locals
     rescue Exception => e
       log "error running hook: #{e.message}"
       log e.backtrace.join("\n")
-      BufferManager.flash "Error running hook: #{e.message}"
+      @hooks[name] = nil # disable it
+      BufferManager.flash "Error running hook: #{e.message}" if BufferManager.instantiated?
     end
-    context.__cleanup
+    result
   end
 
   def register name, desc
@@ -95,18 +111,20 @@ EOS
     end
   end
 
+  def enabled? name; !hook_for(name).nil? end
+
 private
 
   def hook_for name
     unless @hooks.member? name
-      @hooks[name] =
-        begin
-          returning IO.readlines(fn_for(name)).join do
-            log "read '#{name}' from #{fn_for(name)}"
-          end
-        rescue SystemCallError => e
-          nil
+      @hooks[name] = begin
+        returning IO.read(fn_for(name)) do
+          log "read '#{name}' from #{fn_for(name)}"
         end
+      rescue SystemCallError => e
+        #log "disabled hook for '#{name}': #{e.message}"
+        nil
+      end
     end
 
     @hooks[name]