1

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
Nathan Hanna
  • 4,643
  • 3
  • 28
  • 32
  • Could you create a class that extends the module-ized class, or create a constant that is set to the module-ized class? It seems a little wonky and might break in other spectacular ways, I suppose. – Dave Newton Jun 18 '15 at 20:35

2 Answers2

3

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
Andrew Kozin
  • 1,012
  • 9
  • 17
  • 2
    Why `dup` it when you remove the reference to the original and thus make it eligible for garbage collection anyway? In fact, even if you didn't remove the reference, I don't see why would want to `dup` the class. – Jörg W Mittag Jun 18 '15 at 22:38
0

I ended up using the solution suggested by @Andrew with one slight change:

::Posts = TableEngine::Posts.dup

Adding the '::' prevented the Posts class from being namespaced by the context within which it was created, ex:

class PostsController < ApplicationController
    Posts = TableEngine::Posts.dup # => PostsController::Posts
    ::Posts = TableEngine::Posts.dup # => Posts
end
Nathan Hanna
  • 4,643
  • 3
  • 28
  • 32