|
1 | 1 | module CodeRay |
2 | 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? String |
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 ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map } |
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 | | - if File.exist? mapfile |
162 | | - require mapfile |
163 | | - true |
164 | | - else |
165 | | - false |
166 | | - end |
167 | | - end |
168 | | - |
169 | | - protected |
170 | | - |
171 | | - # Return a plugin hash that automatically loads plugins. |
172 | | - def make_plugin_hash |
173 | | - Hash.new do |h, plugin_id| |
174 | | - id = validate_id(plugin_id) |
175 | | - path = path_to id |
176 | | - begin |
177 | | - require path |
178 | | - rescue LoadError => boom |
179 | | - if h.has_key?(:default) |
180 | | - h[:default] |
181 | | - else |
182 | | - raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom] |
183 | | - end |
184 | | - else |
185 | | - # Plugin should have registered by now |
186 | | - if h.has_key? id |
187 | | - h[id] |
188 | | - else |
189 | | - raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}." |
190 | | - end |
191 | | - end |
192 | | - end |
193 | | - end |
194 | | - |
195 | | - # Returns the expected path to the plugin file for the given id. |
196 | | - def path_to plugin_id |
197 | | - File.join plugin_path, "#{plugin_id}.rb" |
198 | | - end |
199 | | - |
200 | | - # Converts +id+ to a valid plugin ID String, or returns +nil+. |
201 | | - # |
202 | | - # Raises +ArgumentError+ for all other objects, or if the |
203 | | - # given String includes non-alphanumeric characters (\W). |
204 | | - def validate_id id |
205 | | - case id |
206 | | - when Symbol |
207 | | - id.to_s |
208 | | - when String |
209 | | - if id[/\w+/] == id |
210 | | - id.downcase |
211 | | - else |
212 | | - raise ArgumentError, "Invalid id given: #{id}" |
213 | | - end |
214 | | - else |
215 | | - raise ArgumentError, "Symbol or String expected, but #{id.class} given." |
216 | | - end |
217 | | - end |
218 | | - |
219 | | - end |
220 | | - |
221 | | - |
222 | 3 | # = Plugin |
223 | 4 | # |
224 | 5 | # Plugins have to include this module. |
|
0 commit comments