如何装饰来自第 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_accessor
s,然后您可以简单地使用散列初始化对象。
例如,如果您的哈希数组存储在 museum_hashes
变量中,您可以将其转换为模型对象数组,如
museum_hashes.map { |hash| MuseumObject.new(hash) }
我在我的 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_accessor
s,然后您可以简单地使用散列初始化对象。
例如,如果您的哈希数组存储在 museum_hashes
变量中,您可以将其转换为模型对象数组,如
museum_hashes.map { |hash| MuseumObject.new(hash) }