aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-02-20 11:06:14 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-02-20 11:06:14 +0000
commit5fa66cd45c5264080a36d49c9ba803cf2fa3b51b (patch)
tree251425eddd7502ccf3f757b56f29536f0ad2259b
parent8c40759b10ec6a2be2e12a9c160cb64085d490b4 (diff)
downloadrails-5fa66cd45c5264080a36d49c9ba803cf2fa3b51b.tar.gz
rails-5fa66cd45c5264080a36d49c9ba803cf2fa3b51b.tar.bz2
rails-5fa66cd45c5264080a36d49c9ba803cf2fa3b51b.zip
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
-rw-r--r--activesupport/lib/active_support/dependencies.rb108
-rw-r--r--activesupport/test/loading_module_tests.rb32
-rw-r--r--railties/lib/dispatcher.rb2
3 files changed, 98 insertions, 44 deletions
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 1aae239d71..5601893c3d 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -50,62 +50,92 @@ module Dependencies
# Ruby-style modules are supported, as a folder named 'submodule' will load 'submodule.rb' when available.
class LoadingModule < Module
attr_reader :path
-
- def initialize(filesystem_root, path=[])
- @path = path
- @filesystem_root = filesystem_root
+ attr_reader :root
+
+ def self.root(*load_paths)
+ RootLoadingModule.new(*load_paths)
end
-
- # The path to this module in the filesystem.
- # Any subpath provided is taken to be composed of filesystem names.
- def filesystem_path(subpath=[])
- File.join(@filesystem_root, self.path, subpath)
+
+ def initialize(root, path=[])
+ @path = path.clone.freeze
+ @root = root
end
-
+
+ def load_paths() self.root.load_paths end
+
# Load missing constants if possible.
def const_missing(name)
- return const_get(name) if const_defined?(name) == false && const_load!(name)
- super(name)
+ const_load!(name) ? const_get(name) : super(name)
end
-
+
# Load the controller class or a parent module.
def const_load!(name)
- name = name.to_s if name.kind_of? Symbol
-
- if File.directory? filesystem_path(name.underscore)
- # Is it a submodule? If so, create a new LoadingModule *before* loading it.
- # This ensures that subitems will be loadable
- new_module = LoadingModule.new(@filesystem_root, self.path + [name.underscore])
- const_set(name, new_module)
- Object.const_set(name, new_module) if @path.empty?
+ path = self.path + [name]
+ load_paths.each do |load_path|
+ fs_path = load_path.filesystem_path(path)
+ next unless fs_path
+ if File.directory?(fs_path)
+ self.const_set name, LoadingModule.new(self.root, self.path + [name])
+ break
+ elsif File.file?(fs_path)
+ self.root.load_file!(fs_path)
+ break
+ end
end
-
- 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)
+ return self.const_defined?(name)
end
-
+
# Is this name present or loadable?
# This method is used by Routes to find valid controllers.
def const_available?(name)
- name = name.to_s unless name.kind_of? String
- File.directory?(filesystem_path(name.underscore)) || File.file?(filesystem_path("#{name.underscore}.rb"))
+ self.const_defined?(name) || load_paths.any? {|lp| lp.filesystem_path(path + [name])}
end
-
- def clear
+ end
+
+ 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|
- 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)
end
end
-
- def load_file(file_path)
- begin
- Controllers.module_eval(IO.read(file_path), file_path, 1) # Hard coded Controller line here!!!
- rescue Object => exception
- exception.blame_file! file_path
- raise
- end
+ end
+
+ # This object defines a path from which Constants can be loaded.
+ class ConstantLoadPath
+ # Create a new load path with the filesystem path
+ def initialize(root) @root = root end
+
+ # Return nil if the path does not exist, or the path to a directory
+ # 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
diff --git a/activesupport/test/loading_module_tests.rb b/activesupport/test/loading_module_tests.rb
index c1d8c7de62..b6587d48ed 100644
--- a/activesupport/test/loading_module_tests.rb
+++ b/activesupport/test/loading_module_tests.rb
@@ -1,12 +1,13 @@
require 'test/unit'
-require '../lib/core_ext.rb'
-require '../lib/dependencies.rb'
+require File.dirname(__FILE__) + '/../lib/active_support/core_ext.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
def setup
- @loading_module = Dependencies::LoadingModule.new(STAGING_DIRECTORY)
+ @loading_module = Dependencies::LoadingModule.root(STAGING_DIRECTORY)
Object.const_set(:Controllers, @loading_module)
end
def teardown
@@ -60,4 +61,27 @@ class LoadingModuleTests < Test::Unit::TestCase
assert_raises(NameError) {@loading_module::PersonController}
assert_raises(NameError) {@loading_module::Admin::FishController}
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 \ No newline at end of file
diff --git a/railties/lib/dispatcher.rb b/railties/lib/dispatcher.rb
index 74de93713c..6456ea25dc 100644
--- a/railties/lib/dispatcher.rb
+++ b/railties/lib/dispatcher.rb
@@ -46,7 +46,7 @@ class Dispatcher
def reset_application
if Dependencies.load?
- Controllers.clear
+ Controllers.clear!
Dependencies.clear
Dependencies.remove_subclasses_for(ActiveRecord::Base, ActiveRecord::Observer, ActionController::Base)
end