comparison vendor/gems/coderay-1.0.0/lib/coderay/helpers/plugin.rb @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents
children
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
1 module CodeRay
2
3 # = PluginHost
4 #
5 # A simple subclass/subfolder plugin system.
6 #
7 # Example:
8 # class Generators
9 # extend PluginHost
10 # plugin_path 'app/generators'
11 # end
12 #
13 # class Generator
14 # extend Plugin
15 # PLUGIN_HOST = Generators
16 # end
17 #
18 # class FancyGenerator < Generator
19 # register_for :fancy
20 # end
21 #
22 # Generators[:fancy] #-> FancyGenerator
23 # # or
24 # CodeRay.require_plugin 'Generators/fancy'
25 # # or
26 # Generators::Fancy
27 module PluginHost
28
29 # Raised if Encoders::[] fails because:
30 # * a file could not be found
31 # * the requested Plugin is not registered
32 PluginNotFound = Class.new LoadError
33 HostNotFound = Class.new LoadError
34
35 PLUGIN_HOSTS = []
36 PLUGIN_HOSTS_BY_ID = {} # dummy hash
37
38 # Loads all plugins using list and load.
39 def load_all
40 for plugin in list
41 load plugin
42 end
43 end
44
45 # Returns the Plugin for +id+.
46 #
47 # Example:
48 # yaml_plugin = MyPluginHost[:yaml]
49 def [] id, *args, &blk
50 plugin = validate_id(id)
51 begin
52 plugin = plugin_hash.[] plugin, *args, &blk
53 end while plugin.is_a? Symbol
54 plugin
55 end
56
57 alias load []
58
59 # Tries to +load+ the missing plugin by translating +const+ to the
60 # underscore form (eg. LinesOfCode becomes lines_of_code).
61 def const_missing const
62 id = const.to_s.
63 gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
64 gsub(/([a-z\d])([A-Z])/,'\1_\2').
65 downcase
66 load id
67 end
68
69 class << self
70
71 # Adds the module/class to the PLUGIN_HOSTS list.
72 def extended mod
73 PLUGIN_HOSTS << mod
74 end
75
76 end
77
78 # The path where the plugins can be found.
79 def plugin_path *args
80 unless args.empty?
81 @plugin_path = File.expand_path File.join(*args)
82 end
83 @plugin_path ||= ''
84 end
85
86 # Map a plugin_id to another.
87 #
88 # Usage: Put this in a file plugin_path/_map.rb.
89 #
90 # class MyColorHost < PluginHost
91 # map :navy => :dark_blue,
92 # :maroon => :brown,
93 # :luna => :moon
94 # end
95 def map hash
96 for from, to in hash
97 from = validate_id from
98 to = validate_id to
99 plugin_hash[from] = to unless plugin_hash.has_key? from
100 end
101 end
102
103 # Define the default plugin to use when no plugin is found
104 # for a given id, or return the default plugin.
105 #
106 # See also map.
107 #
108 # class MyColorHost < PluginHost
109 # map :navy => :dark_blue
110 # default :gray
111 # end
112 #
113 # MyColorHost.default # loads and returns the Gray plugin
114 def default id = nil
115 if id
116 id = validate_id id
117 raise "The default plugin can't be named \"default\"." if id == :default
118 plugin_hash[:default] = id
119 else
120 load :default
121 end
122 end
123
124 # Every plugin must register itself for +id+ by calling register_for,
125 # which calls this method.
126 #
127 # See Plugin#register_for.
128 def register plugin, id
129 plugin_hash[validate_id(id)] = plugin
130 end
131
132 # A Hash of plugion_id => Plugin pairs.
133 def plugin_hash
134 @plugin_hash ||= make_plugin_hash
135 end
136
137 # Returns an array of all .rb files in the plugin path.
138 #
139 # The extension .rb is not included.
140 def list
141 Dir[path_to('*')].select do |file|
142 File.basename(file)[/^(?!_)\w+\.rb$/]
143 end.map do |file|
144 File.basename(file, '.rb').to_sym
145 end
146 end
147
148 # Returns an array of all Plugins.
149 #
150 # Note: This loads all plugins using load_all.
151 def all_plugins
152 load_all
153 plugin_hash.values.grep(Class)
154 end
155
156 # Loads the map file (see map).
157 #
158 # This is done automatically when plugin_path is called.
159 def load_plugin_map
160 mapfile = path_to '_map'
161 @plugin_map_loaded = true
162 if File.exist? mapfile
163 require mapfile
164 true
165 else
166 false
167 end
168 end
169
170 protected
171
172 # Return a plugin hash that automatically loads plugins.
173 def make_plugin_hash
174 @plugin_map_loaded ||= false
175 Hash.new do |h, plugin_id|
176 id = validate_id(plugin_id)
177 path = path_to id
178 begin
179 raise LoadError, "#{path} not found" unless File.exist? path
180 require path
181 rescue LoadError => boom
182 if @plugin_map_loaded
183 if h.has_key?(:default)
184 warn '%p could not load plugin %p; falling back to %p' % [self, id, h[:default]]
185 h[:default]
186 else
187 raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
188 end
189 else
190 load_plugin_map
191 h[plugin_id]
192 end
193 else
194 # Plugin should have registered by now
195 if h.has_key? id
196 h[id]
197 else
198 raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}."
199 end
200 end
201 end
202 end
203
204 # Returns the expected path to the plugin file for the given id.
205 def path_to plugin_id
206 File.join plugin_path, "#{plugin_id}.rb"
207 end
208
209 # Converts +id+ to a Symbol if it is a String,
210 # or returns +id+ if it already is a Symbol.
211 #
212 # Raises +ArgumentError+ for all other objects, or if the
213 # given String includes non-alphanumeric characters (\W).
214 def validate_id id
215 if id.is_a? Symbol or id.nil?
216 id
217 elsif id.is_a? String
218 if id[/\w+/] == id
219 id.downcase.to_sym
220 else
221 raise ArgumentError, "Invalid id given: #{id}"
222 end
223 else
224 raise ArgumentError, "String or Symbol expected, but #{id.class} given."
225 end
226 end
227
228 end
229
230
231 # = Plugin
232 #
233 # Plugins have to include this module.
234 #
235 # IMPORTANT: Use extend for this module.
236 #
237 # See CodeRay::PluginHost for examples.
238 module Plugin
239
240 attr_reader :plugin_id
241
242 # Register this class for the given +id+.
243 #
244 # Example:
245 # class MyPlugin < PluginHost::BaseClass
246 # register_for :my_id
247 # ...
248 # end
249 #
250 # See PluginHost.register.
251 def register_for id
252 @plugin_id = id
253 plugin_host.register self, id
254 end
255
256 # Returns the title of the plugin, or sets it to the
257 # optional argument +title+.
258 def title title = nil
259 if title
260 @title = title.to_s
261 else
262 @title ||= name[/([^:]+)$/, 1]
263 end
264 end
265
266 # The PluginHost for this Plugin class.
267 def plugin_host host = nil
268 if host.is_a? PluginHost
269 const_set :PLUGIN_HOST, host
270 end
271 self::PLUGIN_HOST
272 end
273
274 def aliases
275 plugin_host.load_plugin_map
276 plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
277 aliases << key if plugin_host[key] == self
278 aliases
279 end
280 end
281
282 end
283
284 end