Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 自定义attr_reader以执行属性的惰性实例化_Ruby_Accessor - Fatal编程技术网

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作为参数。但是有点臭。