2009-09-19 7 views
2

Extrait d'un post previous avec quelques modifications pour répondre au commentaire de sepp2k sur les espaces de noms, j'ai implémenté la méthode String # to_class. Je partage le code ici et je crois qu'il pourrait être refacturé d'une manière ou d'une autre en particulier le compteur "i". Vos commentaires sont appréciés.Ruby String # to_class

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 

Répondre

4

j'ai couru quelques repères par curiosité et ma solution est très lent ! voici une solution refactorisée avec des repères, espoir qui aide.

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) 
+0

Bon travail! J'utiliserais plutôt Benchmark.bmbm pour me réchauffer. Merci! – khelll

6

Je prendrais un coup d'œil à ActiveSupport::CoreExtensions::String::Inflections spécifiquement c'est constantize méthode:

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 
+0

J'ai ajouté quelques tests pour donner de meilleures spécifications. Je ne cherche pas des choses constantes, mais de bonnes prises. – khelll

2

Vous pouvez utiliser la récursivité:

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 
+0

+1 pour la programmation fonctionnelle – khelll