]> git.cworth.org Git - sup/blob - lib/sup/hook.rb
adds a cross-hook variable storage mechanism
[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       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 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
83     self.class.i_am_the_instance self
84   end
85
86   attr_reader :tags
87
88   def run name, locals={}
89     hook = hook_for(name) or return
90     context = @contexts[hook] ||= HookContext.new(name)
91     context.__locals = locals
92
93     result = nil
94     begin
95       result = context.instance_eval @hooks[name], fn_for(name)
96     rescue Exception => e
97       log "error running hook: #{e.message}"
98       log e.backtrace.join("\n")
99       @hooks[name] = nil # disable it
100       BufferManager.flash "Error running hook: #{e.message}" if BufferManager.instantiated?
101     end
102     context.__cleanup
103     result
104   end
105
106   def register name, desc
107     @descs[name] = desc
108   end
109
110   def print_hooks f=$stdout
111 puts <<EOS
112 Have #{@descs.size} registered hooks:
113
114 EOS
115
116     @descs.sort.each do |name, desc|
117       f.puts <<EOS
118 #{name}
119 #{"-" * name.length}
120 File: #{fn_for name}
121 #{desc}
122 EOS
123     end
124   end
125
126   def enabled? name; !hook_for(name).nil? end
127
128 private
129
130   def hook_for name
131     unless @hooks.member? name
132       @hooks[name] =
133         begin
134           returning IO.read(fn_for(name)) do
135             log "read '#{name}' from #{fn_for(name)}"
136           end
137         rescue SystemCallError => e
138           #log "disabled hook for '#{name}': #{e.message}"
139           nil
140         end
141     end
142
143     @hooks[name]
144   end
145
146   def fn_for name
147     File.join @dir, "#{name}.rb"
148   end
149
150   def log m
151     Redwood::log("hook: " + m)
152   end
153 end
154
155 end