Почему метод установки Ruby возвращает в качестве последнего выражения выражение String, а не Symbol?

Неожиданное возвращаемое значение из метода: ожидание символа

У меня есть следующий класс билетов:

class Ticket
  VALID_STATES = %i[open closed invalid wontfix]
  attr_reader :status
  def status= new_state
    new_state = new_state.to_sym
    @status = new_state
  end
end

Когда передается String, а не Symbol, метод setter неожиданно возвращает String, даже если метод getter возвращает правильное значение. Например:

t = Ticket.new
t.status = 'closed'
#=> "closed"

t.status
#=> :closed

Похоже, правильное значение хранится в виде символа, но я не понимаю, почему метод возвращает "closed" в REPL, когда должно быть возвращено последнее вычисленное выражение :closed, Я ожидаю, что данное выражение должно быть решено как @status = :closedи, следовательно, должен вернуть символ.

Может кто-нибудь объяснить, почему я получаю String, а не Symbol в качестве возвращаемого значения из метода setter?

Предостережения и Предотвращение Велосипедов

  1. Я знаю, что этот пример можно просто использовать @status = new_state.to_sym вместо присвоения обратно new_state, но есть промежуточный код, который был удален для создания этого минимального примера. Я не хотел слишком сильно менять код, так как это лишает смысла показывать, что делает мой настоящий код. В любом случае, это не имеет значения для этой конкретной проблемы; Я пробовал это в обе стороны.
  2. Я пробовал это с Ruby 2.3.1, 2.4.0-preview2 и JRuby 9.1.4.0, так что это не зависит от версии.
  3. Различные попытки отладки сталкивались с другими проблемами, характерными для верхнего уровня REPL как в Pry, так и в IRB, которые я открою как отдельный вопрос. Дело в том, что попытки отладки с помощью альтернативных абстракций, таких как def foo=(str); @foo = str.to_sym; end ведет дальше вниз по кроличьей норе.
  4. Вполне возможно, что существует проблема между клавиатурой и креслом, но вопрос в действительности заключается в том, почему возвращаемое значение не соответствует ожидаемому классу.

2 ответа

Решение

Это ожидается. Из документации:

Обратите внимание, что для методов присваивания возвращаемое значение всегда будет игнорироваться. Вместо этого аргумент будет возвращен:

def a=(value)
  return 1 + value
end

p(a = 5) # prints 5

Ruby позволяет связывать назначения:

foo = bar = 'closed'

Вышеуказанное присваивает "closed" как для, foo а также bar,

Возвращая аргумент и игнорируя возвращаемое значение метода, вы можете заменить bar с вызовом метода:

foo = t.status = 'closed'

ИМО было бы весьма удивительно, если бы выше :closed в foo,

Если вы действительно хотите получить возвращаемое значение, используйте send или же public_send:

def a=(value)
  return 1 + value
end

p(a = 5)        # prints 5
p(send(:a=, 5)) # prints 6

Ответ довольно прост: в Ruby присваивания оцениваются по присваиваемому значению. В назначениях методов нет ничего особенного.

Другие вопросы по тегам