/Ruby/lib/ruby/gems/2.0.0/gems/coderay-1.1.0/lib/coderay/helpers/plugin.rb

https://gitlab.com/orvi2014/rcs-db-ext · Ruby · 274 lines · 205 code · 13 blank · 56 comment · 4 complexity · 42d87bb6b494a94c719bef643f12971f MD5 · raw file

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