can't access ActiveRecord mutators in a block

2019-09-07 05:14发布

I am inside a Rails controller and I am trying to access my instance variable in a block: This gives an error saying that "no method field1 for Nil":

Prawn::Document.generate("hello.pdf") do
  @model.field1
end

However, if I do this, then it works:

my_model = @model
Prawn::Document.generate("hello.pdf") do
  my_model.field1
end

Could this have something to do with ActiveRecord accessors or instance variables in a block?

2条回答
一纸荒年 Trace。
2楼-- · 2019-09-07 05:45

This kind of problem appears when a block is being executed in a different context, usually through a instance_eval. So let's check the code:

#lib/prawn/document.rb: Document#initialize    
if block
  block.arity < 1 ? instance_eval(&block) : block[self]
end

There you have your instance_eval, and you can also see the solution: pass a block that accepts the document as argument and you'll now be able to access the instance variables as usual:

Prawn::Document.generate("hello.pdf") do |doc|
  @my_model.field1
end
查看更多
孤傲高冷的网名
3楼-- · 2019-09-07 05:52

That's happening because code inside block is executed in context of Prawn::Document object. Let's go inside this code:

module Prawn
  class Document
    def self.generate(filename,options={},&block)
      pdf = new(options,&block)
      pdf.render_file(filename)
    end

    def initialize(options={},&block)
      if block
        block.arity < 1 ? instance_eval(&block) : block[self]
      end
    end
  end
end

As you can see, block is executed with Document object as self. It try to find @model as instance variable of self, can't do this and return nil. If you use local variable model, you get help of closures and your code is working properly

查看更多
登录 后发表回答