Ruby String#to_class

从上一篇文章中做了一些修改以回应sepp2k关于命名空间的评论,我已经实现了String#to_class方法。 我在这里分享代码,我相信它可能会被重构,特别是“i”计数器。 您的意见表示赞赏。

class String def to_class chain = self.split "::" i=0 res = chain.inject(Module) do |ans,obj| break if ans.nil? i+=1 klass = ans.const_get(obj) # Make sure the current obj is a valid class # Or it's a module but not the last element, # as the last element should be a class klass.is_a?(Class) || (klass.is_a?(Module) and i != chain.length) ? klass : nil end rescue NameError nil end end #Tests that should be passed. assert_equal(Fixnum,"Fixnum".to_class) assert_equal(M::C,"M::C".to_class) assert_nil "Math".to_class assert_nil "Math::PI".to_class assert_nil "Something".to_class 

我好奇地运行了一些基准测试,我的解决方案很慢! 这是一个带有基准的重构解决方案,希望有所帮助。

 require "benchmark" class String def to_class_recursive chain = self.split "::" klass = parent.const_get chain.shift return chain.size < 1 ? (klass.is_a?(Class) ? klass : nil) : chain.join("::").to_class(klass) rescue nil end def to_class_original chain = self.split "::" i=0 res = chain.inject(Module) do |ans,obj| break if ans.nil? i+=1 klass = ans.const_get(obj) # Make sure the current obj is a valid class # Or it's a module but not the last element, # as the last element should be a class klass.is_a?(Class) || (klass.is_a?(Module) and i != chain.length) ? klass : nil end rescue NameError nil end def to_class_refactored chain = self.split "::" klass = Kernel chain.each do |klass_string| klass = klass.const_get klass_string end klass.is_a?(Class) ? klass : nil rescue NameError nil end end module M class C end end n = 100000 class_string = "M::C" Benchmark.bm(20) do |x| x.report("to_class_recursive") { n.times { class_string.to_class_recursive } } x.report("to_class_original") { n.times { class_string.to_class_original } } x.report("to_class_refactored") { n.times { class_string.to_class_refactored } } end # user system total real # to_class_recursive 2.430000 0.170000 2.600000 ( 2.701991) # to_class_original 1.000000 0.010000 1.010000 ( 1.049478) # to_class_refactored 0.570000 0.000000 0.570000 ( 0.587346) 

我会看一下ActiveSupport::CoreExtensions::String::Inflections特别是它的constantize方法:

 def constantize(camel_cased_word) names = camel_cased_word.split('::') names.shift if names.empty? || names.first.empty? constant = Object names.each do |name| constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name) end constant end 

你可以使用递归:

 class String def to_class(parent = Kernel) chain = self.split "::" klass = parent.const_get chain.shift return chain.size < 1 ? (klass.is_a?(Class) ? klass : nil) : chain.join("::").to_class(klass) rescue nil end end