comparison vendor/plugins/coderay-0.9.2/lib/coderay/helpers/.svn/text-base/plugin.rb.svn-base @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:513646585e45
1 module CodeRay
2
3 # = PluginHost
4 #
5 # A simple subclass plugin system.
6 #
7 # Example:
8 # class Generators < PluginHost
9 # plugin_path 'app/generators'
10 # end
11 #
12 # class Generator
13 # extend Plugin
14 # PLUGIN_HOST = Generators
15 # end
16 #
17 # class FancyGenerator < Generator
18 # register_for :fancy
19 # end
20 #
21 # Generators[:fancy] #-> FancyGenerator
22 # # or
23 # CodeRay.require_plugin 'Generators/fancy'
24 module PluginHost
25
26 # Raised if Encoders::[] fails because:
27 # * a file could not be found
28 # * the requested Encoder is not registered
29 PluginNotFound = Class.new Exception
30 HostNotFound = Class.new Exception
31
32 PLUGIN_HOSTS = []
33 PLUGIN_HOSTS_BY_ID = {} # dummy hash
34
35 # Loads all plugins using list and load.
36 def load_all
37 for plugin in list
38 load plugin
39 end
40 end
41
42 # Returns the Plugin for +id+.
43 #
44 # Example:
45 # yaml_plugin = MyPluginHost[:yaml]
46 def [] id, *args, &blk
47 plugin = validate_id(id)
48 begin
49 plugin = plugin_hash.[] plugin, *args, &blk
50 end while plugin.is_a? Symbol
51 plugin
52 end
53
54 # Alias for +[]+.
55 alias load []
56
57 def require_helper plugin_id, helper_name
58 path = path_to File.join(plugin_id, helper_name)
59 require path
60 end
61
62 class << self
63
64 # Adds the module/class to the PLUGIN_HOSTS list.
65 def extended mod
66 PLUGIN_HOSTS << mod
67 end
68
69 # Warns you that you should not #include this module.
70 def included mod
71 warn "#{name} should not be included. Use extend."
72 end
73
74 # Find the PluginHost for host_id.
75 def host_by_id host_id
76 unless PLUGIN_HOSTS_BY_ID.default_proc
77 ph = Hash.new do |h, a_host_id|
78 for host in PLUGIN_HOSTS
79 h[host.host_id] = host
80 end
81 h.fetch a_host_id, nil
82 end
83 PLUGIN_HOSTS_BY_ID.replace ph
84 end
85 PLUGIN_HOSTS_BY_ID[host_id]
86 end
87
88 end
89
90 # The path where the plugins can be found.
91 def plugin_path *args
92 unless args.empty?
93 @plugin_path = File.expand_path File.join(*args)
94 load_map
95 end
96 @plugin_path
97 end
98
99 # The host's ID.
100 #
101 # If PLUGIN_HOST_ID is not set, it is simply the class name.
102 def host_id
103 if self.const_defined? :PLUGIN_HOST_ID
104 self::PLUGIN_HOST_ID
105 else
106 name
107 end
108 end
109
110 # Map a plugin_id to another.
111 #
112 # Usage: Put this in a file plugin_path/_map.rb.
113 #
114 # class MyColorHost < PluginHost
115 # map :navy => :dark_blue,
116 # :maroon => :brown,
117 # :luna => :moon
118 # end
119 def map hash
120 for from, to in hash
121 from = validate_id from
122 to = validate_id to
123 plugin_hash[from] = to unless plugin_hash.has_key? from
124 end
125 end
126
127 # Define the default plugin to use when no plugin is found
128 # for a given id.
129 #
130 # See also map.
131 #
132 # class MyColorHost < PluginHost
133 # map :navy => :dark_blue
134 # default :gray
135 # end
136 def default id = nil
137 if id
138 id = validate_id id
139 plugin_hash[nil] = id
140 else
141 plugin_hash[nil]
142 end
143 end
144
145 # Every plugin must register itself for one or more
146 # +ids+ by calling register_for, which calls this method.
147 #
148 # See Plugin#register_for.
149 def register plugin, *ids
150 for id in ids
151 unless id.is_a? Symbol
152 raise ArgumentError,
153 "id must be a Symbol, but it was a #{id.class}"
154 end
155 plugin_hash[validate_id(id)] = plugin
156 end
157 end
158
159 # A Hash of plugion_id => Plugin pairs.
160 def plugin_hash
161 @plugin_hash ||= create_plugin_hash
162 end
163
164 # Returns an array of all .rb files in the plugin path.
165 #
166 # The extension .rb is not included.
167 def list
168 Dir[path_to('*')].select do |file|
169 File.basename(file)[/^(?!_)\w+\.rb$/]
170 end.map do |file|
171 File.basename file, '.rb'
172 end
173 end
174
175 # Makes a map of all loaded plugins.
176 def inspect
177 map = plugin_hash.dup
178 map.each do |id, plugin|
179 map[id] = plugin.to_s[/(?>\w+)$/]
180 end
181 "#{name}[#{host_id}]#{map.inspect}"
182 end
183
184 protected
185 # Created a new plugin list and stores it to @plugin_hash.
186 def create_plugin_hash
187 @plugin_hash =
188 Hash.new do |h, plugin_id|
189 id = validate_id(plugin_id)
190 path = path_to id
191 begin
192 require path
193 rescue LoadError => boom
194 if h.has_key? nil # default plugin
195 h[id] = h[nil]
196 else
197 raise PluginNotFound, 'Could not load plugin %p: %s' % [id, boom]
198 end
199 else
200 # Plugin should have registered by now
201 unless h.has_key? id
202 raise PluginNotFound,
203 "No #{self.name} plugin for #{id.inspect} found in #{path}."
204 end
205 end
206 h[id]
207 end
208 end
209
210 # Loads the map file (see map).
211 #
212 # This is done automatically when plugin_path is called.
213 def load_map
214 mapfile = path_to '_map'
215 if File.exist? mapfile
216 require mapfile
217 elsif $VERBOSE
218 warn 'no _map.rb found for %s' % name
219 end
220 end
221
222 # Returns the Plugin for +id+.
223 # Use it like Hash#fetch.
224 #
225 # Example:
226 # yaml_plugin = MyPluginHost[:yaml, :default]
227 def fetch id, *args, &blk
228 plugin_hash.fetch validate_id(id), *args, &blk
229 end
230
231 # Returns the expected path to the plugin file for the given id.
232 def path_to plugin_id
233 File.join plugin_path, "#{plugin_id}.rb"
234 end
235
236 # Converts +id+ to a Symbol if it is a String,
237 # or returns +id+ if it already is a Symbol.
238 #
239 # Raises +ArgumentError+ for all other objects, or if the
240 # given String includes non-alphanumeric characters (\W).
241 def validate_id id
242 if id.is_a? Symbol or id.nil?
243 id
244 elsif id.is_a? String
245 if id[/\w+/] == id
246 id.downcase.to_sym
247 else
248 raise ArgumentError, "Invalid id: '#{id}' given."
249 end
250 else
251 raise ArgumentError,
252 "String or Symbol expected, but #{id.class} given."
253 end
254 end
255
256 end
257
258
259 # = Plugin
260 #
261 # Plugins have to include this module.
262 #
263 # IMPORTANT: use extend for this module.
264 #
265 # Example: see PluginHost.
266 module Plugin
267
268 def included mod
269 warn "#{name} should not be included. Use extend."
270 end
271
272 # Register this class for the given langs.
273 # Example:
274 # class MyPlugin < PluginHost::BaseClass
275 # register_for :my_id
276 # ...
277 # end
278 #
279 # See PluginHost.register.
280 def register_for *ids
281 plugin_host.register self, *ids
282 end
283
284 # Returns the title of the plugin, or sets it to the
285 # optional argument +title+.
286 def title title = nil
287 if title
288 @title = title.to_s
289 else
290 @title ||= name[/([^:]+)$/, 1]
291 end
292 end
293
294 # The host for this Plugin class.
295 def plugin_host host = nil
296 if host and not host.is_a? PluginHost
297 raise ArgumentError,
298 "PluginHost expected, but #{host.class} given."
299 end
300 self.const_set :PLUGIN_HOST, host if host
301 self::PLUGIN_HOST
302 end
303
304 # Require some helper files.
305 #
306 # Example:
307 #
308 # class MyPlugin < PluginHost::BaseClass
309 # register_for :my_id
310 # helper :my_helper
311 #
312 # The above example loads the file myplugin/my_helper.rb relative to the
313 # file in which MyPlugin was defined.
314 #
315 # You can also load a helper from a different plugin:
316 #
317 # helper 'other_plugin/helper_name'
318 def helper *helpers
319 for helper in helpers
320 if helper.is_a?(String) && helper[/\//]
321 self::PLUGIN_HOST.require_helper $`, $'
322 else
323 self::PLUGIN_HOST.require_helper plugin_id, helper.to_s
324 end
325 end
326 end
327
328 # Returns the pulgin id used by the engine.
329 def plugin_id
330 name[/\w+$/].downcase
331 end
332
333 end
334
335 # Convenience method for plugin loading.
336 # The syntax used is:
337 #
338 # CodeRay.require_plugin '<Host ID>/<Plugin ID>'
339 #
340 # Returns the loaded plugin.
341 def self.require_plugin path
342 host_id, plugin_id = path.split '/', 2
343 host = PluginHost.host_by_id(host_id)
344 raise PluginHost::HostNotFound,
345 "No host for #{host_id.inspect} found." unless host
346 host.load plugin_id
347 end
348
349 end