Установка переменной экземпляра из блока - PullRequest
4 голосов
/ 15 июня 2010

Как мне добиться чего-то подобного ниже, чтобы, когда я устанавливаю переменную s в блоке, она также устанавливала переменную экземпляра @subject в моем классе Topic?

class Topic
  def subject(&blk)
    blk.call(@subject) if block_given?
    @subject unless block_given?
  end
end

my_topic = Topic.new

p my_topic.subject #=> nil

my_topic.subject do |s|
  s = ['one', 'two', 'three']
  s.pop
  p s #=> ['one', 'two']
end

p my_topic.subject #=> nil... want it to be ['one, 'two']

Ответы [ 3 ]

4 голосов
/ 15 июня 2010

Вы не можете делать это так, как хотите.Аргумент блока ссылается на тот же объект, что и переменная экземпляра, но это абсолютно разные переменные, и установка одной никогда не установит другую.Есть два варианта:

  1. Установить переменную для результата блока, чтобы она была такой:

    class Topic
      def subject
        @subject = yield if block_given?
        @subject unless block_given?
      end
    end
    

    и внутри блока:

    my_topic.subject do
      s = ['one', 'two', 'three']
      s.pop
      p s #=> ['one', 'two']
      s
    end
    
  2. Имеет метод subject instance_eval блок, чтобы блок мог явно установить переменную экземпляра

3 голосов
/ 15 июня 2010

То, что вы хотите сделать, называется передачей по ссылке.Это не возможно в рубине.У вас есть две альтернативы:

a) Выполните команду @subject = blk.call и верните s из блока.Обычно самый простой и чистый вариант.

b) Вместо s = сделайте @subject = в блоке, а затем используйте instance_eval(&blk) вместо blk.call.Это установит переменную @subject, однако для этого необходимо, чтобы пользователь метода субъекта знал о переменной @subject, и это не позволит вам вызывать блок несколько раз для установки различных переменных.

2 голосов
/ 16 июня 2010

В дополнение к данным решениям, если вы знаете, что ивар будет оставаться String / Array / Hash, вы можете сделать следующее:

class Topic
  def subject
    @subject ||= 'sane default'
    if block_given? then yield(@subject)
    else @subject
    end
  end
end

t = Topic.new
t.subject { |s| s.replace 'fancy stuff' }

Хотя из того, что, я думаю, вы делаетеэто наиболее подходящий код:

class Topic
  def subject
    return @subject unless block_given?
    @subject = yield(@subject)
  end
end

t = Topic.new
t.subject { |s| 'fancy stuff' }
t.subject { |s| "very #{s}" }
t.subject # => "very fancy stuff"

Кроме того, вы можете сделать это без блока:

class Topic
  def subject(value = nil)
    @subject = value % @subject if value
    @subject = yield @subject if block_given?
    @subject
  end
end

t = Topic.new
t.subject 'fancy stuff'                   # => "fancy stuff"
t.subject 'very %s'                       # => "very fancy stuff"
t.subject { |s| s.sub 'fancy', 'freaky' } # => "very freaky stuff"

Имейте в виду, что оператор p s, который вы используете, возвращаетnil.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...