]> git.cworth.org Git - sup/blob - lib/sup/hook.rb
minor: move hook load messages from info to debug
[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   ## "fail-fast", we'll call it.
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] = @__locals[m].call(*a) # only call the proc once
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       info "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 get tag
56       HookManager.tags[tag]
57     end
58
59     def set tag, value
60       HookManager.tags[tag] = value
61     end
62
63     def __binding 
64       binding
65     end
66
67     def __cleanup
68       BufferManager.clear @__say_id if @__say_id
69     end
70   end
71
72   include Singleton
73
74   def initialize dir
75     @dir = dir
76     @hooks = {}
77     @descs = {}
78     @contexts = {}
79     @tags = {}
80
81     Dir.mkdir dir unless File.exists? dir
82   end
83
84   attr_reader :tags
85
86   def run name, locals={}
87     hook = hook_for(name) or return
88     context = @contexts[hook] ||= HookContext.new(name)
89     context.__locals = locals
90
91     result = nil
92     begin
93       result = context.instance_eval @hooks[name], fn_for(name)
94     rescue Exception => e
95       log "error running hook: #{e.message}"
96       log e.backtrace.join("\n")
97       @hooks[name] = nil # disable it
98       BufferManager.flash "Error running hook: #{e.message}" if BufferManager.instantiated?
99     end
100     context.__cleanup
101     result
102   end
103
104   def register name, desc
105     @descs[name] = desc
106   end
107
108   def print_hooks f=$stdout
109 puts <<EOS
110 Have #{@descs.size} registered hooks:
111
112 EOS
113
114     @descs.sort.each do |name, desc|
115       f.puts <<EOS
116 #{name}
117 #{"-" * name.length}
118 File: #{fn_for name}
119 #{desc}
120 EOS
121     end
122   end
123
124   def enabled? name; !hook_for(name).nil? end
125
126 private
127
128   def hook_for name
129     unless @hooks.member? name
130       @hooks[name] =
131         begin
132           returning IO.read(fn_for(name)) do
133             debug "read '#{name}' from #{fn_for(name)}"
134           end
135         rescue SystemCallError => e
136           #log "disabled hook for '#{name}': #{e.message}"
137           nil
138         end
139     end
140
141     @hooks[name]
142   end
143
144   def fn_for name
145     File.join @dir, "#{name}.rb"
146   end
147
148   def log m
149     info("hook: " + m)
150   end
151 end
152
153 end