Reputation: 17400
Foo.val
return nil
instead of "foo"
before calling Foo.set
?@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"
Upvotes: 4
Views: 1673
Reputation: 12225
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
Upvotes: 9
Reputation:
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"
Upvotes: 0
Reputation: 39620
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.
Upvotes: 3