To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / vendor / gems / coderay-0.9.7 / lib / coderay / helpers / plugin.rb @ 442:753f1380d6bc

History | View | Annotate | Download (7.74 KB)

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