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

Lan*_*ard 2 ruby metaprogramming

是否可以在Ruby中向Module类添加核心方法?我想做这样的事情(昨天弄乱了这个想法,只是在一瞬间鞭打了这个):

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
Run Code Online (Sandbox Code Playgroud)

如果我创建一个模块,我无法访问该方法:

$ module User; end
$ User.base
NoMethodError: undefined method `base' for User:Module
Run Code Online (Sandbox Code Playgroud)

有什么办法吗?

更新

这有效!谢谢@JörgWMittag.使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
Run Code Online (Sandbox Code Playgroud)

像这个例子:

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
Run Code Online (Sandbox Code Playgroud)

干杯!

Jör*_*tag 6

如果要向Module该类添加方法,只需向Module该类添加一个方法:

class Module
  def base
    # ...
  end
end
Run Code Online (Sandbox Code Playgroud)

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

def self.base
  # ...
end
Run Code Online (Sandbox Code Playgroud)

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

因此,在您的代码中,您添加了一个以base引用的对象命名的单例方法,在这种情况下self,其内部class_eval是类对象本身Module.单例方法被称为单例方法的原因是因为它们只存在于单个对象上.在您的情况下,该方法base仅存在于对象上Module,而不存在于任何其他对象上,特别是不存在于对象上User.换句话说:调用方法的唯一base方法是Module.base,因为这是您添加它的唯一对象.

我花了很长时间来浏览你的三个(!)嵌套class_evals,以找出你想要实现的目标.这是不是事实,你的代码甚至不实际的工作变得更加容易,甚至如果在正确的类中定义,因为def创建一个新的范围,所以block在最里面的不确定class_eval.

显然,你要做的是:

class Module
  def base(&block)
    define_singleton_method(:included) do |base|
      base.class_eval(&block)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)