]> git.cworth.org Git - sup/blob - lib/sup/hook.rb
when a hook throws an exception, disable it rather than re-calling it over and over
[sup] / lib / sup / hook.rb
1 module Redwood
2
3 class HookManager
4   ## there's probably a better way to do this, but to evaluate a hook
5   ## with a bunch of pre-set "local variables" i define a function
6   ## per variable and then instance_evaluate the code.
7   ##
8   ## how does rails do it, when you pass :locals into a partial?
9   ##
10   ## i don't bother providing setters, since i'm pretty sure the
11   ## charade will fall apart pretty quickly with respect to scoping.
12   ## this is basically fail-fast.
13   class HookContext
14     def initialize name
15       @__say_id = nil
16       @__name = name
17       @__locals = {}
18     end
19
20     attr_writer :__locals
21
22     def method_missing m, *a
23       case @__locals[m]
24       when Proc
25         @__locals[m].call(*a)
26       when nil
27         super
28       else
29         @__locals[m]
30       end
31     end
32
33     def say s
34       if BufferManager.instantiated?
35         @__say_id = BufferManager.say s, @__say_id
36         BufferManager.draw_screen
37       else
38         log s
39       end
40     end
41
42     def log s
43       Redwood::log "hook[#@__name]: #{s}"
44     end
45
46     def ask_yes_or_no q
47       if BufferManager.instantiated?
48         BufferManager.ask_yes_or_no q
49       else
50         print q
51         gets.chomp.downcase == 'y'
52       end
53     end
54
55     def __binding 
56       binding
57     end
58
59     def __cleanup
60       BufferManager.clear @__say_id if @__say_id
61     end
62   end
63
64   include Singleton
65
66   def initialize dir
67     @dir = dir
68     @hooks = {}
69     @descs = {}
70     @contexts = {}
71     
72     Dir.mkdir dir unless File.exists? dir
73
74     self.class.i_am_the_instance self
75   end
76
77   def run name, locals={}
78     hook = hook_for(name) or return
79     context = @contexts[hook] ||= HookContext.new(name)
80     context.__locals = locals
81
82     result = nil
83     begin
84       result = context.instance_eval @hooks[name], fn_for(name)
85     rescue Exception => e
86       log "error running hook: #{e.message}"
87       log e.backtrace.join("\n")
88       BufferManager.flash "Error running hook: #{e.message}"
89       @hooks[name] = nil # disable it
90     end
91     context.__cleanup
92     result
93   end
94
95   def register name, desc
96     @descs[name] = desc
97   end
98
99   def print_hooks f=$stdout
100 puts <<EOS
101 Have #{@descs.size} registered hooks:
102
103 EOS
104
105     @descs.sort.each do |name, desc|
106       f.puts <<EOS
107 #{name}
108 #{"-" * name.length}
109 File: #{fn_for name}
110 #{desc}
111 EOS
112     end
113   end
114
115 private
116
117   def hook_for name
118     unless @hooks.member? name
119       @hooks[name] =
120         begin
121           returning IO.readlines(fn_for(name)).join do
122             log "read '#{name}' from #{fn_for(name)}"
123           end
124         rescue SystemCallError => e
125           #log "disabled hook for '#{name}': #{e.message}"
126           nil
127         end
128     end
129
130     @hooks[name]
131   end
132
133   def fn_for name
134     File.join @dir, "#{name}.rb"
135   end
136
137   def log m
138     Redwood::log("hook: " + m)
139   end
140 end
141
142 end