Sean Lerner
Sean Lerner

Reputation: 599

Why does a class variable work but an instance variable return nil when including a module in a class [Ruby]?

In the following code, why does the data variable work properly as a class variable but not an instance variable? (Note: I tried to simplify/reproduce only the necessary bits of the script to have the question make sense. If more is needed I can post the full script.)

require 'prawn'

class Test
  include Prawn

  def initialize (data, filename) #data is an array of arrays
    @@data = data #this is the variable in question, when @data the script fails
    @filename = filename
  end

  def to_pdf
    Document.generate("#{@filename}.pdf") do #included from Prawn
      @@data.each do |item|
        do some stuff to the data
      end
      make_table with the data that's been worked on
    end
  end
end

test_run = Test.new([[1, 2, 1], [1, 2, 2]], "testfile.pdf")
test.to_pdf

If @@data is instead @data the script returns undefined method 'each' for nil:NilClass (NoMethodError) Why is that? Shouldn't an instance variable be equally accessible to the included module? (also why is it ok that the @filename variable is an instance method?)

Upvotes: 0

Views: 487

Answers (1)

mu is too short
mu is too short

Reputation: 434755

From the fine manual:

When using the implicit block form, Prawn will evaluate the block within an instance of Prawn::Document, simplifying your syntax. However, please note that you will not be able to reference variables from the enclosing scope within this block.

Prawn::Document.generate "example.pdf" do
    # ...

If you need to access your local and instance variables, use the explicit block form shown below. In this case, Prawn yields an instance of PDF::Document and the block is an ordinary closure:

Prawn::Document.generate "example.pdf" do |pdf|
    #...

Your block in to_pdf is being evaluated in the content of a Prawn::Document instance, not within the context of your object and that means that there is no @data instance variable so @data is created with a value of nil when you try to access it. You probably want to use a block with a single argument:

Document.generate("#{@filename}.pdf") do |pdf|
  # do things to 'pdf' in here

Upvotes: 4

Related Questions