Ruby, iOS, and Other Development

A place to share useful code snippets, ideas, and techniques

All code in posted articles shall be considered public domain unless otherwise noted.
Comments remain the property of their authors.

2006-05-19

Kernel#qualified_const_get

The question of how to get a class by name comes up with some regularity on the ruby-talk and rails mailing lists. The first response is usually to use Object::const_get. The response to that is that it doesn't handle classes within namespaces, e.g. Foo::Bar. I might argue that Object::const_get("Foo::Bar") should do the right thing and retrieve the value of Bar from the Foo class/module, but the fact of the matter is that it does not. Having an itch to scratch, I wrote Kernel#qualified_const_get:

module Kernel
  def qualified_const_get(str)
    path = str.to_s.split('::')
    from_root = path[0].empty?
    if from_root
      from_root = []
      path = path[1..-1]
    else
      start_ns = ((Class === self)||(Module === self)) ? self : self.class
      from_root = start_ns.to_s.split('::')
    end
    until from_root.empty?
      begin
        return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
      rescue NameError
        from_root.delete_at(-1)
      end
    end
    path.inject(Object) { |ns,name| ns.const_get(name) }
  end
end

One of the advantages of this usage is that it handles partially qualified constant names. The following does the right thing in both cases:

require 'qualified_const_get'

module Foo
  module Bar
    class Baz
      def initialize
        puts 'You found me!'
      end
    end
  end
  def self.find_it
    klass = qualified_const_get("Bar::Baz")
    klass.new
  end
  module Quux
    def self.find_it
      klass = qualified_const_get("Bar::Baz")
      klass.new
    end
  end
end

Foo::find_it
Foo::Quux::find_it
Enjoy!

Labels: , ,

2 Comments:

  • At 6/30/2006 03:16:00 PM, Blogger Gregory said…

    Of those, only the facets link has comparable functionality, and even that isn't quite as good because it will not attempt to start from the root namespace if it is not found in the relative namespace, though it will respect an initial ::. Also, you got the link slightly wrong. It should be:

    http://facets.rubyforge.org/doc/api/core/classes/Kernel.html#M000405

     
  • At 12/02/2011 09:34:00 AM, Blogger Lukáš Zapletal said…

    Your code contains a bug, try it with two classes with the same name (but different module). Returns the top level one.

     

Post a Comment

<< Home