Skip to content

Commit

Permalink
Refactor Plugin Loader. Add plugin lib paths early, and add lots of t…
Browse files Browse the repository at this point in the history
…ests. Closes #9795 [lazyatom]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8115 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
technoweenie committed Nov 8, 2007
1 parent c443a61 commit f1b1af8
Show file tree
Hide file tree
Showing 10 changed files with 679 additions and 294 deletions.
2 changes: 2 additions & 0 deletions railties/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*

* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 [lazyatom]

* Added --skip-timestamps to generators that produce models #10036 [tpope]

* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. [sam, madrobby]
Expand Down
39 changes: 18 additions & 21 deletions railties/lib/initializer.rb
Expand Up @@ -34,7 +34,7 @@ class Initializer

# The set of loaded plugins.
attr_reader :loaded_plugins

# Runs the initializer. By default, this will invoke the #process method,
# which simply executes all of the initialization routines. Alternately,
# you can specify explicitly which initialization routine you want:
Expand Down Expand Up @@ -64,6 +64,7 @@ def initialize(configuration)
# * #set_load_path
# * #require_frameworks
# * #set_autoload_paths
# * add_plugin_load_paths
# * #load_environment
# * #initialize_encoding
# * #initialize_database
Expand All @@ -83,9 +84,10 @@ def initialize(configuration)
def process
check_ruby_version
set_load_path

require_frameworks
set_autoload_paths
add_plugin_load_paths
load_environment

initialize_encoding
Expand Down Expand Up @@ -161,14 +163,20 @@ def require_frameworks
def add_support_load_paths
end

# Adds all load paths from plugins to the global set of load paths, so that
# code from plugins can be required (explicitly or automatically via Dependencies).
def add_plugin_load_paths
plugin_loader.add_plugin_load_paths
end

# Loads all plugins in <tt>config.plugin_paths</tt>. <tt>plugin_paths</tt>
# defaults to <tt>vendor/plugins</tt> but may also be set to a list of
# paths, such as
# config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"]
#
# Each plugin discovered in <tt>plugin_paths</tt> is initialized:
# * add its +lib+ directory, if present, to the beginning of the load path
# * evaluate <tt>init.rb</tt> if present
# In the default implementation, as each plugin discovered in <tt>plugin_paths</tt> is initialized:
# * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory)
# * <tt>init.rb</tt> is evalutated, if present
#
# After all plugins are loaded, duplicates are removed from the load path.
# If an array of plugin names is specified in config.plugins, only those plugins will be loaded
Expand All @@ -178,13 +186,11 @@ def add_support_load_paths
# if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other
# plugins will be loaded in alphabetical order
def load_plugins
configuration.plugin_locators.each do |locator|
locator.new(self).each do |plugin|
plugin.load
end
end
ensure_all_registered_plugins_are_loaded!
$LOAD_PATH.uniq!
plugin_loader.load_plugins
end

def plugin_loader
@plugin_loader ||= configuration.plugin_loader.new(self)
end

# Loads the environment specified by Configuration#environment_path, which
Expand Down Expand Up @@ -337,15 +343,6 @@ def load_application_initializers
end
end

private
def ensure_all_registered_plugins_are_loaded!
unless configuration.plugins.nil?
if configuration.plugins.detect {|plugin| plugin != :all && !loaded_plugins.include?( plugin)}
missing_plugins = configuration.plugins - (loaded_plugins + [:all])
raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence}"
end
end
end
end

# The Configuration class holds all the parameters for the Initializer and
Expand Down
83 changes: 83 additions & 0 deletions railties/lib/rails/plugin.rb
@@ -0,0 +1,83 @@
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 <tt>config.plugins</tt>)
# * +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 <tt>lib</tt> directory as its </tt>load_paths</tt>,
# and evaluates <tt>init.rb</tt> when <tt>load</tt> is called.
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)
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

private

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
end

0 comments on commit f1b1af8

Please sign in to comment.