1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

refactor Reference to a ClassCache object, fix lazy lookup in Middleware so that anonymous classes are supported

This commit is contained in:
Aaron Patterson 2011-03-01 17:20:10 -08:00
parent 50ed1a25a4
commit 7b6bfe84f3
7 changed files with 187 additions and 21 deletions

View file

@ -7,12 +7,19 @@ module ActionDispatch
attr_reader :args, :block
def initialize(klass_or_name, *args, &block)
@ref = ActiveSupport::Dependencies::Reference.new(klass_or_name)
@klass = nil
@name = klass_or_name
if klass_or_name.respond_to?(:name)
@klass = klass_or_name
@name = @klass.name
end
@args, @block = args, block
end
def klass
@ref.get
@klass ||= ActiveSupport::Inflector.constantize(@name)
end
def ==(middleware)
@ -22,7 +29,7 @@ module ActionDispatch
when Class
klass == middleware
else
normalize(@ref.name) == normalize(middleware)
normalize(@name) == normalize(middleware)
end
end

View file

@ -50,12 +50,13 @@ module ActionDispatch
private
def controller_reference(controller_param)
controller_name = "#{controller_param.camelize}Controller"
unless controller = @controllers[controller_param]
controller_name = "#{controller_param.camelize}Controller"
controller = @controllers[controller_param] =
ActiveSupport::Dependencies.ref(controller_name)
end
controller.get
controller.get(controller_name)
end
def dispatch(controller, action, env)

View file

@ -0,0 +1,50 @@
require 'abstract_unit'
require 'action_dispatch/middleware/stack'
module ActionDispatch
class MiddlewareStack
class MiddlewareTest < ActiveSupport::TestCase
class Omg; end
{
'concrete' => Omg,
'anonymous' => Class.new
}.each do |name, klass|
define_method("test_#{name}_klass") do
mw = Middleware.new klass
assert_equal klass, mw.klass
end
define_method("test_#{name}_==") do
mw1 = Middleware.new klass
mw2 = Middleware.new klass
assert_equal mw1, mw2
end
end
def test_string_class
mw = Middleware.new Omg.name
assert_equal Omg, mw.klass
end
def test_double_equal_works_with_classes
k = Class.new
mw = Middleware.new k
assert_operator mw, :==, k
assert_operator mw, :!=, Class.new
end
def test_double_equal_works_with_strings
mw = Middleware.new Omg
assert_operator mw, :==, Omg.name
end
def test_double_equal_normalizes_strings
mw = Middleware.new Omg
assert_operator mw, :==, "::#{Omg.name}"
end
end
end
end

View file

@ -1,5 +1,11 @@
*Rails 3.1.0 (unreleased)*
* ActiveSupport::Dependencies::ClassCache class has been introduced for
holding references to reloadable classes.
* ActiveSupport::Dependencies::Reference has been refactored to take direct
advantage of the new ClassCache.
* Backports Range#cover? as an alias for Range#include? in Ruby 1.8 [Diego Carrion, fxn]
* Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn]

View file

@ -524,31 +524,52 @@ module ActiveSupport #:nodoc:
explicitly_unloadable_constants.each { |const| remove_constant const }
end
class Reference
@@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
attr_reader :name
def initialize(name)
@name = name.to_s
@@constants[@name] = name if name.respond_to?(:name)
class ClassCache
def initialize
@store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
end
def get
@@constants[@name]
def empty?
@store.empty?
end
def self.clear!
@@constants.clear
def key?(key)
@store.key?(key)
end
def []=(key, value)
return unless key.respond_to?(:name)
raise(ArgumentError, 'anonymous classes cannot be cached') unless key.name
@store[key.name] = value
end
def [](key)
key = key.name if key.respond_to?(:name)
@store[key]
end
alias :get :[]
def new(name)
self[name] = name
self
end
def clear!
@store.clear
end
end
Reference = ClassCache.new
def ref(name)
references[name] ||= Reference.new(name)
end
def constantize(name)
ref(name).get
ref(name).get(name)
end
# Determine if the given constant has been automatically loaded.

View file

@ -0,0 +1,81 @@
require 'abstract_unit'
require 'active_support/dependencies'
module ActiveSupport
module Dependencies
class ClassCacheTest < ActiveSupport::TestCase
def setup
@cache = ClassCache.new
end
def test_empty?
assert @cache.empty?
@cache[ClassCacheTest] = ClassCacheTest
assert !@cache.empty?
end
def test_clear!
assert @cache.empty?
@cache[ClassCacheTest] = ClassCacheTest
assert !@cache.empty?
@cache.clear!
assert @cache.empty?
end
def test_set_key
@cache[ClassCacheTest] = ClassCacheTest
assert @cache.key?(ClassCacheTest.name)
end
def test_set_rejects_strings
@cache[ClassCacheTest.name] = ClassCacheTest
assert @cache.empty?
end
def test_get_with_class
@cache[ClassCacheTest] = ClassCacheTest
assert_equal ClassCacheTest, @cache[ClassCacheTest]
end
def test_get_with_name
@cache[ClassCacheTest] = ClassCacheTest
assert_equal ClassCacheTest, @cache[ClassCacheTest.name]
end
def test_get_constantizes
assert @cache.empty?
assert_equal ClassCacheTest, @cache[ClassCacheTest.name]
end
def test_get_is_an_alias
assert_equal @cache[ClassCacheTest], @cache.get(ClassCacheTest.name)
end
def test_new
@cache.new ClassCacheTest
assert @cache.key?(ClassCacheTest.name)
end
def test_new_rejects_strings
@cache.new ClassCacheTest.name
assert !@cache.key?(ClassCacheTest.name)
end
def test_new_returns_self
v = @cache.new ClassCacheTest.name
assert_equal @cache, v
end
def test_anonymous_class_fail
assert_raises(ArgumentError) do
@cache.new Class.new
end
assert_raises(ArgumentError) do
x = Class.new
@cache[x] = x
end
end
end
end
end

View file

@ -479,13 +479,13 @@ class DependenciesTest < Test::Unit::TestCase
with_loading 'dependencies' do
c = ActiveSupport::Dependencies.ref("ServiceOne")
service_one_first = ServiceOne
assert_equal service_one_first, c.get
assert_equal service_one_first, c.get("ServiceOne")
ActiveSupport::Dependencies.clear
assert ! defined?(ServiceOne)
service_one_second = ServiceOne
assert_not_equal service_one_first, c.get
assert_equal service_one_second, c.get
assert_not_equal service_one_first, c.get("ServiceOne")
assert_equal service_one_second, c.get("ServiceOne")
end
end