Требуется ли ActiveModel::Serializer явный вызов рендеринга?

Я знаю, что при использовании шаблонов представлений (html, rabl) мне не нужен явный вызов рендеринга в моем действии контроллера, потому что по умолчанию Rails отображает шаблон с именем, соответствующим имени действия контроллера. Мне нравится эта концепция (не заботясь о рендеринге в моем коде контроллера), и поэтому мне интересно, возможно ли это также при использовании ActiveModel::Serializers?

Пример, это код из сгенерированного контроллера (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, я ожидаю, что произойдут две вещи:

  1. Поля, не перечисленные в сериализаторе, следует опустить,
  2. Весь объект JSON должен быть инкапсулирован в поле верхнего уровня «продукт».

Однако этого не происходит, весь сериализатор игнорируется. Но тогда, если я изменю метод 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 (и мне кажется, что у меня какой-то лишний код).

Как это должно быть сделано на самом деле?


person zero-divisor    schedule 11.07.2013    source источник
comment
@zmilojko Пробовали ли вы использовать respond_with? Я думаю, что respond_with(@product) приблизит вас к тому, чего вы хотите. Пример из ActiveModel::Serializer README.   -  person Paul Fioravanti    schedule 18.04.2014
comment
@PaulFioravanti Но это не то, что мне нужно. Я хотел бы, чтобы метод show оставался пустым, поскольку генератор Rails4 создает его, но по-прежнему мог использовать сериализатор, как определено в вопросе (а не jbuilder, как кажется, предпочел бы Rails).   -  person zmilojko    schedule 19.04.2014
comment
@zmilojko Это прямое приложение Rails 4.1? Или приложение rails-api? Как вы создаете начальное состояние вашего приложения?   -  person noel    schedule 25.04.2014


Ответы (2)


Без явного указания 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.)
  • Я только немного протестировал это. Логика, наверное, могла бы быть умнее.

Примечания:

  • Если шаблон присутствует, он будет использоваться первым. Это позволяет вам переопределить это поведение.
  • Вы не можете просто создать новый рендерер и зарегистрировать его, потому что процесс по умолчанию его не затрагивает. Если шаблон не найден, вы получите сообщение об ошибке. Если файл найден, сразу вызывается обработчик шаблона.
person noel    schedule 25.04.2014
comment
Я переделал код из книги Хосе Валима «Создание рельсов», чтобы создать этот ответ. - person noel; 25.04.2014
comment
Хммм, я искал решение для упрощения моего кода, это как раз наоборот. Я предполагаю, что тогда подход jbuilder выигрывает, а ActiveModel:Serializer просто не так полезен... - person zmilojko; 26.04.2014
comment
Ты мог сказать это. Но это нужно сделать только один раз. - person noel; 27.04.2014
comment
Я не могу воспроизвести сгенерированный вывод вопроса с помощью Rails 4.1, rails-api или active_model_serializers. Я также не нашел примеров json без явного render json:. Вы используете другой драгоценный камень? Я получаю только ошибку без явного рендера. Я мог бы дать вам лучший ответ, если бы я соответствовал вашей настройке. - person noel; 27.04.2014

«Избыточный код», который мы видим во втором, состоит только из этой строки:

@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

or

respond_with @product

person James Chen    schedule 25.04.2014
comment
Но моей целью было удалить не повторяющуюся строку, а остальную часть. Я бы хотел, чтобы метод «Show» оставался пустым и по-прежнему вызывал сериализатор. Кажется, это не работает, делая весь ActiveModel:Serializer бесполезным. - person zmilojko; 26.04.2014