module Rails
# The Plugin class should be an object which provides the following methods:
#
# * +name+ - used during initialisation to order the plugin (based on name and
# the contents of config.plugins)
# * +valid?+ - returns true if this plugin can be loaded
# * +load_paths+ - each path within the returned array will be added to the $LOAD_PATH
# * +load+ - finally 'load' the plugin.
#
# These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes.
# The default implementation returns the lib directory as its load_paths,
# and evaluates init.rb when load is called.
#
# You can also inspect the about.yml data programmatically:
#
# plugin = Rails::Plugin.new(path_to_my_plugin)
# plugin.about["author"] # => "James Adam"
# plugin.about["url"] # => "http://interblah.net"
class Plugin
include Comparable
attr_reader :directory, :name
def initialize(directory)
@directory = directory
@name = File.basename(@directory) rescue nil
@loaded = false
end
def valid?
File.directory?(directory) && (has_lib_directory? || has_init_file?)
end
# Returns a list of paths this plugin wishes to make available in $LOAD_PATH
def load_paths
report_nonexistant_or_empty_plugin! unless valid?
has_lib_directory? ? [lib_path] : []
end
# Evaluates a plugin's init.rb file
def load(initializer)
return if loaded?
report_nonexistant_or_empty_plugin! unless valid?
evaluate_init_rb(initializer)
@loaded = true
end
def loaded?
@loaded
end
def <=>(other_plugin)
name <=> other_plugin.name
end
def about
@about ||= load_about_information
end
private
def load_about_information
about_yml_path = File.join(@directory, "about.yml")
parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {}
parsed_yml || {}
rescue Exception
{}
end
def report_nonexistant_or_empty_plugin!
raise LoadError, "Can not find the plugin named: #{name}"
end
def lib_path
File.join(directory, 'lib')
end
def init_path
File.join(directory, 'init.rb')
end
def has_lib_directory?
File.directory?(lib_path)
end
def has_init_file?
File.file?(init_path)
end
def evaluate_init_rb(initializer)
if has_init_file?
silence_warnings do
# Allow plugins to reference the current configuration object
config = initializer.configuration
eval(IO.read(init_path), binding, init_path)
end
end
end
end
# This Plugin subclass represents a Gem plugin. It behaves exactly like a
# "traditional" Rails plugin, but doesn't expose any additional load paths,
# since RubyGems has already taken care of things.
class GemPlugin < Plugin
# Initialize this plugin from a Gem::Specification.
def initialize(spec)
super(File.join(spec.full_gem_path, "rails"))
@name = spec.name
end
def valid?
true
end
def load_paths
[]
end
end
end