ruby - 设置一个 Ruby 类变量,然后在类方法中使用它。HTTP派对
问题描述
我正在尝试创建一个用于充值的 API 包装器(Shopify 订阅服务),我正在使用 HTTParty gem
module RechargeAPI
require 'httparty'
BASE_URI = 'https://api.rechargeapps.com'
API_TOKEN = 'my_token'
class Client
include HTTParty
base_uri BASE_URI
headers 'X-Recharge-Access-Token': API_TOKEN
end
class Customer < Client
def self.search(params)
response = get('/customers', query: params)
self.from_json(response.body)
end
def self.find(params)
self.search(params).first
end
def self.all
response = get('/customers')
self.from_json(response.body)
end
def self.from_json(customers_json)
customers = JSON.parse(customers_json).dig('customers')
customers.map do|customer| OpenStruct.new(customer)
end
end
end
end
RechargeAPI::Customer.find(shopify_customer_id: 5363543224286) # returns <OpenStruct accepts_marketing=nil, analytics_data={"utm_params"=>[]}, billing_address1=....
它工作正常,但是我觉得我没有使用编写 api 包装器的最佳实践。理想情况下,我会设置我的 api 令牌,RechargeAPI.api_token = 'token'
而不是硬编码或在 ENV 文件中。但我不知道我会如何使用headers 'X-Recharge-Access-Token': API_TOKEN
理想情况下RechargeAPI::Customer.find(shopify_customer_id: 5363543224863)
也会返回一个RechargeAPI::Customer
对象而不是一个OpenStruct
. 我很想能够继承,Struct
但显然我不能,因为我是从我的RechargeAPI::Client
班级继承的。
任何人都可以建议我如何去做这件事,或者任何改进这段代码的方法。谢谢!
解决方案
理想情况下,我会使用 RechargeAPI.api_token = 'token' 之类的东西设置我的 api 令牌,而不是硬编码或在 ENV 文件中。
最简单的方法是在模块上创建访问器:
# /lib/recharge_api.rb
module RechargeAPI
class << self
attr_accessor :api_token
end
end
这看起来像黑魔法,但请记住模块是 Module 类的一个实例。这会将 api 令牌存储在@api_token
模块中。
对于更高级的配置,您可以使用该MyGem.configure
模式。
理想情况下,RechargeAPI::Customer.find(shopify_customer_id: 5363543224863) 将返回 RechargeAPI::Customer 对象而不是 OpenStruct。我希望能够从 Struct 继承,但显然我不能,因为我是从 RechargeAPI::Client 类继承的。
这种设计有一个大问题,就是如果你的RechargeAPI::Customer
类负责发送 HTTP 请求和管理来自响应的数据,那么你的类会做得太多。
我想构建一个类似 ActiveRecord 的接口,但在这种情况下,你最终会遇到一个很难处理的上帝类。
而是让客户端只执行 HTTP,一旦您真正开始处理错误和意外响应,这已经足够复杂了。
# /lib/recharge_api/client.rb
require 'httparty'
module RechargeAPI
# Base client class for RechargeAPI
class Client
include HTTParty
format :json
headers 'X-Recharge-Access-Token' => RechargeAPI.api_token
def initialize(**options)
@options = {
# set defaults here
}.merge(options)
end
end
end
# /lib/recharge_api/customer_client.rb
module RechargeAPI
# Gets customer data from RechargeAPI
class CustomerClient < Client
def search(params)
# @todo what are you going to do when the request is not successful?
handle_response(self.class.get('/customers', query: params))
end
def find(params)
search(params).first
end
def all
# @todo what are you going to do when the request is not successful?
response = self.class.get('/customers')
handle_response(response.body)
end
private
# refactor into a separate object if this gets too complex
def handle_response(json)
# @todo what are you going to do when the JSON does not contain the
# expected values?
json.dig('customers')&.map do |raw|
Customer.from_json(raw)
end
end
end
end
customers = RechargeAPI::CustomerClient.new(
foo_option: 'bar'
).all
然后创建一个数据对象,它获取原始 JSON 数据并对其进行规范化:
# /lib/recharge_api/customer.rb
module RechargeAPI
# Model representing a customer
class Customer
attr_accessor :email
attr_accessor :name
attr_accessor :is_karen
# Simple attribute assignment from keyword arguments
def initialize(**attributes)
attributes.each do |key, value|
send("#{key}=", value)
end
end
# factory method for creating a model from the API response
# refactor into a separate object if it gets too complex
def self.from_json(**json)
# do your data normalization here
new(**json)
end
end
end
这使您可以在不触及应用程序边界和存根 API 的情况下对其进行测试。如果你想允许任何属性,你也可以从 OpenStruct 继承。
推荐阅读
- mysql - 在mysql中保存数千个多个多边形组合的逻辑方法
- kdevelop - 如何在 KDevelop 中禁用变量多色?
- c# - 将 docx 转换为 byte[] 并使用 File.WriteAllBytes 将其保存到磁盘
- xcode - 我的一些图集动画被裁剪了,只显示了一半
- ios - SwiftUI - 具有透明背景的按钮
- java - 按子类型搜索失败
- amazon-web-services - Github 操作 - 传递秘密变量以呈现 ECS 任务定义操作
- python - 是否有计算不同类型概率密度函数积分的捷径?
- sql - 对于用户和联系人列表,实体关系图会是什么样子?
- spring - spring jpa 查询返回 404 没有消息