Метапрограммирование в Ruby: инициализация переменной singleton_class - PullRequest
4 голосов
/ 06 августа 2011
  • Почему Foo.val возвращает nil вместо "foo" перед вызовом Foo.set?
  • Есть ли механизм инициализации @val при оценке класса?
  • В каком объеме @val = "foo" хранится в?

    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 ]

9 голосов
/ 06 августа 2011

Вы можете инициализировать @val в Foo следующим образом:

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"

Ваш код инициализирует @val не в Foo, а в метаклассе Foo

3 голосов
/ 06 августа 2011

Ruby обычно выполняет выражения при их разборе.Причина, по которой ваш код не работал должным образом, заключается в том, что вы устанавливаете переменную экземпляра класса для синглтон-класса класса Foo, но с другой стороны, вы обращаетесь к переменной экземпляра класса самого Foo, поэтомуэто не работает:

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

Вот почему Foo.val дает nil в вашем случае - оно еще не было установлено.

Установка val на оценку класса можетбыть достигнутым так, как Виктор уже продемонстрировал.

См. также в этом посте для обсуждения возможностей.

0 голосов
/ 22 августа 2011

Нет необходимости использовать #set. Просто определите метод #val и используйте nil guard:

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