Я изучаю, как расширять сериализаторы Discourse, и столкнулся с поведением, которое до конца не понимаю.
Я добавил этот код для переопределения PostSerializer#raw:
require_dependency "post_serializer"
class ::PostSerializer
def raw
if scope.can_edit?(object)
object.raw
else
object.raw&.truncate(300)
end
end
end
Это работает идеально.
Однако, если я изменю код, чтобы использовать super:
require_dependency "post_serializer"
class ::PostSerializer
def raw
if scope.can_edit?(object)
super
else
object.raw&.truncate(300)
end
end
end
Когда scope.can_edit?(object) возвращает true, вызов super приводит к тому, что /posts/<id>.json возвращает ошибку 500.
Я знаю, что могу избежать проблемы, используя object.raw вместо super, но я хочу понять, почему super вызывает исключение в этом случае, особенно потому, что я также переопределяю другие методы (cooked, post_stream и т. д.), и super работает там без проблем.
Поскольку я всё ещё новичок во внутренних механизмах Discourse, я был бы очень признателен за объяснение:
на что именно ссылается super внутри PostSerializer#raw,
почему вызов super в данном случае приводит к ошибке 500,
и почему raw ведёт себя иначе по сравнению с методами вроде cooked.
Я почти уверен, что это связано с тем, что raw отсутствует в сериализаторе для обычного поста. Он включается только при редактировании. Для отображения поста достаточно поля cooked; было бы расточительно включать raw, если он не нужен.
cooked и post_stream присутствуют в сериализаторе, поэтому super работает корректно.
Это особенность Ruby. Когда вы используете class ::PostSerializer, вы переопределяете определение в исходном классе, а не наследуете его. Поскольку вы не наследуете PostSerializer, метод super не может найти соответствующий метод.
Вместо повторного открытия уже определённого класса следует использовать prepend.
Спасибо за объяснение!
Вы правы — повторное открытие ::PostSerializer было проблемой. После переключения на prepend всё работает как ожидалось.
Теперь я использую этот модуль:
module PostSerializerExtension
def raw
if scope.can_edit?(object)
super
else
object.raw&.truncate(300)
end
end
end
reloadable_patch do
require_dependency "post_serializer"
::PostSerializer.prepend(::PostSerializerExtension)
end
У меня это работает идеально. Ещё раз спасибо за помощь!