Преамбула:
Я исследовал, как сделать версию API, и нашел несколько способов сделать это. Я решил попробовать предложение Питера Уильямса и создал новые типы MIME для определения версии и формата. Я не смог найти исчерпывающего описания того, что делал это по «рельсовому пути», поэтому я собрал информацию из нескольких мест. Мне удалось заставить его работать, но есть некоторая глупость в том, как рендереры обрабатывают массив виджетов и экземпляр виджета в response_with
.
Основные шаги и проблема:
Я зарегистрировал типы mime и добавил рендереры для версии 1 как в xml, так и в json в ApplicationController, рендереры вызывают методы to_myproj_v1_xml
и to_myproj_v1_json
в модели. response_with (@widget)
работает нормально, но response_with (@widgets)
вызывает HTTP / 1. 1 500 Внутренняя ошибка сервера
: «Шаблон отсутствует».
Временное решение:
«Шаблон отсутствует» означает, что визуализация не вызывалась и соответствующий шаблон не существует. случайно я обнаружил, что он ищет метод класса ... поэтому я придумал приведенный ниже код, который работает, но я не очень доволен им. Глупость в основном связана с xml = obj.to_myproj_v1_xml (obj)
и дублированием в модели.
У меня вопрос - делал ли кто-нибудь что-нибудь похожее чуть более чистым способом?
- = обновленный код = -
config / initializers / mime_types.rb :
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+xml', :myproj_v1_xml
Mime::Type.register 'application/vnd.com.mydomain.myproj-v1+json', :myproj_v1_json
app / controllers / application_controller. rb :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate
ActionController.add_renderer :myproj_v1_xml do |obj, options|
xml = obj.to_myproj_v1_xml
self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+xml')
self.response_body = xml
end
ActionController.add_renderer :myproj_v1_json do |obj, options|
json = obj.to_myproj_v1_json
self.content_type ||= Mime::Type.lookup('application/vnd.com.mydomain.myproj-v1+json')
self.response_body = json
end
end
app / models / widget.rb :
class Widget < ActiveRecord::Base
belongs_to :user
V1_FIELDS = [:version, :model, :description, :name, :id]
def to_myproj_v1_xml
self.to_xml(:only => V1_FIELDS)
end
def to_myproj_v1_json
self.to_json(:only => V1_FIELDS)
end
def as_myproj_v1_json
self.as_json(:only => V1_FIELDS)
end
end
app / controllers / widgets_controller.rb :
class WidgetsController < ApplicationController
respond_to :myproj_v1_xml, :myproj_v1_json
def index
@widgets = @user.widgets
respond_with(@widgets)
end
def create
@widget = @user.widgets.create(params[:widget])
respond_with(@widget)
end
def destroy
@widget = @user.widgets.find(params[:id])
respond_with(@widget.destroy)
end
def show
respond_with(@widget = @user.widgets.find(params[:id]))
end
...
end
config / initializers / monkey_array.rb
class Array
def to_myproj_v1_json(options = {})
a = []
self.each { |obj| a.push obj.as_myproj_v1_json }
a.to_json()
end
def to_myproj_v1_xml(options = {})
a = []
self.each { |obj| a.push obj.as_myproj_v1_json } # yes this is json instead of xml. as_json returns a hash
a.to_xml()
end
end
ОБНОВЛЕНИЕ:
Нашел другое решение, которое выглядит лучше, но все же немного странно (я все еще не совсем уверен в патчах обезьяны), хотя, вероятно, нормально ... в основном переместил сборку данных ответа из метода класса в_myproj_v1_json
к патчу обезьяны на Array. Таким образом, когда есть массив виджетов, он вызывает метод экземпляра as_myproj_v1_json
для каждого виджета и возвращает весь массив в желаемом формате.
Одно примечание:
Я обновил приведенный ниже код, чтобы он стал тем, что используется в настоящее время, поэтому исходный вопрос может не иметь смысла. если кто-то хочет, чтобы исходный вопрос и код отображались как был, а также исправленный код в ответе, я могу сделать это вместо этого.