#ruby-on-rails #api #decorator
Вопрос:
Я использую внешний сторонний API в своем приложении rails, и мне интересно, как мне лучше всего обработать ответ, чтобы я мог добавить функциональность к каждому объекту в представлении.
У меня есть окно поиска, которое запускает вызов внешнего API музея. У меня есть объект службы, обрабатывающий вызов и анализирующий ответ, поэтому я могу обеспечить согласованную структуру. Затем я представляю возвращенные товары в списке, чтобы отдельные товары можно было импортировать в приложение.
Я хочу добавить уменьшенное изображение для каждого элемента в списке, где идентификатор изображения существует в ответе API, или добавить заполнитель, где его нет. Я справляюсь с этим с некоторой логикой в представлении, которое я хотел бы извлечь.
<% @mus_objects.each do |ob| %>
<div class="row mt-5">
<div class="col-lg-6">
<% if ob['_primaryImageId'] %>
<%= image_tag("https://iiif_image_server_example.com/#{ob['_primaryImageId']}/full/!140,140/0/default.jpg") %>
<% else %>
<%= image_tag("https://placehold.it/140x140?text=No Image") %>
<% end %>
<%= ob["_primaryTitle"] %>
<%= link_to 'Import', new_admin_museum_object_path(ob) %>
</div>
</div>
<% end %>
Это мой код контроллера:
class Admin::MuseumApiController < ApplicationController
def search
@search_term = params[:search_term]
if params[:searchtype] == 'search_term'
response = MuseumApiManager::FetchBySearchTerm.call(@search_term)
elsif params[:searchtype] == 'systemNumber'
response = MuseumApiManager::FetchBySystemNumber.call(@search_term)
end
if response amp;amp; response.success?
@mus_objects = response["records"]
end
end
end
Ответ на мой вызов объекта службы выглядит следующим образом:
#<OpenStruct success?=true, records=[{"systemNumber"=>"O123", "objectType"=>"Bottle", "_primaryTitle"=>"Bottle", "_primaryImageId"=>'1234'}]>
Таким образом, мои @mus_objects, используемые представлением, представляют собой массив хэшей.
Я не уверен, каков наилучший подход для решения этой проблемы. Я думаю, что что-то вроде декоратора было бы уместно, чтобы я мог вместо этого позвонить ob.thumbnail_url
в пределах представления, но декоратору требуется модель.
**** Экспериментируйте с ответом, предложенным с помощью ActiveModel ****
Я создал PORO, как было предложено, и привел методы поиска здесь, хотя, пока я его тестирую, я оставил логику в объекте ServiceObject.
class MuseumApiObject
include ActiveModel::Model
def self.fetch_by_search_term(search_term)
response = MuseumApiManager::FetchBySearchTerm.call(search_term)
response["records"]
end
def thumbnail_url
if _primaryImageId
"https://iiif_example.com/#{_primaryImageId}/full/!140,140/0/default.jpg"
else
'https://placehold.it/140x140?text=No Image'
end
end
end
теперь в контроллере я делаю это:
class Admin::MuseumApiController < ApplicationController
def search
@search_term = params[:search_term]
@mus_objects = MuseumApiObject.fetch_by_search_term(@search_term)
end
end
Теперь это все еще просто создает массив хэшей. Как я могу создать экземпляр этого массива хэшей для загрузки объектов MuseumApiObjects? Я попытался использовать create с массивом, но он не существует.
Ответ №1:
Я предпочитаю создавать объекты модели (не ActiveRecord) для инкапсуляции логики домена в моем коде (вместо служб). Это можно сделать, включив ActiveModel::Model
в ПОРОс.
Эта стратегия позволяет мне сохранить всю мою бизнес-логику в моих моделях, и теперь не все модели должны иметь связанную таблицу. В качестве плюса я все еще получаю все преимущества, такие как проверки и обратные вызовы, и я также могу писать свои тестовые примеры, как и для любой другой модели.
В вашем конкретном случае использования я создам MuseumObject
класс модели (который также может включать логику взаимодействия с API), а также thumbnail_url
метод.
class MuseumObject
include ActiveModel::Model
attr_accessor :system_number, :object_type, primary_title, primary_image_id
def initialize(attributes)
self.system_number = attributes['systemNumber']
...
end
def self.search
...
end
def thumbnail_url
...
end
end
Изменить:
Вам нужно будет инициализировать объект с помощью хэша. Вы можете определить attr_accessor
s для всех ваших полей, а затем вы можете просто инициализировать объекты с помощью хэшей.
Скажем, например, если ваш массив хэшей хранится в museum_hashes
переменной, вы можете преобразовать его в массив объектов вашей модели следующим образом,
museum_hashes.map { |hash| MuseumObject.new(hash) }
Комментарии:
1. Спасибо, что познакомили меня с ActiveModel. Я добавил свои последующие примечания к исходному вопросу, пока пытаюсь заставить это работать — не могли бы вы помочь?
2. Добавил более подробную информацию к ответу, пожалуйста, посмотрите, поможет ли это.