如何装饰来自第 3 方的哈希对象 API - Rails

How to Decorate a Hash Object from 3rd Party API - Rails

我在我的 rails 应用程序中使用外部第 3 方 API,我想知道我应该如何最好地处理响应,以便我可以向视图中的每个对象添加功能。

我有一个搜索框可以触发对外部博物馆的调用 API。我有一个处理调用和解析响应的服务对象,因此我可以确保一致的结构。 然后,我将在列表中展示返回的项目,以便可以将单个项目导入到应用程序中。

我想为 API 响应中存在图像 ID 的列表中的每个项目添加缩略图,或者在不存在图像 ID 的地方添加占位符。我在视图中使用一些我想提取的逻辑来处理这个问题。

   <% @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 && 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 负载?我尝试对数组使用创建,但它不存在。

我更喜欢创建模型对象(而不是 ActiveRecord)来在我的代码(而不是服务)中封装域逻辑。这可以通过在 PORO 中包含 ActiveModel::Model 来完成。

此策略允许我将所有业务逻辑保留在我的模型中,现在并非所有模型都需要关联 table。另外,我仍然可以获得验证和回调等所有优点,而且我还可以像编写任何其他模型一样编写测试用例。

在您的特定用例中,我将创建一个 MuseumObject 模型 class(其中可能还包括 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_accessors,然后您可以简单地使用散列初始化对象。

例如,如果您的哈希数组存储在 museum_hashes 变量中,您可以将其转换为模型对象数组,如

museum_hashes.map { |hash| MuseumObject.new(hash) }