Преамбула:
Я исследовал, как версионировать API, и нашел несколько способов сделать это. Я решил попробовать предложение peter williams' и создал нового поставщика. типы mime для указания версии и формата. Я не смог найти окончательного описания того, как это сделать, следуя «пути рельсов», поэтому я собрал информацию из нескольких мест. Мне удалось заставить его работать, но есть некоторая глупость в том, как средства визуализации обрабатывают массив виджетов и экземпляр виджета в respond_with
.
Основные шаги и проблема:
Я зарегистрировал mime-типы и добавил рендереры для версии 1 как в xml, так и в json в ApplicationController, рендереры вызывают методы to_myproj_v1_xml
и to_myproj_v1_json
в модели. respond_with(@widget)
работает нормально, но respond_with(@widgets)
выдает HTTP/1.1 500 Internal Server Error
о том, что "Шаблон отсутствует".
Временное решение:
«Шаблон отсутствует» означает, что рендеринг не вызывался и соответствующий шаблон не существует. случайно я обнаружил, что он ищет метод класса... поэтому я придумал приведенный ниже код, который работает, но я не очень доволен им. Глупость в основном связана с 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
ОБНОВЛЕНИЕ:
Нашел другое решение, которое кажется лучше, но все же немного странным (мне все еще не совсем комфортно с патчами для обезьян), хотя, вероятно, все в порядке ... в основном перенесено построение данных ответа из метода класса to_myproj_v1_json
в патч для обезьяны на Array. Таким образом, когда есть массив виджетов, он вызывает метод экземпляра as_myproj_v1_json
для каждого виджета и возвращает весь массив в желаемом формате.
Одна нота:
- as_json не имеет ничего общего с форматом json, просто создает хеш. Добавьте пользовательское форматирование в as_myproj_v1_json (или переопределите as_json, если вы не используете настраиваемые типы mime), тогда to_json изменит хэш на строку json.
я обновил приведенный ниже код, чтобы он использовался в настоящее время, поэтому исходный вопрос может не иметь смысла. если кто-то хочет, чтобы исходный вопрос и код отображались как были, а фиксированный код в ответе, я могу сделать это вместо этого.