Можете ли вы добавить метод класса к классу базового модуля в Ruby? - PullRequest
2 голосов
/ 28 октября 2010

Можно ли добавить базовый метод в класс Module в 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

Если я создаю модуль, я не могу получить доступ к этому методу:

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

Есть ли способы сделать это?

Обновление

Это работает! Спасибо @ Йорг 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

Как в этом примере:

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

Ура!

Ответы [ 2 ]

6 голосов
/ 28 октября 2010

Если вы хотите добавить метод в класс Module, вы просто добавляете метод в класс Module:

class Module
  def base
    # ...
  end
end

В вашем коде вы использовали

def self.base
  # ...
end

Этот синтаксис (def foo.bar) является синтаксисом для добавления одноэлементного метода с именем bar к объекту, на который ссылается foo, который на самом деле просто добавляет метод обычного экземпляра к классу синглтона объекта, на который ссылаетсяby foo.

Итак, в вашем коде вы добавляли одноэлементный метод с именем base к объекту, на который ссылается self, который внутри class_eval является самим объектом класса, в данном случаеModule.Причина, по которой одноэлементные методы называются одноэлементными методами, заключается в том, что они существуют только в одном объекте.В вашем случае метод base существует только для объекта Module, но не для любого другого объекта и, в частности, не для объекта User.Другими словами: only способ вызова метода base - это Module.base, потому что это единственный объект, к которому вы добавили его.

Мне потребовалось довольно много временипробраться через ваши три (!) вложенных class_eval s, чтобы узнать, чего вы пытаетесь достичь.Что не облегчается тем фактом, что ваш код даже не работает, даже , если определен в правильном классе, потому что def создает новую область видимости, и поэтому block не определен всамое внутреннее class_eval.

Очевидно, что вы пытаетесь сделать это:

class Module
  def base(&block)
    define_singleton_method(:included) do |base|
      base.class_eval(&block)
    end
  end
end
1 голос
/ 28 октября 2010

Я полагаю, вы добавите его, определив метод в модуле модуля:

class Module
    def new_method
       ...
    end
end

Будьте очень осторожны с этим, так как это влияет на весь код, который использует Модуль.

...