1

我有一个包含一些私有属性的类。我想做的是为这些动态添加一些设置器,仅用于执行特定块。

我希望能够做到的示例:

class Content
  attr_reader :a, :b

  def initialize
    @a = 1
    @b = "plop"
  end

  def set(&block)
    extend(Setter)
    instance_eval(&block)
    unextend(Setter) ????
  end

  module Setter
    def a(value)
      @a = value
    end
    def b(value)
      @b = value
    end
  end
end

content = Content.new
content.set do
  a 2
  b "yeah!"
end
content.a # should return 2

编辑:感谢迄今为止的精彩回答。我澄清了这个问题,因为我实际上需要在类本身中定义可能与模块中定义的设置器冲突的属性读取器。我在发布问题时忘记了这部分。(来晚了^^)

澄清:这个类是供 DSL 编写配置文件的。它针对的是非开发人员,因此运营商越少越好。

我目前使用阻止的代理类来实现这一点,但为了设置值instance_eval我必须搞砸,我不喜欢它。instance_variable_set我只是在尝试另一种方法,看看是否可以使我的代码更具可读性。

4

4 回答 4

3

在 Ruby 中没有“非扩展”模块的本地方法。mixology gem 将此模式实现为 C(和 Java,对于 JRuby)扩展、创建mixinunmix方法。但是,如果您需要 Ruby 1.9 支持,您可能需要应用补丁。

如果您希望避免使用第三方库,另一种方法可能只是将设置器设为私有:

class Content
  def initialize
    @a = 1
    @b = "plop"
  end

  def set(&block)
    instance_eval(&block)
  end

  private

  def a(val)
    @a = val
  end

  def b(val)
    @b = val
  end 

end

content = Content.new

#This will succeed
content.set do
  a 2
  b "yeah!"
end

# This will raise a NoMethodError, as it attempts to call a private method
content.a 3
于 2009-06-10T00:42:13.747 回答
2
  def set(&block)
    extend(Setter)
    instance_eval(&block)
    Setter.instance_methods.each do |m| 
      instance_eval "undef #{m}"
    end
  end

我不知道有什么方法可以为你做到这一点,尽管可能会有一些东西。虽然这应该可以完成这项工作,方法是找到 Setter 的所有实例方法并在 Content.xml 中取消定义它们。

于 2009-06-09T21:53:43.730 回答
2

你可以使用_why 的 mixico 库在 github 上可用)

它会让你这样做:

require 'mixology'
#...
def set(&block)
  Setter.mix_eval(Setter, &block)
end

mixology gem的作用大致相同,只是略有不同。

于 2009-06-10T02:00:30.987 回答
0

如果您感觉处于实验状态,还请查看:dup_eval

它在某些方面与 mixico 相似,但有一些有趣的附加功能(object2module

于 2009-07-23T08:26:43.183 回答