4
  • Why does Foo.val return nil instead of "foo" before calling Foo.set?
  • Is there any mechanism for initializing @val on class evaluation?
  • In which scope is @val = "foo" stored into?

    class Foo
      class << self
        @val  = "foo"
        attr_reader :val
    
        def set(val)
          @val = val
        end
      end
    end
    
    p Foo.val # nil
    Foo.set("bar")
    p Foo.val # "bar"
    

3 Answers 3

9

You can initialize @val in Foo like this:

class Foo
  @val  = "foo"
  class << self
    attr_reader :val

    def set(val)
      @val = val
    end
  end
end

p Foo.val         #=> "foo"
Foo.set("bar")
p Foo.val         #=> "bar"

Your code initializes @val not on Foo, but on Foo's metaclass

Sign up to request clarification or add additional context in comments.

Comments

3

Ruby generally executes expressions upon parsing them. The reason why your code did not perform as expected is because you are setting a class instance variable for the singleton class of Foo, but on the other hand you are accessing the class instance variable of Foo itself, that's why it doesn't work:

class << self
  @val  = "foo" # scope is class scope of singleton class of Foo
  attr_reader :val

  def set(val)
    # scope is instance scope of singleton class of Foo (equal to Foo itself)
    @val = val 
  end
end

That's why Foo.val yields nil in your case - it hasn't been set yet.

Setting val on class evaluation can be achieved in the way that Victor already demonstrated.

See also this post for a discussion about scope.

Comments

0

No need to use #set. Simply define #val method and use nil guard:

class Foo
    class << self
        def val; @val ||= "bar" end
    end
end
p Foo.val  #=> "bar"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.