Search code examples
ruby-on-railsrubywash-out

How do I un-namespace a ruby constant?


I am using a library that doesn't handle namespaced models. I have a rails engine that has all the ActiveRecord models that I need to use and as such I reference them with the namespace of the engine, ex: TableEngine::Posts.

Is there a way in Ruby to un-namespace the Posts class?

something like

TableEngine::Posts.demodulize # => Posts

Solution

  • At first you can add the constant you need and assign the Class/Module to it (remember both are just objects). You should duplicate it to reset the name:

    Posts = TableEngine::Posts.dup
    

    Afterwards remove the source constant name:

    Object.send :remove_const, :TableEngine
    

    Then:

    Posts
    # => Posts
    TableEngine
    # => NameError: uninitialized constant TableEngine
    TableEngine::Posts
    # => NameError: uninitialized constant TableEngine
    

    UPDATE. Why dup is needed:

    Notice, the class SomeClass is a shortcut for creating an object of type Class and assigning it to some constant:

    SomeClass = Class.new
    

    When you create a class, its name value isn't set:

    klass = Class.new
    # => #<Class:0x00000001545a28>
    klass.name
    # => nil
    

    When you assign it to the constant for the first time, the name is assigned and memoized:

    Klass = klass
    # => Klass
    klass.name
    # => "Klass"
    

    When you later assign the object of type Class to another constant, it remains just the same object, referred by both constants:

    NewKlass = Klass
    # => Klass
    Klass.name
    # => "Klass"
    NewKlass.name
    # => "Klass"
    

    That's why even if the initial constant will be removed, the object will keep carrying the old name.

    Object.send :remove_const, :Klass
    # => Klass
    NewKlass
    # => Klass
    NewKlass.name
    # => "Klass"
    
    Klass
    # => NameError: uninitialized constant Klass
    

    The object by itself hasn't been changed. What has been changed is the list of constants, carried by the Object object.

    When you duplicate the object of type Class it is created without any name (it is the new object):

    new_klass = NewKlass.dup
    # => #<Class:0x000000016ced90>
    new_klass.name
    # => nil
    

    And when you assing it to the new constant, the name is set. That is how in the very first example above it receives the new name:

    Posts = TableEngine::Posts.dup
    # => Post