Требуется ли ActiveModel::Serializer явный вызов рендеринга?
Я знаю, что при использовании шаблонов представления (html, rabl) мне не нужен явный вызов рендеринга в моем действии контроллера, потому что по умолчанию Rails отображает шаблон с именем, соответствующим имени действия контроллера. Мне нравится эта концепция (не заботясь о рендеринге в коде моего контроллера), и поэтому задаюсь вопросом, возможно ли это также при использовании ActiveModel::Serializer?
Пример, это код сгенерированного контроллера (Rails 4.1.0):
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
#other actions
# GET /products/1
# GET /products/1.json
def show
end
end
и это сериализатор:
class ProductSerializer < ActiveModel::Serializer
attributes :id, :name, :description, :url, :quantity, :price
end
Нажав на /products/1.json, я ожидаю, что произойдут две вещи:
- Поля, не перечисленные в сериализаторе, должны быть пропущены,
- Весь объект JSON должен быть инкапсулирован в поле верхнего уровня "product".
Однако этого не происходит, весь сериализатор игнорируется. Но тогда, если я изменю метод Show следующим образом:
# GET /products/1
# GET /products/1.json
def show
@product = Product.find(params[:id])
respond_to do |format|
format.html
format.json { render json: @product }
end
end
И теперь все хорошо, но я потерял преимущество фильтра before_action (и мне кажется, что у меня есть избыточный код).
Как это действительно должно быть сделано?
2 ответа
"Избыточный код", который мы видим во втором, это только эта строка:
@product = Product.find(params[:id])
И я верю, что это та же логика, что и у вашей before_action. Вам не нужна эта строка, просто удалите ее. Теперь дублирование удалено.
Для оставшейся части. Действие должно знать, что делать. По умолчанию, если действие пустое или отсутствует, соответствующее 'action_name'.html.erb (и другие форматы, указанные respond_to
) будет найден и обработан.
Вот почему работает генератор Rails 4: он создает show.html.erb
а также show.json.jbuilder
которые оказываются.
С ActiveModel::Serializer
У вас нет шаблона. Если вы оставляете действие пустым, оно не имеет ни малейшего представления, что делать. Таким образом, вы должны сказать это, чтобы сделать @product
как JSON, либо:
render json: @product
или же
respond_with @product
Без явного render
или же respond_with
или же respond_to
Rails будет искать соответствующий шаблон. Если этот шаблон не существует, Rails выдает ошибку.
Тем не менее, вы можете создать свой собственный распознаватель, чтобы обойти это. Например, предположим, что вы создали app\models\serialize_resolver.rb
и поместите это в это:
class SerializeResolver < ActionView::Resolver
protected
def find_templates(name, prefix, partial, details)
if details[:formats].to_a.include?(:json) && prefix !~ /layout/
instance = prefix.to_s.singularize
source = "<%= @#{instance}.active_model_serializer.new(@#{instance}).to_json.html_safe %>"
identifier = "SerializeResolver - #{prefix} - #{name}"
handler = ActionView::Template.registered_template_handler(:erb)
details = {
format: Mime[:json],
updated_at: Date.today,
virtual_path: "/#{normalize_path(name, prefix)}"
}
[ActionView::Template.new(source, identifier, handler, details)]
else
[]
end
end
def normalize_path(name, prefix)
prefix.present? ? "#{prefix}/#{name}" : name
end
end
И затем, в вашем контроллере приложения (или в отдельном контроллере) место:
append_view_path ::SerializeResolver.new
При этом вы сможете делать то, что хотите. Если это запрос json, он создаст шаблон erb с правильным содержимым и вернет его.
Ограничения:
- Это немного неуклюже, потому что оно опирается на erb, который не нужен. Если у меня будет время, я создам простой обработчик шаблонов. Тогда мы можем вызвать это без erb.
- Это стирает по умолчанию
json
ответ. - Он полагается на имя контроллера, чтобы найти переменную экземпляра (
/posts
преобразуется в@post
.) - Я только немного проверил это. Логика, вероятно, может быть умнее.
Заметки:
- Если шаблон присутствует, он будет использован первым. Это позволяет вам переопределить это поведение.
- Вы не можете просто создать новый рендерер и зарегистрировать его, потому что процесс по умолчанию не поразит его. Если шаблон не найден, вы получаете сообщение об ошибке. Если файл найден, он переходит к вызову обработчика шаблона.