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