1
0
Fork 0
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:
David Heinemeier Hansson 2005-02-20 11:06:14 +00:00
parent 8c40759b10
commit 5fa66cd45c
3 changed files with 98 additions and 44 deletions

View file

@ -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

View file

@ -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

View file

@ -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