你能在Ruby的核心模块类中添加一个类方法吗?

时间:2023-01-15 20:20:13

Is it possible to add a core method to the Module class in Ruby? I want to do something like this (messed around with the idea yesterday, just whipped this together in a second):

是否有可能在Ruby中的模块类中添加一个核心方法?我想做这样的事情(昨天的想法搞得一团糟,一会儿就把它拼凑起来):

module MyModule
  base do
    has_many :something
  end
end

# implementation, doesn't work though... reason for the question
Module.class_eval do
  def self.base(&block)
    class_eval do
      def self.included(base)
        base.class_eval(&block)
      end
    end
  end
end

If I create a module, I can't access that method:

如果我创建一个模块,我不能访问这个方法:

$ module User; end
$ User.base
NoMethodError: undefined method `base' for User:Module

Any ways to do this?

有什么办法吗?

Update

更新

This works! Thanks @Jörg W Mittag. Makes it much easier to read for Rails has_many and such:

这个工作!由于@Jorg W Mittag。使Rails has_many等更容易阅读:

class Module
  def base(&block)
    metaclass = class << self; self; end
    # add the method using define_method instead of def x.say so we can use a closure
    metaclass.send :define_method, :included do |base|
      base.class_eval(&block) unless block.nil?
      base.class_eval <<-EOF
        extend  #{name}::ClassMethods
        include #{name}::InstanceMethods
      EOF
    end
    block
  end
end

Like this example:

像这样的例子:

module UserRoleBehavior
  base do
    has_many :user_roles
    has_many :roles, :through => :user_roles
  end

  module ClassMethods
    # ...
  end

  module InstanceMethods
    # ...
  end
end

class User < ActiveRecord::Base
  include UserRoleBehavior
end

Cheers!

干杯!

2 个解决方案

#1


6  

If you want to add a method to the Module class, you simply add a method to the Module class:

如果要向模块类添加方法,只需向模块类添加方法:

class Module
  def base
    # ...
  end
end

In your code, you were using

在您的代码中,您正在使用

def self.base
  # ...
end

This syntax (def foo.bar) is the syntax for adding a singleton method named bar to the object referenced by foo, which is actually really just adding a regular instance method to the singleton class of the object referenced by foo.

这个语法(def . foo.bar)是向foo引用的对象添加名为bar的单例方法的语法,它实际上只是向foo引用的对象的单例类添加一个常规实例方法。

So, in your code you were adding a singleton method named base to the object referenced by self, which inside of class_eval is the class object itself, in this case Module. The reason why singleton methods are called singleton methods, is because they only exist on a single object. In your case, the method base exists only on the object Module, not on any other object, and in particular not on the object User. In other words: the only way to call the base method is as Module.base, because that is the only object that you added it to.

因此,在代码中,您向self引用的对象添加了一个名为base的单例方法,在本例模块中,这个方法在class_eval内部就是类对象本身。单例方法之所以被称为单例方法,是因为它们只存在于一个对象上。在您的示例中,方法库仅存在于对象模块上,而不存在于任何其他对象上,特别是不存在于对象用户上。换句话说:调用基方法的唯一方法是作为模块。基,因为这是唯一添加的对象。

It took me quite a while to wade through your three(!) nested class_evals to find out what it is that you are trying to achieve. Which wasn't made easier by the fact that your code doesn't even actually work, even if defined on the correct class, because def creates a new scope, and so block is undefined in the innermost class_eval.

我花了很长时间来遍历您的三个(!)嵌套class_evals,以找出您要实现的目标。因为def创建了一个新范围,所以在最内部的class_eval中没有定义block,所以即使在正确的类上定义了代码,代码实际上也不能工作,这一点也不容易实现。

Apparently, what you are trying to do is this:

显然,你想做的是:

class Module
  def base(&block)
    define_singleton_method(:included) do |base|
      base.class_eval(&block)
    end
  end
end

#2


1  

I believe you add it by defining the method in the module module:

我相信你通过在模块模块中定义方法来添加它:

class Module
    def new_method
       ...
    end
end

Be very careful with this, as it effects all code that uses Module.

要非常小心,因为它影响了所有使用模块的代码。

#1


6  

If you want to add a method to the Module class, you simply add a method to the Module class:

如果要向模块类添加方法,只需向模块类添加方法:

class Module
  def base
    # ...
  end
end

In your code, you were using

在您的代码中,您正在使用

def self.base
  # ...
end

This syntax (def foo.bar) is the syntax for adding a singleton method named bar to the object referenced by foo, which is actually really just adding a regular instance method to the singleton class of the object referenced by foo.

这个语法(def . foo.bar)是向foo引用的对象添加名为bar的单例方法的语法,它实际上只是向foo引用的对象的单例类添加一个常规实例方法。

So, in your code you were adding a singleton method named base to the object referenced by self, which inside of class_eval is the class object itself, in this case Module. The reason why singleton methods are called singleton methods, is because they only exist on a single object. In your case, the method base exists only on the object Module, not on any other object, and in particular not on the object User. In other words: the only way to call the base method is as Module.base, because that is the only object that you added it to.

因此,在代码中,您向self引用的对象添加了一个名为base的单例方法,在本例模块中,这个方法在class_eval内部就是类对象本身。单例方法之所以被称为单例方法,是因为它们只存在于一个对象上。在您的示例中,方法库仅存在于对象模块上,而不存在于任何其他对象上,特别是不存在于对象用户上。换句话说:调用基方法的唯一方法是作为模块。基,因为这是唯一添加的对象。

It took me quite a while to wade through your three(!) nested class_evals to find out what it is that you are trying to achieve. Which wasn't made easier by the fact that your code doesn't even actually work, even if defined on the correct class, because def creates a new scope, and so block is undefined in the innermost class_eval.

我花了很长时间来遍历您的三个(!)嵌套class_evals,以找出您要实现的目标。因为def创建了一个新范围,所以在最内部的class_eval中没有定义block,所以即使在正确的类上定义了代码,代码实际上也不能工作,这一点也不容易实现。

Apparently, what you are trying to do is this:

显然,你想做的是:

class Module
  def base(&block)
    define_singleton_method(:included) do |base|
      base.class_eval(&block)
    end
  end
end

#2


1  

I believe you add it by defining the method in the module module:

我相信你通过在模块模块中定义方法来添加它:

class Module
    def new_method
       ...
    end
end

Be very careful with this, as it effects all code that uses Module.

要非常小心,因为它影响了所有使用模块的代码。