Ruby 自定义attr_reader以执行属性的惰性实例化
(大编辑,我得到了其中的一部分…) 我一直在进行黑客攻击,我提出了一种方法来指定在读取属性之前需要做的事情:Ruby 自定义attr_reader以执行属性的惰性实例化,ruby,accessor,Ruby,Accessor,(大编辑,我得到了其中的一部分…) 我一直在进行黑客攻击,我提出了一种方法来指定在读取属性之前需要做的事情: class Class def attr_reader(*params) if block_given? params.each do |sym| define_method(sym) do yield self.instance_variable_get("@#{sym}") end
class Class
def attr_reader(*params)
if block_given?
params.each do |sym|
define_method(sym) do
yield
self.instance_variable_get("@#{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
class Test
attr_reader :normal
attr_reader(:jp,:nope) { changethings if @nope.nil? }
def initialize
@normal = "Normal"
@jp = "JP"
@done = false
end
def changethings
p "doing"
@jp = "Haha!"
@nope = "poop"
end
end
j = Test.new
p j.normal
p j.jp
但是,
changetings
并没有被认为是一种方法——有人有什么想法吗?这里是另一种方法,你可以看看。它不像您使用define\u方法所做的那样优雅,但它可能值得一看
将新方法lazy\u attr\u reader
添加到Class
class Class
def lazy_attr_reader(*vars)
options = vars.last.is_a?(::Hash) ? vars.pop : {}
# get the name of the method that will populate the attribute from options
# default to 'get_things'
init_method = options[:via] || 'get_things'
vars.each do |var|
class_eval("def #{var}; #{init_method} if !defined? @#{var}; @#{var}; end")
end
end
end
然后像这样使用它:
class Test
lazy_attr_reader :name, :via => "name_loader"
def name_loader
@name = "Bob"
end
end
在行动中:
irb(main):145:0> t = Test.new
=> #<Test:0x2d6291c>
irb(main):146:0> t.name
=> "Bob"
irb(main):145:0>t=Test.new
=> #
irb(主):146:0>t.name
=>“鲍勃”
您需要在实例的上下文中计算块<默认情况下,code>yield
将在其本机上下文中对其进行评估
class Class
def attr_reader(*params, &blk)
if block_given?
params.each do |sym|
define_method(sym) do
self.instance_eval(&blk)
self.instance_variable_get("@#{sym}")
end
end
else
params.each do |sym|
attr sym
end
end
end
end
从使用类固醇类
attr\u reader
的人的角度来看,改变区块上下文是非常违反直觉的
也许您应该考虑“使用可选参数指定方法名称”:
def lazy_attr_reader(*args, params)
args.each do |e|
define_method(e) do
send(params[:init]) if params[:init] && !instance_variable_get("@#{e}")
instance_variable_get("@#{e}")
end
end
end
class Foo
lazy_attr_reader :foo, :bar, :init => :load
def load
@foo = 'foo'
@bar = 'bar'
end
end
f = Foo.new
puts f.bar
#=> bar
看起来我的attr_reader块是在类对象的范围内执行的,而不是在它的实例中执行的。关于如何将其强制到实例,您可以使用
instance\u eval
而不是yield,以block作为参数。但是有点臭。