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

3

我正在尝试使用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

我在这里要做的是使用IdAndNameRedefine模块动态插入一个方法来覆盖Model类中的id_and_name类方法。当包含该模块时,它会创建一个“静态”方法(实际上是Model.class的类方法,据我所知),并且当调用use_id_and_name_from_module时,它会在Model中创建一个类方法,该方法重新定义了id_and_name以使用Model请求的任何属性。
我的问题是... 有没有更好的方法来完成这个任务,或者这是正确的方式?我不确定是否喜欢class_eval的字符串格式,需要转义值等才能使其正常工作。
1个回答

5

您不必传递字符串给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

但在这种特定情况下,我们只是告诉self去执行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

(我只是想展示一下class_eval的真正工作原理,因为你似乎对这部分最感兴趣。)


谢谢!我觉得它看起来有点愚蠢,但我想不出更好的方法。我确实尝试过使用define_method,但那时我在类定义的顶部调用了use_id_and_name_from_module,因此它没有起作用(我猜是被后面的声明覆盖了)。只是出于好奇,有没有办法将use_id_and_name_from_module放在类的顶部,并确保它不能在后面的声明中被覆盖? - Thorbjørn Hermansen
你可以重写 method_added 方法,并在有人试图替换它时重新添加你的版本。虽然这似乎有点复杂,但最好还是不要在第一次定义方法时重新定义它。 - Chuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接