我有一个包含一些私有属性的类。我想要做的是为这些只为执行特定块动态添加一些setter。
我希望能够做到的例子:
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
编辑:感谢目前为止的精彩答案。我澄清了这个问题,因为我实际上需要在类本身中定义可能与模块中定义的setter冲突的属性读取器。在发布问题时我忘记了这一部分。 (已晚了^^)
CLARIFICATION :此类用于DSL编写配置文件。它针对的是非开发人员,因此运营商越少越好。
我目前使用instance_eval
块的代理类来实现它,但我必须弄乱instance_variable_set
才能设置值而我不喜欢它。我只是想尝试另一种方法,看看我是否可以让我的代码更具可读性。
答案 0 :(得分:3)
在Ruby中没有本地方法来“扩展”模块。 mixology gem将此模式实现为C(和Java,用于JRuby)扩展,创建mixin
和unmix
方法。如果您需要Ruby 1.9支持,似乎您可能需要应用patch。
如果您更愿意避免使用第三方库,另一种方法可能只是让setter私有化:
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
答案 1 :(得分:2)
def set(&block)
extend(Setter)
instance_eval(&block)
Setter.instance_methods.each do |m|
instance_eval "undef #{m}"
end
end
我不知道有什么方法能为你做到这一点虽然可能有些东西..这应该可以完成工作,通过查找Setter的所有实例方法并在Content中取消定义它们。
答案 2 :(得分:2)
您可以使用_why's mixico library(可用on github)
它可以让你这样做:
require 'mixology'
#...
def set(&block)
Setter.mix_eval(Setter, &block)
end
mixology gem做了很多相同的事情,只是略有不同。
答案 3 :(得分:0)
如果您感觉有实验情绪,请查看:dup_eval
它在某些方面类似于mixico但有一些有趣的附加功能(object2module)