Search code examples
rubyinitializationblock

Ruby object initialization using instance_eval


Consider the following class:

class Person
    attr_accessor :first_name

    def initialize(&block)
        instance_eval(&block) if block_given?
    end
end

When I create an instance of Person as follows:

person = Person.new do
    first_name = "Adam"
end

I expected the following:

puts person.first_name

to output "Adam". Instead, it outputs only a blank line: the first_name attribute has ended up with a value of nil.

When I create a person likes this, though:

person = Person.new do
    @first_name = "Adam"
end

The first_name attribute is set to the expected value.

The problem is that I want to use the attr_accessor in the initialization block, and not the attributes directly. Can this be done?


Solution

  • Ruby setters cannot be called without an explicit receiver since local variables take a precedence over method calls.

    You don’t need to experiment with such an overcomplicated example, the below won’t work as well:

    class Person
      attr_accessor :name
      def set_name(new_name)
        name = new_name
      end
    end
    

    only this will:

    class Person
      attr_accessor :name
      def set_name(new_name)
        # name = new_name does not call `#name=`
        self.name = new_name
      end
    end
    

    For your example, you must explicitly call the method on a receiver:

    person = Person.new do
      self.first_name = "Adam"
    end