Taken from a previous post with some modifications to respond to sepp2k\'s comment about namespaces, I have implemented String#to_class method. I\'m sharing the code here an
You can use recursion:
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
I ran some benchmarks by curiosity and my solution is very slow! here is a refactored solution with benchmarks, hope that helps.
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)
I would take a look at ActiveSupport::CoreExtensions::String::Inflections
specifically it's constantize
method:
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