aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2005-02-15 00:51:02 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2005-02-15 00:51:02 +0000
commitc844755e5a0c3d4edfcc78f9c30ef91fa0de550a (patch)
tree4cf4890fc5af5f58dd0a6a19c0a6fea6ed39a1df /activesupport
parenta3298e5efdf33398b49933323ea3fef7ff4e9a9c (diff)
downloadrails-c844755e5a0c3d4edfcc78f9c30ef91fa0de550a.tar.gz
rails-c844755e5a0c3d4edfcc78f9c30ef91fa0de550a.tar.bz2
rails-c844755e5a0c3d4edfcc78f9c30ef91fa0de550a.zip
Merged back the Routing branch
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@614 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG4
-rw-r--r--activesupport/lib/core_ext/hash/indifferent_access.rb10
-rw-r--r--activesupport/lib/core_ext/string/inflections.rb4
-rw-r--r--activesupport/lib/dependencies.rb67
-rw-r--r--activesupport/lib/inflector.rb12
-rw-r--r--activesupport/test/inflector_test.rb31
-rw-r--r--activesupport/test/loading_module/admin/access_controller.rb2
-rw-r--r--activesupport/test/loading_module/admin/user_controller.rb2
-rw-r--r--activesupport/test/loading_module/content_controller.rb2
-rw-r--r--activesupport/test/loading_module/resource_controller.rb2
-rw-r--r--activesupport/test/loading_module_tests.rb63
11 files changed, 190 insertions, 9 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 7a005ce970..cf9fc0a728 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -2,6 +2,10 @@
* Added IndifferentAccess as a way to wrap a hash by a symbol-based store that also can be accessed by string keys
+* Added Inflector.constantize to turn "Admin::User" into a reference for the constant Admin::User
+
+* Added that Inflector.camelize and Inflector.underscore can deal with modules like turning "Admin::User" into "admin/user" and back
+
* Added Inflector.humanize to turn attribute names like employee_salary into "Employee salary". Used by automated error reporting in AR.
* Added availability of class inheritable attributes to the masses #477 [bitsweat]
diff --git a/activesupport/lib/core_ext/hash/indifferent_access.rb b/activesupport/lib/core_ext/hash/indifferent_access.rb
index 3fe0999866..2353cfaf3b 100644
--- a/activesupport/lib/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/core_ext/hash/indifferent_access.rb
@@ -8,17 +8,17 @@ class HashWithIndifferentAccess < Hash
end
end
- alias_method :regular_read, :[]
+ alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
def [](key)
case key
- when Symbol: regular_read(key) || regular_read(key.to_s)
- when String: regular_read(key) || regular_read(key.to_sym)
- else regular_read(key)
+ when Symbol: regular_reader(key) || regular_reader(key.to_s)
+ when String: regular_reader(key) || regular_reader(key.to_sym)
+ else regular_reader(key)
end
end
- alias_method :regular_writer, :[]=
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
def []=(key, value)
regular_writer(key.is_a?(String) ? key.to_sym : key, value)
diff --git a/activesupport/lib/core_ext/string/inflections.rb b/activesupport/lib/core_ext/string/inflections.rb
index 5d1070b00a..aa4ff3a74d 100644
--- a/activesupport/lib/core_ext/string/inflections.rb
+++ b/activesupport/lib/core_ext/string/inflections.rb
@@ -39,6 +39,10 @@ module ActiveSupport #:nodoc:
def foreign_key(separate_class_name_and_id_with_underscore = true)
Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
end
+
+ def constantize
+ Inflector.constantize(self)
+ end
end
end
end
diff --git a/activesupport/lib/dependencies.rb b/activesupport/lib/dependencies.rb
index 7f53998e7a..0a9b8e1d3d 100644
--- a/activesupport/lib/dependencies.rb
+++ b/activesupport/lib/dependencies.rb
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/module_attribute_accessors'
module Dependencies
extend self
-
+
@@loaded = [ ]
mattr_accessor :loaded
@@ -41,6 +41,68 @@ module Dependencies
def remove_subclasses_for(*classes)
classes.each { |klass| klass.remove_subclasses }
end
+
+ # LoadingModules implement namespace-safe dynamic loading.
+ # They support automatic loading via const_missing, allowing contained items to be automatically
+ # loaded when required. No extra syntax is required, as expressions such as Controller::Admin::UserController
+ # load the relavent files automatically.
+ #
+ # 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
+ 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)
+ end
+
+ # Load missing constants if possible.
+ def const_missing(name)
+ return const_get(name) if const_defined?(name) == false && const_load!(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?
+ 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)
+ 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"))
+ end
+
+ def clear
+ constants.each do |name|
+ Object.send(:remove_const, name) if Object.const_defined?(name) && @path.empty?
+ self.send(:remove_const, name)
+ end
+ end
+
+ def load_file(file_path)
+ Controllers.module_eval(IO.read(file_path), file_path, 1) # Hard coded Controller line here!!!
+ end
+ end
end
Object.send(:define_method, :require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load)
@@ -52,6 +114,9 @@ class Object #:nodoc:
# Use const_missing to autoload associations so we don't have to
# require_association when using single-table inheritance.
def const_missing(class_id)
+ if Object.const_defined?(:Controllers) and Object::Controllers.const_available?(class_id)
+ return Object::Controllers.const_get(class_id)
+ end
begin
require_or_load(class_id.to_s.demodulize.underscore)
if Object.const_defined?(class_id) then return Object.const_get(class_id) else raise LoadError end
diff --git a/activesupport/lib/inflector.rb b/activesupport/lib/inflector.rb
index 065fad3762..a9b2f87be4 100644
--- a/activesupport/lib/inflector.rb
+++ b/activesupport/lib/inflector.rb
@@ -20,11 +20,11 @@ module Inflector
end
def camelize(lower_case_and_underscored_word)
- lower_case_and_underscored_word.to_s.gsub(/(^|_)(.)/){$2.upcase}
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
end
def underscore(camel_cased_word)
- camel_cased_word.to_s.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
+ camel_cased_word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
end
def humanize(lower_case_and_underscored_word)
@@ -47,7 +47,13 @@ module Inflector
Inflector.underscore(Inflector.demodulize(class_name)) +
(separate_class_name_and_id_with_underscore ? "_id" : "id")
end
-
+
+ def constantize(camel_cased_word)
+ camel_cased_word.split("::").inject(Object) do |final_type, part|
+ final_type = final_type.const_get(part)
+ end
+ end
+
private
def plural_rules #:doc:
[
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index 4523430ebe..cdab0f9ed0 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -1,6 +1,13 @@
require 'test/unit'
require File.dirname(__FILE__) + '/../lib/inflector'
+module A
+ module B
+ class C
+ end
+ end
+end
+
class InflectorTest < Test::Unit::TestCase
SingularToPlural = {
"search" => "searches",
@@ -50,6 +57,12 @@ class InflectorTest < Test::Unit::TestCase
"SpecialGuest" => "special_guest",
"ApplicationController" => "application_controller"
}
+
+ CamelWithModuleToUnderscoreWithSlash = {
+ "Admin::Product" => "admin/product",
+ "Users::Commission::Department" => "users/commission/department",
+ "UsersSection::CommissionDepartment" => "users_section/commission_department",
+ }
ClassNameToForeignKeyWithUnderscore = {
"Person" => "person_id",
@@ -100,6 +113,18 @@ class InflectorTest < Test::Unit::TestCase
assert_equal "html_tidy_generator", Inflector.underscore("HTMLTidyGenerator")
end
+ def test_camelize_with_module
+ CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore|
+ assert_equal(camel, Inflector.camelize(underscore))
+ end
+ end
+
+ def test_underscore_with_slashes
+ CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore|
+ assert_equal(underscore, Inflector.underscore(camel))
+ end
+ end
+
def test_demodulize
assert_equal "Account", Inflector.demodulize("MyApplication::Billing::Account")
end
@@ -131,4 +156,10 @@ class InflectorTest < Test::Unit::TestCase
assert_equal(human, Inflector.humanize(underscore))
end
end
+
+ def test_constantize
+ assert_equal A::B::C, Inflector.constantize("A::B::C")
+ assert_equal InflectorTest, Inflector.constantize("InflectorTest")
+ assert_raises(NameError) { Inflector.constantize("UnknownClass") }
+ end
end \ No newline at end of file
diff --git a/activesupport/test/loading_module/admin/access_controller.rb b/activesupport/test/loading_module/admin/access_controller.rb
new file mode 100644
index 0000000000..ddcbda8132
--- /dev/null
+++ b/activesupport/test/loading_module/admin/access_controller.rb
@@ -0,0 +1,2 @@
+class Admin::AccessController
+end
diff --git a/activesupport/test/loading_module/admin/user_controller.rb b/activesupport/test/loading_module/admin/user_controller.rb
new file mode 100644
index 0000000000..f265f1597a
--- /dev/null
+++ b/activesupport/test/loading_module/admin/user_controller.rb
@@ -0,0 +1,2 @@
+class Admin::UserController
+end
diff --git a/activesupport/test/loading_module/content_controller.rb b/activesupport/test/loading_module/content_controller.rb
new file mode 100644
index 0000000000..f0870161e5
--- /dev/null
+++ b/activesupport/test/loading_module/content_controller.rb
@@ -0,0 +1,2 @@
+class ContentController
+end
diff --git a/activesupport/test/loading_module/resource_controller.rb b/activesupport/test/loading_module/resource_controller.rb
new file mode 100644
index 0000000000..d948f366bf
--- /dev/null
+++ b/activesupport/test/loading_module/resource_controller.rb
@@ -0,0 +1,2 @@
+class ResourceController
+end
diff --git a/activesupport/test/loading_module_tests.rb b/activesupport/test/loading_module_tests.rb
new file mode 100644
index 0000000000..c1d8c7de62
--- /dev/null
+++ b/activesupport/test/loading_module_tests.rb
@@ -0,0 +1,63 @@
+require 'test/unit'
+require '../lib/core_ext.rb'
+require '../lib/dependencies.rb'
+
+STAGING_DIRECTORY = 'loading_module'
+
+class LoadingModuleTests < Test::Unit::TestCase
+ def setup
+ @loading_module = Dependencies::LoadingModule.new(STAGING_DIRECTORY)
+ Object.const_set(:Controllers, @loading_module)
+ end
+ def teardown
+ @loading_module.clear
+ Object.send :remove_const, :Controllers
+ end
+
+ def test_setup
+ assert_kind_of Dependencies::LoadingModule, @loading_module
+ end
+
+ def test_const_available
+ assert @loading_module.const_available?(:Admin)
+ assert @loading_module.const_available?(:ResourceController)
+ assert @loading_module.const_available?(:ContentController)
+ assert @loading_module.const_available?("ContentController")
+
+ assert_equal false, @loading_module.const_available?(:AdminController)
+ assert_equal false, @loading_module.const_available?(:RandomName)
+ end
+
+ def test_const_load_module
+ assert @loading_module.const_load!(:Admin)
+ assert_kind_of Module, @loading_module::Admin
+ assert_kind_of Dependencies::LoadingModule, @loading_module::Admin
+ end
+
+ def test_const_load_controller
+ assert @loading_module.const_load!(:ContentController)
+ assert_kind_of Class, @loading_module::ContentController
+ end
+
+ def test_const_load_nested_controller
+ assert @loading_module.const_load!(:Admin)
+ assert @loading_module::Admin.const_available?(:UserController)
+ assert @loading_module::Admin.const_load!(:UserController)
+ assert_kind_of Class, @loading_module::Admin::UserController
+ end
+
+ def test_pretty_access
+ assert_kind_of Module, @loading_module::Admin
+ assert_kind_of Dependencies::LoadingModule, @loading_module::Admin
+
+ assert_kind_of Class, @loading_module::Admin::UserController
+ assert_kind_of Class, @loading_module::Admin::AccessController
+ assert_kind_of Class, @loading_module::ResourceController
+ assert_kind_of Class, @loading_module::ContentController
+ end
+
+ def test_missing_name
+ assert_raises(NameError) {@loading_module::PersonController}
+ assert_raises(NameError) {@loading_module::Admin::FishController}
+ end
+end \ No newline at end of file