调用模块子类而不指定名称

时间:2012-09-19 22:23:13

标签: ruby-on-rails ruby module

我想仅使用模块的名称来访问我的子类。

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

我想写MyModule.x而不是MyModule::UselessName.x

我可以在课堂上转换我的模块,但是我使用RoR Helpers,我希望MyModule仍然是一个模块,而不是一个类。

有办法做到这一点吗? 谢谢;)

2 个答案:

答案 0 :(得分:1)

好的,我发现了一种非常糟糕的方式来实现我的意思:

module MyModule
  class UselessName
    include OtherModule

    # have whatever you want here
  end

  def self.x
    # whatever
  end
end

所以在你的代码中你可以做到的,我再说一遍,这非常非常糟糕!

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

我不知道如果它在之前的类中覆盖了一个具有相同名称的实例方法,或者如果它抛出异常,那么你必须尝试。

在我看来,你应该过度思考你的应用程序设计,因为这不是应该的样子,但是你去了......

答案 1 :(得分:1)

好的,让我们将问题分成两部分 - 获取此类方法的列表并在模块中创建代理。

获取列表可能有点棘手:

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

这里我们从所有公共类方法的列表开始,并从中减去所有超类的公共类方法的列表。

现在,假设我们知道方法的名称,我们需要制作代理方法。

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

此代码将在运行时等效于以下定义。

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

所以让我们把它放在简单的功能中。

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

所以现在你只需要为所需的模块和类调用它。请注意,“destination”模块可以与拥有该类的“source”模块不同,因此您可以将所有方法(假设它们具有不同的名称或者您将使用类名称作为前缀)添加到一个模块中。例如。对于你的情况,只需拨打以下电话。

make_proxies(MyModule, MyModule::UselessName)