【问题标题】:Ruby String#to_class红宝石字符串#to_class
【发布时间】:2010-11-29 17:43:45
【问题描述】:

取自 previous 的帖子,经过一些修改以回应 sepp2k 关于命名空间的评论,我已经实现了 String#to_class 方法。我在这里分享代码,我相信它可以以某种方式特别是“i”计数器进行重构。感谢您的 cmets。

 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

【问题讨论】:

    标签: ruby metaprogramming introspection


    【解决方案1】:

    我出于好奇运行了一些基准测试,但我的解决方案非常慢! 这是一个带有基准的重构解决方案,希望对您有所帮助。

    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)
    

    【讨论】:

    • 干得好!我会使用 Benchmark.bmbm 来热身。谢谢!
    【解决方案2】:

    你可以使用递归:

    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
    

    【讨论】:

      【解决方案3】:

      我会看看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
      

      【讨论】:

      • 我添加了一些测试来提供更好的规格。我不是在寻求固定化的东西,而是很好的捕捉。
      猜你喜欢
      • 1970-01-01
      • 2014-02-11
      • 2011-01-21
      • 1970-01-01
      • 1970-01-01
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多