]> git.cworth.org Git - sup/blob - lib/sup/hook.rb
added before-add-message hook, thanks to Marcus Williams
[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     end
90     context.__cleanup
91     result
92   end
93
94   def register name, desc
95     @descs[name] = desc
96   end
97
98   def print_hooks f=$stdout
99 puts <<EOS
100 Have #{@descs.size} registered hooks:
101
102 EOS
103
104     @descs.sort.each do |name, desc|
105       f.puts <<EOS
106 #{name}
107 #{"-" * name.length}
108 File: #{fn_for name}
109 #{desc}
110 EOS
111     end
112   end
113
114 private
115
116   def hook_for name
117     unless @hooks.member? name
118       @hooks[name] =
119         begin
120           returning IO.readlines(fn_for(name)).join do
121             log "read '#{name}' from #{fn_for(name)}"
122           end
123         rescue SystemCallError => e
124           #log "disabled hook for '#{name}': #{e.message}"
125           nil
126         end
127     end
128
129     @hooks[name]
130   end
131
132   def fn_for name
133     File.join @dir, "#{name}.rb"
134   end
135
136   def log m
137     Redwood::log("hook: " + m)
138   end
139 end
140
141 end