Call module subclass without specify the name

I would like to access my subclass using only the name of my module.

module MyModule
    class UselessName
        include OtherModel
        # only self method
        def self.x
        end
    end
    # No other class
end

And I would like to write MyModule.x and not MyModule::UselessName.x

I could transform my module in class, but I use RoR Helpers, and I would prefer that MyModule remains a module and not a class.

Is there a way to do this ? Thanks ;)

Upvotes: 0

Views: 250

Answers (2)

iced
iced

Reputation: 1572

OK, let's split problem into two - getting list of such methods and making proxies in the module.

Getting list might be a little tricky:

MyModule::UselessName.public_methods(false) - MyModule::UselessName.superclass.public_methods(false)

Here we start with list of all public class methods and subtract list of all superclass's public class methods from it.

Now, assuming we know method's name, we need to make proxy method.

metaclass = class << MyModule; self; end
metaclass.send(:define_method, :x) do |*args, &block|
  MyModule::UselessName.send(:x, *args, &block)
end

This code will just make equivalent of following definition at runtime.

module MyModule
  def x(*args, &block)
    MyModule::UselessName.send(:x, *args, &block)
  end
end

So let's put it together in simple function.

def make_proxies(mod, cls)
  methods = cls.public_methods(false) - cls.superclass.public_methods(false)
  metaclass = class << mod; self; end

  methods.each do |method|
    metaclass.send(:define_method, method) do |*args, &block|
      cls.send(method, *args, &block)
    end
  end
end

So now you'll just need to call it for needed modules and classes. Note that "destination" module can be different from "source" module owning the class, so you can slurp all methods (given they have different names or you'll prefix them using class name) to one module. E.g. for your case just make following call.

make_proxies(MyModule, MyModule::UselessName)

Upvotes: 1

Vapire
Vapire

Reputation: 4578

Ok, I've found a VERY DIRTY way to accomplish what I THINK you mean:

module MyModule
  class UselessName
    include OtherModule

    # have whatever you want here
  end

  def self.x
    # whatever
  end
end

So somewhere in your code you can do, and I repeat THIS IS VERY, VERY DIRTY!

MyModule.methods(false).each do |m|
  # m = method

  # now you can redefine it in your class
  # as an instance method. Not sure if this
  # is what you want though
  MyModule::UselessName.send(:define_method, :m) do
    # in this NEW (it's not the same) method you can
    # call the method from the module to get the same
    # behaviour
    MyModule.send(m)
  end
end

I don't know if this overwrites an instance method with the same name if it's in the class before or if it throws an exception, you have to try that.

In my opinion you should overthink your app design, because this is not the way it should be, but here you go...

Upvotes: 1

Related Questions