5

Tôi biết rằng khi sử dụng mẫu xem (html, rabl), tôi không cần một lời gọi render rõ ràng trong hành động điều khiển của tôi vì theo mặc định, Rails sẽ hiển thị mẫu có tên tương ứng với tên hành động của bộ điều khiển. Tôi thích khái niệm này (không quan tâm đến rendering trong mã điều khiển của tôi) và do đó tự hỏi liệu điều này là có thể cũng như khi sử dụng ActiveModel :: Serializers?ActiveModel :: Serializer có yêu cầu hiển thị cuộc gọi rõ ràng không?

Ví dụ, đây là mã từ một bộ điều khiển tạo (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 

và đây là serializer:

class ProductSerializer < ActiveModel::Serializer 
    attributes :id, :name, :description, :url, :quantity, :price 
end 

Hitting /products/1.json, tôi sẽ mong đợi hai điều sẽ xảy ra:

  1. Các trường không được liệt kê trong bộ nối tiếp cần được ommited,
  2. Toàn bộ đối tượng JSON được lồng vào trong trường cấp cao nhất 'sản phẩm'.

Tuy nhiên, điều này không xảy ra, toàn bộ bộ nối tiếp bị bỏ qua. Nhưng sau đó nếu tôi thay đổi Hiện phương pháp như sau:

# 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 

Và bây giờ nó là tất cả tiền phạt, nhưng tôi đã mất đi lợi ích của các bộ lọc before_action (và có vẻ như với tôi rằng tôi có một số mã dư thừa).

Điều này thực sự nên được thực hiện như thế nào?

+0

@zmilojko Bạn đã thử sử dụng ['respond_with'] (http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_with)? Tôi nghĩ rằng 'respond_with (@product)' sẽ giúp bạn gần gũi nếu không chính xác những gì bạn muốn. [Ví dụ từ 'ActiveModel :: Serializer' README] (https://github.com/rails-api/active_model_serializers#render-json). –

+0

@PaulFioravanti Nhưng đó không phải là những gì tôi theo sau. Tôi muốn 'show' phương pháp để ở lại trống như Rails4 máy phát điện tạo ra nó, nhưng vẫn có thể sử dụng Serializer như được định nghĩa trong câu hỏi (và không jbuilder, như nó iseems Rails sẽ thích). – zmilojko

+0

@zmilojko Đây có phải là ứng dụng Rails 4.1 thẳng không? Hoặc một ứng dụng rails-api? Bạn đang tạo trạng thái ban đầu của ứng dụng như thế nào? – noel

Trả lời

0

Các 'mã dư thừa' chúng ta thấy trong một giây, chỉ dòng này:

@product = Product.find(params[:id])

Và tôi tin rằng đây là cùng một logic như before_action của bạn. Bạn không cần dòng này, chỉ cần loại bỏ nó. Bây giờ trùng lặp được loại bỏ.

Đến phần còn lại. Một hành động cần biết phải làm gì. Theo mặc định nếu tác vụ trống hoặc vắng mặt, 'action_name'.html.erb tương ứng (và các định dạng khác được chỉ định bởi respond_to) sẽ được tra cứu và hiển thị.

Đây là lý do tại sao trình tạo Rails 4 được tạo hoạt động: tạo ra show.html.erbshow.json.jbuilder được hiển thị.

Với ActiveModel::Serializer, bạn không có mẫu. Nếu bạn để trống hành động, nó không có đầu mối để render. Vì vậy bạn cần phải nói cho nó để render các @product như json, bởi một trong hai:

render json: @product

hoặc

respond_with @product

+0

Nhưng mục tiêu của tôi không phải là loại bỏ dòng trùng lặp, nhưng phần còn lại của nó. Tôi muốn phương thức ´Show´ để trống và vẫn gọi Serializer. Điều đó có vẻ không hoạt động, hiển thị toàn bộ ActiveModel: Serializer vô ích. – zmilojko

0

Nếu không có một rõ ràng render hoặc respond_with hoặc respond_to Rails sẽ tìm kiếm một mẫu phù hợp . Nếu mẫu đó không tồn tại thì Rails sẽ phát ra lỗi.

Tuy nhiên, bạn có thể tạo trình phân giải của riêng mình để bỏ qua điều này.Ví dụ, giả sử bạn đã tạo app\models\serialize_resolver.rb và đưa điều này vào nó:

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 

Và sau đó, trong cả hai bộ điều khiển ứng dụng của bạn (hoặc trong một bộ điều khiển cá nhân) nơi:

append_view_path ::SerializeResolver.new 

Với nó, bạn sẽ có thể để làm những gì bạn muốn. Nếu nó là một yêu cầu json, nó sẽ tạo ra một mẫu erb với đúng nội dung và trả về nó.

Hạn chế:

  • Đây là một chút thời gian vì nó dựa trên ERB, đó là không cần thiết. Nếu tôi có thời gian, tôi sẽ tạo một trình xử lý mẫu đơn giản. Sau đó, chúng ta có thể gọi mà không có erb.
  • Thao tác này xóa sạch phản hồi json mặc định.
  • Nó dựa vào tên bộ điều khiển để tìm biến thể (/posts được chuyển thành @post.)
  • Tôi chỉ thử nghiệm điều này một chút. Logic có thể thông minh hơn.

Ghi chú:

  • Nếu một mẫu có mặt, nó sẽ được sử dụng đầu tiên. Điều đó cho phép bạn ghi đè hành vi này.
  • Bạn không thể chỉ tạo trình kết xuất đồ họa mới và đăng ký nó, bởi vì quy trình mặc định không đạt được. Nếu không tìm thấy mẫu, bạn sẽ gặp lỗi. Nếu tập tin được tìm thấy, nó đi thẳng để gọi trình xử lý mẫu.
+0

Tôi đã sửa lại một số mã từ cuốn sách 'Crafting Rails' của Jose Valim để tạo ra câu trả lời này. – noel

+0

Hmmm, tôi đang tìm kiếm một giải pháp để đơn giản hóa mã của tôi, điều này hoàn toàn ngược lại. Tôi đoán cách tiếp cận jbuilder thắng sau đó, và ActiveModel: Serializer chỉ là không hữu ích ... – zmilojko

+0

Bạn có thể nói điều đó. Nhưng nó chỉ phải được thực hiện một lần. – noel

Các vấn đề liên quan