包含一个模块来定义动态类方法

时间:2023-01-15 18:42:24

I'm playing around with Ruby and I have written the following code:

我正在玩Ruby,我编写了以下代码:

module IdAndNameRedefine
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def use_id_and_name_from_module(use_attribute)
      class_eval <<CODE
        def id_and_name
          "\#{id}-\#{#{use_attribute}.downcase}"
        end
CODE
    end
  end
end


class Model
  include IdAndNameRedefine

  attr_reader :id, :name1, :name2

  def initialize(id, name1, name2)
    @id = id
    @name1 = name1
    @name2 = name2
  end

  def id_and_name
    "#{id}-#{name1}"
  end

  use_id_and_name_from_module :name2
end

model = Model.new(1, "TesT", "Test number TWO")
puts model.id_and_name

When I'm trying to do here is to override the class method id_and_name in class Model with a method dynamicly inserted by the IdAndNameRedefine-module. When that module is included, it creates a "static" method (really, a class method of the Model.class as I understands it) and when use_id_and_name_from_module is called, it creates a class method in Model which redefines the id_and_name to use whatever attribute of Model asked for.

当我在这里尝试时,使用由IdAndNameRedefine模块动态插入的方法覆盖类Model中的类方法id_and_name。当包含该模块时,它会创建一个“静态”方法(实际上,我理解它是Model.class的类方法),当调用use_id_and_name_from_module时,它会在Model中创建一个类方法,重新定义id_and_name以使用任何属性模特要求。

My question is.. Are there any better way of doing this, or is this the "correct" way of doing it? I'm not really sure if I like the class_eval with takes a string where I need to escape values etc. to make this work.

我的问题是..有没有更好的方法来做到这一点,或者这是“正确”的做法?我不确定我是否喜欢class_eval带有一个字符串,我需要转义值等来使这个工作。

1 个解决方案

#1


You don't have to pass a string to class_eval. It can take a block instead. In fact, I can't think of a single occasion where I've had to pass a string to class_eval. So we can rewrite the ClassMethods module like so:

您不必将字符串传递给class_eval。它可能需要一个块。事实上,我想不出一个我必须将字符串传递给class_eval的场合。所以我们可以像这样重写ClassMethods模块:

module ClassMethods
  def use_id_and_name_from_module(use_attribute)
    class_eval do 
      define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"}
    end
  end
end

But in this particular case, we're just telling self to class_eval, meaning we're already in that class's context. So it can actually be shortened to:

但在这种特殊情况下,我们只是告诉自己class_eval,这意味着我们已经在该类的上下文中。所以它实际上可以缩短为:

module ClassMethods
  def use_id_and_name_from_module(use_attribute)
    define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"}
  end
end

(I just wanted to show how class_eval really works, since you seemed most interested in that part.)

(我只是想展示class_eval是如何工作的,因为你似乎对那部分最感兴趣。)

#1


You don't have to pass a string to class_eval. It can take a block instead. In fact, I can't think of a single occasion where I've had to pass a string to class_eval. So we can rewrite the ClassMethods module like so:

您不必将字符串传递给class_eval。它可能需要一个块。事实上,我想不出一个我必须将字符串传递给class_eval的场合。所以我们可以像这样重写ClassMethods模块:

module ClassMethods
  def use_id_and_name_from_module(use_attribute)
    class_eval do 
      define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"}
    end
  end
end

But in this particular case, we're just telling self to class_eval, meaning we're already in that class's context. So it can actually be shortened to:

但在这种特殊情况下,我们只是告诉自己class_eval,这意味着我们已经在该类的上下文中。所以它实际上可以缩短为:

module ClassMethods
  def use_id_and_name_from_module(use_attribute)
    define_method(:id_and_name) {"#{id}-#{send(use_attribute).downcase}"}
  end
end

(I just wanted to show how class_eval really works, since you seemed most interested in that part.)

(我只是想展示class_eval是如何工作的,因为你似乎对那部分最感兴趣。)