mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. Plugins are discovered in nested paths, so you can organize your plugins directory as you like. Refactor load_plugin from load_plugins. Simplify initializer unit test. Closes #2757.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2904 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
5c1eb899de
commit
6c434e8b8e
7 changed files with 186 additions and 54 deletions
|
@ -1,5 +1,11 @@
|
|||
*SVN*
|
||||
|
||||
* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. [Jeremy Kemper]
|
||||
|
||||
* Plugins are discovered in nested paths, so you can organize your plugins directory as you like. [Jeremy Kemper]
|
||||
|
||||
* Refactor load_plugin from load_plugins. #2757 [alex.r.moon@gmail.com]
|
||||
|
||||
* Make use of silence_stderr in script/lighttpd, script/plugin, and Rails::Info [Sam Stephenson]
|
||||
|
||||
* Enable HTTP installation of plugins when svn isn't avaialable. Closes #2661. [Chad Fowler]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'logger'
|
||||
require 'set'
|
||||
|
||||
RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
|
||||
|
||||
|
@ -22,7 +23,10 @@ module Rails
|
|||
class Initializer
|
||||
# The Configuration instance used by this Initializer instance.
|
||||
attr_reader :configuration
|
||||
|
||||
|
||||
# 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:
|
||||
|
@ -40,8 +44,9 @@ module Rails
|
|||
# instance.
|
||||
def initialize(configuration)
|
||||
@configuration = configuration
|
||||
@loaded_plugins = Set.new
|
||||
end
|
||||
|
||||
|
||||
# Sequentially step through all of the available initialization routines,
|
||||
# in order:
|
||||
#
|
||||
|
@ -120,32 +125,19 @@ module Rails
|
|||
def load_framework_info
|
||||
require 'rails_info'
|
||||
end
|
||||
|
||||
# Loads all plugins in the <tt>vendor/plugins</tt> directory. Each
|
||||
# subdirectory of <tt>vendor/plugins</tt> is inspected as follows:
|
||||
|
||||
# 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 = ['lib/plugins', 'vendor/plugins']
|
||||
#
|
||||
# * if the directory has a +lib+ subdirectory, add it to the load path
|
||||
# * if the directory contains an <tt>init.rb</tt> file, read it in and
|
||||
# eval it.
|
||||
# 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
|
||||
#
|
||||
# After all plugins are loaded, duplicates are removed from the load path.
|
||||
def load_plugins
|
||||
config = configuration
|
||||
|
||||
Dir.glob("#{configuration.plugins_path}/*") do |directory|
|
||||
next if File.basename(directory)[0] == ?. || !File.directory?(directory)
|
||||
|
||||
if File.directory?("#{directory}/lib")
|
||||
$LOAD_PATH.unshift "#{directory}/lib"
|
||||
end
|
||||
|
||||
if File.exist?("#{directory}/init.rb")
|
||||
silence_warnings do
|
||||
eval(IO.read("#{directory}/init.rb"), binding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
find_plugins(configuration.plugin_paths).each { |path| load_plugin path }
|
||||
$LOAD_PATH.uniq!
|
||||
end
|
||||
|
||||
|
@ -260,8 +252,62 @@ module Rails
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# Return a list of plugin paths within base_path. A plugin path is
|
||||
# a directory that contains either a lib directory or an init.rb file.
|
||||
# This recurses into directories which are not plugin paths, so you
|
||||
# may organize your plugins which the plugin path.
|
||||
def find_plugins(*base_paths)
|
||||
base_paths.flatten.inject([]) do |plugins, base_path|
|
||||
Dir.glob(File.join(base_path, '*')).each do |path|
|
||||
if plugin_path?(path)
|
||||
plugins << path
|
||||
elsif File.directory?(path)
|
||||
plugins += find_plugins(path)
|
||||
end
|
||||
end
|
||||
plugins
|
||||
end
|
||||
end
|
||||
|
||||
def plugin_path?(path)
|
||||
File.directory?(path) and (File.directory?(File.join(path, 'lib')) or File.file?(File.join(path, 'init.rb')))
|
||||
end
|
||||
|
||||
# Load the plugin at <tt>path</tt> unless already loaded.
|
||||
#
|
||||
# Each plugin is initialized:
|
||||
# * add its +lib+ directory, if present, to the beginning of the load path
|
||||
# * evaluate <tt>init.rb</tt> if present
|
||||
#
|
||||
# Returns <tt>true</tt> if the plugin is successfully loaded or
|
||||
# <tt>false</tt> if it is already loaded (similar to Kernel#require).
|
||||
# Raises <tt>LoadError</tt> if the plugin is not found.
|
||||
def load_plugin(path)
|
||||
name = File.basename(path)
|
||||
return false if loaded_plugins.include?(name)
|
||||
|
||||
# Catch nonexistent and empty plugins.
|
||||
raise LoadError, "No such plugin: #{path}" unless plugin_path?(path)
|
||||
|
||||
lib_path = File.join(path, 'lib')
|
||||
init_path = File.join(path, 'init.rb')
|
||||
has_lib = File.directory?(lib_path)
|
||||
has_init = File.file?(init_path)
|
||||
|
||||
# Add lib to load path.
|
||||
$LOAD_PATH.unshift(lib_path) if has_lib
|
||||
|
||||
# Evaluate init.rb.
|
||||
silence_warnings { eval(IO.read(init_path), binding) } if has_init
|
||||
|
||||
# Add to set of loaded plugins.
|
||||
loaded_plugins << name
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The Configuration class holds all the parameters for the Initializer and
|
||||
# ships with defaults that suites most Rails applications. But it's possible
|
||||
# to overwrite everything. Usually, you'll create an Configuration file
|
||||
|
@ -339,6 +385,10 @@ module Rails
|
|||
# any method of +nil+. Set to +false+ for the standard Ruby behavior.
|
||||
attr_accessor :whiny_nils
|
||||
|
||||
# The path to the root of the plugins directory. By default, it is in
|
||||
# <tt>vendor/plugins</tt>.
|
||||
attr_accessor :plugin_paths
|
||||
|
||||
# Create a new Configuration instance, initialized with the default
|
||||
# values.
|
||||
def initialize
|
||||
|
@ -351,8 +401,9 @@ module Rails
|
|||
self.cache_classes = default_cache_classes
|
||||
self.breakpoint_server = default_breakpoint_server
|
||||
self.whiny_nils = default_whiny_nils
|
||||
self.plugin_paths = default_plugin_paths
|
||||
self.database_configuration_file = default_database_configuration_file
|
||||
|
||||
|
||||
for framework in default_frameworks
|
||||
self.send("#{framework}=", OrderedOptions.new)
|
||||
end
|
||||
|
@ -368,15 +419,9 @@ module Rails
|
|||
# The path to the current environment's file (development.rb, etc.). By
|
||||
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
|
||||
def environment_path
|
||||
"#{RAILS_ROOT}/config/environments/#{environment}.rb"
|
||||
"#{root_path}/config/environments/#{environment}.rb"
|
||||
end
|
||||
|
||||
# The path to the root of the plugins directory. By default, it is in
|
||||
# <tt>vendor/plugins</tt>.
|
||||
def plugins_path
|
||||
"#{RAILS_ROOT}/vendor/plugins"
|
||||
end
|
||||
|
||||
# Return the currently selected environment. By default, it returns the
|
||||
# value of the +RAILS_ENV+ constant.
|
||||
def environment
|
||||
|
@ -384,17 +429,21 @@ module Rails
|
|||
end
|
||||
|
||||
private
|
||||
def root_path
|
||||
::RAILS_ROOT
|
||||
end
|
||||
|
||||
def default_frameworks
|
||||
[ :active_record, :action_controller, :action_view, :action_mailer, :action_web_service ]
|
||||
end
|
||||
|
||||
def default_load_paths
|
||||
paths = ["#{RAILS_ROOT}/test/mocks/#{environment}"]
|
||||
paths = ["#{root_path}/test/mocks/#{environment}"]
|
||||
|
||||
# Then model subdirectories.
|
||||
# TODO: Don't include .rb models as load paths
|
||||
paths.concat(Dir["#{RAILS_ROOT}/app/models/[_a-z]*"])
|
||||
paths.concat(Dir["#{RAILS_ROOT}/components/[_a-z]*"])
|
||||
paths.concat(Dir["#{root_path}/app/models/[_a-z]*"])
|
||||
paths.concat(Dir["#{root_path}/components/[_a-z]*"])
|
||||
|
||||
# Followed by the standard includes.
|
||||
# TODO: Don't include dirs for frameworks that are not used
|
||||
|
@ -416,11 +465,11 @@ module Rails
|
|||
vendor/rails/activerecord/lib
|
||||
vendor/rails/actionmailer/lib
|
||||
vendor/rails/actionwebservice/lib
|
||||
).map { |dir| "#{RAILS_ROOT}/#{dir}" }.select { |dir| File.directory?(dir) }
|
||||
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
|
||||
end
|
||||
|
||||
def default_log_path
|
||||
File.join(RAILS_ROOT, 'log', "#{environment}.log")
|
||||
File.join(root_path, 'log', "#{environment}.log")
|
||||
end
|
||||
|
||||
def default_log_level
|
||||
|
@ -428,15 +477,15 @@ module Rails
|
|||
end
|
||||
|
||||
def default_database_configuration_file
|
||||
File.join(RAILS_ROOT, 'config', 'database.yml')
|
||||
File.join(root_path, 'config', 'database.yml')
|
||||
end
|
||||
|
||||
def default_view_path
|
||||
File.join(RAILS_ROOT, 'app', 'views')
|
||||
File.join(root_path, 'app', 'views')
|
||||
end
|
||||
|
||||
def default_controller_paths
|
||||
[ File.join(RAILS_ROOT, 'app', 'controllers'), File.join(RAILS_ROOT, 'components') ]
|
||||
[ File.join(root_path, 'app', 'controllers'), File.join(root_path, 'components') ]
|
||||
end
|
||||
|
||||
def default_dependency_mechanism
|
||||
|
@ -454,6 +503,10 @@ module Rails
|
|||
def default_whiny_nils
|
||||
false
|
||||
end
|
||||
|
||||
def default_plugin_paths
|
||||
["#{root_path}/vendor/plugins"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
SET_FROM_ENV = "success"
|
||||
$initialize_test_set_from_env = "success"
|
||||
|
|
2
railties/test/fixtures/plugins/default/stubby/init.rb
vendored
Normal file
2
railties/test/fixtures/plugins/default/stubby/init.rb
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
require 'stubby_mixin'
|
||||
raise unless defined? StubbyMixin
|
2
railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb
vendored
Normal file
2
railties/test/fixtures/plugins/default/stubby/lib/stubby_mixin.rb
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
module StubbyMixin
|
||||
end
|
|
@ -15,22 +15,19 @@ class InitializerTest < Test::Unit::TestCase
|
|||
def environment_path
|
||||
@envpath
|
||||
end
|
||||
|
||||
protected
|
||||
def root_path
|
||||
File.dirname(__FILE__)
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
Object.const_set(:RAILS_ROOT, "") rescue nil
|
||||
end
|
||||
|
||||
def teardown
|
||||
Object.remove_const(:RAILS_ROOT) rescue nil
|
||||
end
|
||||
|
||||
def test_load_environment_with_constant
|
||||
config = ConfigurationMock.new("#{File.dirname(__FILE__)}/fixtures/environment_with_constant.rb")
|
||||
assert_nil $initialize_test_set_from_env
|
||||
Rails::Initializer.run(:load_environment, config)
|
||||
assert Object.const_defined?(:SET_FROM_ENV)
|
||||
assert_equal "success", SET_FROM_ENV
|
||||
assert_equal "success", $initialize_test_set_from_env
|
||||
ensure
|
||||
Object.remove_const(:SET_FROM_ENV) rescue nil
|
||||
$initialize_test_set_from_env = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
72
railties/test/plugin_test.rb
Normal file
72
railties/test/plugin_test.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
$:.unshift File.dirname(__FILE__) + "/../lib"
|
||||
$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib"
|
||||
|
||||
require 'test/unit'
|
||||
require 'active_support'
|
||||
require 'initializer'
|
||||
|
||||
class PluginTest < Test::Unit::TestCase
|
||||
class TestConfig < Rails::Configuration
|
||||
protected
|
||||
def root_path
|
||||
File.dirname(__FILE__)
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@init = Rails::Initializer.new(TestConfig.new)
|
||||
end
|
||||
|
||||
def test_plugin_path?
|
||||
assert @init.send(:plugin_path?, "#{File.dirname(__FILE__)}/fixtures/plugins/default/stubby")
|
||||
assert !@init.send(:plugin_path?, "#{File.dirname(__FILE__)}/fixtures/plugins/default/empty")
|
||||
assert !@init.send(:plugin_path?, "#{File.dirname(__FILE__)}/fixtures/plugins/default/jalskdjflkas")
|
||||
end
|
||||
|
||||
def test_find_plugins
|
||||
base = "#{File.dirname(__FILE__)}/fixtures/plugins"
|
||||
default = "#{base}/default"
|
||||
alt = "#{base}/alternate"
|
||||
acts = "#{default}/acts"
|
||||
assert_equal ["#{acts}/acts_as_chunky_bacon"], @init.send(:find_plugins, acts)
|
||||
assert_equal ["#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugins, default).sort
|
||||
assert_equal ["#{alt}/a", "#{acts}/acts_as_chunky_bacon", "#{default}/stubby"], @init.send(:find_plugins, base).sort
|
||||
end
|
||||
|
||||
def test_load_plugin
|
||||
stubby = "#{File.dirname(__FILE__)}/fixtures/plugins/default/stubby"
|
||||
expected = Set.new(['stubby'])
|
||||
|
||||
assert @init.send(:load_plugin, stubby)
|
||||
assert_equal expected, @init.loaded_plugins
|
||||
|
||||
assert !@init.send(:load_plugin, stubby)
|
||||
assert_equal expected, @init.loaded_plugins
|
||||
|
||||
assert_raise(LoadError) { @init.send(:load_plugin, 'lakjsdfkasljdf') }
|
||||
assert_equal expected, @init.loaded_plugins
|
||||
end
|
||||
|
||||
def test_load_default_plugins
|
||||
assert_loaded_plugins %w(stubby acts_as_chunky_bacon), 'default'
|
||||
end
|
||||
|
||||
def test_load_alternate_plugins
|
||||
assert_loaded_plugins %w(a), 'alternate'
|
||||
end
|
||||
|
||||
def test_load_plugins_from_two_sources
|
||||
assert_loaded_plugins %w(a stubby acts_as_chunky_bacon), ['default', 'alternate']
|
||||
end
|
||||
|
||||
protected
|
||||
def assert_loaded_plugins(plugins, path)
|
||||
assert_equal Set.new(plugins), load_plugins(path)
|
||||
end
|
||||
|
||||
def load_plugins(*paths)
|
||||
@init.configuration.plugin_paths = paths.flatten.map { |p| "#{File.dirname(__FILE__)}/fixtures/plugins/#{p}" }
|
||||
@init.load_plugins
|
||||
@init.loaded_plugins
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue