Ruby String#to_class

后端 未结 3 1641
谎友^
谎友^ 2020-12-21 15:45

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

相关标签:
3条回答
  • 2020-12-21 16:24

    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
    
    0 讨论(0)
  • 2020-12-21 16:39

    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)
    
    0 讨论(0)
  • 2020-12-21 16:48

    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
    
    0 讨论(0)
提交回复
热议问题