Confusing behaviour of const_get in Ruby?

后端 未结 3 768
春和景丽
春和景丽 2021-02-01 05:59

According to the documentation mod.const_get(sym) \"Returns the value of the named constant in mod.\"

I also know that const_get by default may

相关标签:
3条回答
  • 2021-02-01 06:24

    I came up with the following script to load name spaced constants:

    def load_constant(name)
      parts = name.split('::')
      klass = Module.const_get(parts.shift)
      klass = klass.const_get(parts.shift) until parts.empty?
      klass
    end
    
    0 讨论(0)
  • 2021-02-01 06:25

    You are correct to be confused... The doc didn't state that Ruby makes a special case for lookup of constants in Modules and has been modified to state this explicitly. If the constant has not been found in the normal hierarchy, Ruby restarts the lookup from Object, as can be found in the source.

    Constant lookup by itself can be bit confusing. Take the following example:

    module M
      Foo = :bar
      module N
        # Accessing Foo here is fine:
        p Foo # => bar
      end
    end
    module M::N
      # Accessing Foo here isn't
      p Foo  # => uninitialized constant M::N::Foo
    end
    p M::N.const_get :Foo  # => uninitialized constant M::N::Foo
    

    In both places, though, accessing Object level constants like Array is fine (thank god!). What's going on is that Ruby maintains a list of "opened Module definitions". If a constant has an explicit scope, say LookHereOnly::Foo, then only LookHereOnly and its included modules will be searched. If no scope is specified (like Foo in the example above), Ruby will look through the opened module definitions to find the constant Foo: M::N, then M and finally Object. The topmost opened module definition is always Object.

    So M::N.const_get :Foo is equivalent to accessing Foo when the opened classes are only M::N and Object, like in the last part of my example.

    I hope I got this right, coz I'm still confused by constant lookups myself :-)

    0 讨论(0)
  • 2021-02-01 06:30

    As long as we are not checking for errors you can:

    def load_constant(name)
        name.split('::').inject(Module) do |mod_path, mod_to_find|
          mod_path.const_get(mod_to_find)
        end
    end
    
    0 讨论(0)
提交回复
热议问题