5

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

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

Однако этого не происходит, весь сериализатор игнорируется. Но если я изменить шоу метод следующим образом:

# 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 фильтра (и мне кажется, что у меня есть некоторый избыточный код).

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

+0

@zmilojko Вы пытались использовать ['response_with'] (http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_with)? Я думаю, 'response_with (@product)' приблизит вас к тому, что вы не хотите. [Пример из «ActiveModel :: Serializer» README] (https://github.com/rails-api/active_model_serializers#render-json). –

+0

@PaulFioravanti Но это не то, что мне нужно. Я бы хотел, чтобы метод show показывался пустым, поскольку генератор Rails4 создает его, но все же можно использовать Serializer, как определено в вопросе (а не jbuilder, так как это исключает Rails). – zmilojko

+0

@zmilojko Является ли это прямым Rails 4.1? Или приложение rails-api? Как вы создаете начальное состояние своего приложения? – noel

ответ

0

«резервный код» мы видим, во втором, эта линия только:

@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

+0

Но моя цель состояла в том, чтобы не удалить дублируемую строку, а остальную часть. Я хотел бы, чтобы метод 's' оставался пустым и все еще вызывал Сериализатор. Кажется, что это не сработало, что делает весь ActiveModel: Сериализатор бесполезен. – zmilojko

0

без явного 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.
  • Это уничтожает ответ по умолчанию json.
  • Он опирается на имя контроллера, чтобы найти переменную экземпляра (/posts преобразуется в @post.)
  • Я только это немного испытанным. Логика, вероятно, могла бы быть более умной.

Примечания:

  • Если шаблон присутствует, то он будет использоваться в первую очередь. Это позволяет вам переопределить это поведение.
  • Вы не можете просто создать новый рендерер и зарегистрировать его, потому что процесс по умолчанию не ударил его. Если шаблон не найден, вы получите сообщение об ошибке. Если файл найден, он обращается прямо к вызову обработчика шаблона.
+0

Я переработал какой-то код из книги Хосе Валима «Ремесленные рельсы», чтобы создать этот ответ. – noel

+0

Хммм, я искал решение для упрощения моего кода, это почти все наоборот. Я предполагаю, что победитель jbuilder выигрывает тогда, и ActiveModel: Сериализатор просто не очень полезен ... – zmilojko

+0

Вы могли бы так сказать. Но это нужно сделать только один раз. – noel