I have written a plugin loader in Ruby, and I would like to know if it uses the best technique to handle classes, or if anyone has any recommendations on how to improve my code.
I have written a class called PluginManager, and an accompanying module named Plugin. The names of these are self explanatory, one handles the plugins (loading them), and the other is a module to be included by plugin files.
My Plugin module looks like the following, and currently just replaces the puts method, to ensure all output comes through the logger. I had some issues with making the @@logger variable definable, and I have solved it, but I'm not sure if there is a better way to solve it.
module Plugin
@@logger = nil
def puts(string)
if @@logger.nil?
super(string)
else
@@logger.info string
end
end
# I tried the following, but it didn't work (still said "undefined method 'logger='")
# class << self
# attr_accessor :logger
# end
def self.logger=(string)
@@logger = string
end
end
My PluginManager class is a bit more complex, but not by much. It simply takes an array of file names, performs some checks to ensure everything exists, then loads them into the loaded_plugins
array.
class PluginManager
def initialize(logger=nil)
set_logger logger
@loaded_plugins = []
end
def set_logger(logger)
@logger = logger
Plugin.logger = logger
end
def load(plugins, directory="./plugins/")
# Iterate through plugins
plugins.each do |plugin|
# Work out plugin information
class_name = plugin.split(".").first.downcase.capitalize
full_path = directory + plugin
@logger.info "Loading \"#{class_name}\" from \"#{full_path}\""
# Ensure the plugin file exists
if File.file? full_path
# Load the plugin file and ensure the class exists
require full_path
unless eval("defined? #{class_name}")
@logger.error "Class \"#{class_name}\" not found!"
next
end
# Get the plugin's class and ensure it includes plugin
plugin_class = eval class_name
if plugin_class.ancestors.include? Plugin
# Ensure the initialize method takes no arguments
if plugin_class.allocate.method(:initialize).arity > 0
@logger.error "\"#{class_name}.initialize\" accepts more than 0 arguments"
next
end
# Load the plugin itself by pushing it into the loaded_plugins list
@logger.info "Plugin \"#{class_name}\" loaded!"
@loaded_plugins.push plugin_class.new
@logger.info "Plugin \"#{class_name}\" enabled!"
else
@logger.error "The class \"#{class_name}\" does not include Plugin!"
next
end
else
# Check if the file exists, or is a directory
unless File.exist? full_path
@logger.error "Unable to find file \"#{full_path}\""
else
@logger.error "\"#{full_path}\" is a directory!"
end
next
end
end
end
end
I am wondering if there are any better ways to perform some of the checks, such as using exec to find if a class exists.