0

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

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 仍然是一个模块而不是一个类。

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

4

2 回答 2

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

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

在我看来,你应该过度考虑你的应用程序设计,因为这不是它应该的方式,但是你去......

于 2012-09-19T23:06:15.890 回答
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

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

make_proxies(MyModule, MyModule::UselessName)
于 2012-09-19T23:26:25.823 回答