mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Allows a loading module to load from multiple load paths #675
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@711 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
8c40759b10
commit
5fa66cd45c
3 changed files with 98 additions and 44 deletions
|
@ -50,62 +50,92 @@ module Dependencies
|
||||||
# Ruby-style modules are supported, as a folder named 'submodule' will load 'submodule.rb' when available.
|
# Ruby-style modules are supported, as a folder named 'submodule' will load 'submodule.rb' when available.
|
||||||
class LoadingModule < Module
|
class LoadingModule < Module
|
||||||
attr_reader :path
|
attr_reader :path
|
||||||
|
attr_reader :root
|
||||||
|
|
||||||
def initialize(filesystem_root, path=[])
|
def self.root(*load_paths)
|
||||||
@path = path
|
RootLoadingModule.new(*load_paths)
|
||||||
@filesystem_root = filesystem_root
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# The path to this module in the filesystem.
|
def initialize(root, path=[])
|
||||||
# Any subpath provided is taken to be composed of filesystem names.
|
@path = path.clone.freeze
|
||||||
def filesystem_path(subpath=[])
|
@root = root
|
||||||
File.join(@filesystem_root, self.path, subpath)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def load_paths() self.root.load_paths end
|
||||||
|
|
||||||
# Load missing constants if possible.
|
# Load missing constants if possible.
|
||||||
def const_missing(name)
|
def const_missing(name)
|
||||||
return const_get(name) if const_defined?(name) == false && const_load!(name)
|
const_load!(name) ? const_get(name) : super(name)
|
||||||
super(name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Load the controller class or a parent module.
|
# Load the controller class or a parent module.
|
||||||
def const_load!(name)
|
def const_load!(name)
|
||||||
name = name.to_s if name.kind_of? Symbol
|
path = self.path + [name]
|
||||||
|
load_paths.each do |load_path|
|
||||||
if File.directory? filesystem_path(name.underscore)
|
fs_path = load_path.filesystem_path(path)
|
||||||
# Is it a submodule? If so, create a new LoadingModule *before* loading it.
|
next unless fs_path
|
||||||
# This ensures that subitems will be loadable
|
if File.directory?(fs_path)
|
||||||
new_module = LoadingModule.new(@filesystem_root, self.path + [name.underscore])
|
self.const_set name, LoadingModule.new(self.root, self.path + [name])
|
||||||
const_set(name, new_module)
|
break
|
||||||
Object.const_set(name, new_module) if @path.empty?
|
elsif File.file?(fs_path)
|
||||||
|
self.root.load_file!(fs_path)
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
return self.const_defined?(name)
|
||||||
source_file = filesystem_path("#{(name == 'ApplicationController' ? 'Application' : name).underscore}.rb")
|
|
||||||
self.load_file(source_file) if File.file?(source_file)
|
|
||||||
self.const_defined?(name.camelize)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this name present or loadable?
|
# Is this name present or loadable?
|
||||||
# This method is used by Routes to find valid controllers.
|
# This method is used by Routes to find valid controllers.
|
||||||
def const_available?(name)
|
def const_available?(name)
|
||||||
name = name.to_s unless name.kind_of? String
|
self.const_defined?(name) || load_paths.any? {|lp| lp.filesystem_path(path + [name])}
|
||||||
File.directory?(filesystem_path(name.underscore)) || File.file?(filesystem_path("#{name.underscore}.rb"))
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def clear
|
class RootLoadingModule < LoadingModule
|
||||||
|
attr_reader :load_paths
|
||||||
|
def initialize(*paths)
|
||||||
|
@load_paths = paths.flatten.collect {|p| p.kind_of?(ConstantLoadPath) ? p : ConstantLoadPath.new(p)}
|
||||||
|
end
|
||||||
|
def root() self end
|
||||||
|
def path() [] end
|
||||||
|
|
||||||
|
# Load the source file at the given file path
|
||||||
|
def load_file!(file_path)
|
||||||
|
root.module_eval(IO.read(file_path), file_path, 1)
|
||||||
|
end
|
||||||
|
# Erase all items in this module
|
||||||
|
def clear!
|
||||||
constants.each do |name|
|
constants.each do |name|
|
||||||
Object.send(:remove_const, name) if Object.const_defined?(name) && @path.empty?
|
Object.send(:remove_const, name) if Object.const_defined?(name) && self.path.empty?
|
||||||
self.send(:remove_const, name)
|
self.send(:remove_const, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def load_file(file_path)
|
# This object defines a path from which Constants can be loaded.
|
||||||
begin
|
class ConstantLoadPath
|
||||||
Controllers.module_eval(IO.read(file_path), file_path, 1) # Hard coded Controller line here!!!
|
# Create a new load path with the filesystem path
|
||||||
rescue Object => exception
|
def initialize(root) @root = root end
|
||||||
exception.blame_file! file_path
|
|
||||||
raise
|
# Return nil if the path does not exist, or the path to a directory
|
||||||
end
|
# if the path leads to a module, or the path to a file if it leads to an object.
|
||||||
|
def filesystem_path(path, allow_module=true)
|
||||||
|
fs_path = [@root]
|
||||||
|
fs_path += path[0..-2].collect {|name| const_name_to_module_name name}
|
||||||
|
if allow_module
|
||||||
|
result = File.join(fs_path, const_name_to_module_name(path.last))
|
||||||
|
return result if File.directory? result # Return the module path if one exists
|
||||||
|
end
|
||||||
|
result = File.join(fs_path, const_name_to_file_name(path.last))
|
||||||
|
return File.file?(result) ? result : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def const_name_to_file_name(name)
|
||||||
|
name.to_s.underscore + '.rb'
|
||||||
|
end
|
||||||
|
def const_name_to_module_name(name)
|
||||||
|
name.to_s.underscore
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require '../lib/core_ext.rb'
|
require File.dirname(__FILE__) + '/../lib/active_support/core_ext.rb'
|
||||||
require '../lib/dependencies.rb'
|
require File.dirname(__FILE__) + '/../lib/active_support/dependencies.rb'
|
||||||
|
|
||||||
STAGING_DIRECTORY = 'loading_module'
|
STAGING_DIRECTORY = File.join(File.dirname(__FILE__), 'loading_module')
|
||||||
|
COMPONENTS_DIRECTORY = File.join(File.dirname(__FILE__), 'loading_module_components')
|
||||||
|
|
||||||
class LoadingModuleTests < Test::Unit::TestCase
|
class LoadingModuleTests < Test::Unit::TestCase
|
||||||
def setup
|
def setup
|
||||||
@loading_module = Dependencies::LoadingModule.new(STAGING_DIRECTORY)
|
@loading_module = Dependencies::LoadingModule.root(STAGING_DIRECTORY)
|
||||||
Object.const_set(:Controllers, @loading_module)
|
Object.const_set(:Controllers, @loading_module)
|
||||||
end
|
end
|
||||||
def teardown
|
def teardown
|
||||||
|
@ -61,3 +62,26 @@ class LoadingModuleTests < Test::Unit::TestCase
|
||||||
assert_raises(NameError) {@loading_module::Admin::FishController}
|
assert_raises(NameError) {@loading_module::Admin::FishController}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class LoadingModuleMultiPathTests < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
@loading_module = Dependencies::LoadingModule.root(STAGING_DIRECTORY, COMPONENTS_DIRECTORY)
|
||||||
|
Object.const_set(:Controllers, @loading_module)
|
||||||
|
end
|
||||||
|
def teardown
|
||||||
|
@loading_module.clear
|
||||||
|
Object.send :remove_const, :Controllers
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_access_from_first
|
||||||
|
assert_kind_of Module, @loading_module::Admin
|
||||||
|
assert_kind_of Dependencies::LoadingModule, @loading_module::Admin
|
||||||
|
assert_kind_of Class, @loading_module::Admin::UserController
|
||||||
|
end
|
||||||
|
def test_access_from_second
|
||||||
|
assert_kind_of Module, @loading_module::List
|
||||||
|
assert_kind_of Dependencies::LoadingModule, @loading_module::List
|
||||||
|
assert @loading_module::List.const_load! :ListController
|
||||||
|
assert_kind_of Class, @loading_module::List::ListController
|
||||||
|
end
|
||||||
|
end
|
|
@ -46,7 +46,7 @@ class Dispatcher
|
||||||
|
|
||||||
def reset_application
|
def reset_application
|
||||||
if Dependencies.load?
|
if Dependencies.load?
|
||||||
Controllers.clear
|
Controllers.clear!
|
||||||
Dependencies.clear
|
Dependencies.clear
|
||||||
Dependencies.remove_subclasses_for(ActiveRecord::Base, ActiveRecord::Observer, ActionController::Base)
|
Dependencies.remove_subclasses_for(ActiveRecord::Base, ActiveRecord::Observer, ActionController::Base)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue