2 Stimmen

Ruby String#to_class

Entnommen aus einer vorherige Beitrag mit einigen Änderungen als Antwort auf sepp2k 's Kommentar über Namespaces, ich habe String#to_class Methode implementiert. Ich teile den Code hier und ich glaube, dass es irgendwie refactored werden könnte, besonders der "i" Zähler. Ihre Kommentare sind erwünscht.

 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

6voto

Michael Sepcot Punkte 10787

Ich würde einen Blick auf ActiveSupport::CoreExtensions::String::Inflections Insbesondere ist es constantize Methode:

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

4voto

Erwan Barrier Punkte 76

Ich habe aus Neugierde einige Benchmarks durchgeführt und meine Lösung ist sehr langsam! hier ist eine überarbeitete Lösung mit Benchmarks, hoffe das hilft.

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)

2voto

Erwan Barrier Punkte 76

Sie können Rekursion verwenden:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X