Last active
June 6, 2018 10:36
-
-
Save noel9999/172946f174e144c2d48e33535fc5d376 to your computer and use it in GitHub Desktop.
一個 controller 的 action 需要去戳外部 api 三次的邏輯需求,使用 fiber 整理邏輯
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'fiber' | |
Result = Struct.new :success, :payload, :status do | |
def self.create(args = {}) | |
args = { success: true, payload: {}, status: :ok }.merge(args.symbolize_keys) | |
new *args.values_at(:success, :payload, :status) | |
end | |
def success? | |
!!success | |
end | |
def error | |
{ message: 'Bad thing happends', errors: {} }.merge(payload) | |
end | |
end | |
class Webhook::CustomersController < ApplicationController | |
# 使用 fiber 組織邏輯,把每個步驟都劃分成一次 yield ,並且檢查結果成不成功,成功的話再把傳回給自己當參數繼續下去,失敗的話就 render | |
# 好處是 create 的邏輯變得很清晰、一目了然 | |
# 但三個步驟拆出來的方法所需的參數可能有相依性(可能會有共用的參數),所以目前採用滾雪球的方式,每個步驟把自己新產生的結果都 merge 回參數,然後拋給下一個方法,有點詭異的作法 | |
# 實作上也多了許多 code ,所成本跟好處比起來真的不太明顯... | |
def create | |
token = get_access_token(params[:merchant_id]) | |
fiber = Fiber.new do | |
payload = Fiber.yield fetch_customer(token, params) | |
payload = Fiber.yield fetch_studioa_vip(payload) | |
create_or_update_customer(payload) | |
end | |
# Ruby 的 fiber 不支援 for 或是其他迭帶性質的方法使用,只能自己循環下去,有一些麻煩 | |
while(fiber.alive?) | |
result = fiber.resume(result ||= nil) | |
(render json: result.payload, status: result.status and return) unless result.success? | |
end | |
render json: result.payload | |
end | |
protected | |
def query_studioa_vip(mobile_phone) | |
uri = URI(Settings.api_endpoint.query_vip) | |
json_parameters = { | |
"SYSPARAM": { | |
"DB_ID": "EPB", | |
"CHAR_SET": "eng", | |
"INCLUDE_NULL": "N" | |
}, | |
"PARAMETER": { | |
"VIP_PHONE": mobile_phone | |
} | |
} | |
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json') | |
req.body = json_parameters.to_json | |
Net::HTTP.start(uri.hostname, uri.port) do |http| | |
http.request(req) | |
end | |
end | |
def fetch_customer(token, params) | |
client = OpenApiClient.set(access_token: token).customers | |
customer = client.get("/#{params[:resource][:_id]}").json_body | |
mobile_phone, confirmed_at = customer.symbolize_keys.values_at(:mobile_phone, :confirmed_at) | |
if [mobile_phone, confirmed_at].all?(&:present?) | |
Result.create payload: { customer: customer, mobile_phone: mobile_phone, token: token } | |
else | |
Result.create payload: { message: "customer: #{customer['id']} is not signed up." }, status: :no_content, success: false | |
end | |
end | |
def fetch_studioa_vip(args = {}) | |
args = args.symbolize_keys | |
res = query_studioa_vip(args[:mobile_phone]) | |
if res['RESULT']['STATUS'].to_s != 'S' | |
Result.create(payload: args.merge(raw_customer: res.json_body['DATA'])) | |
else | |
Result.create(payload: { errors: res.json_body }, status: :bad_request, success: false) | |
end | |
end | |
def create_or_update_customer(args = {}) | |
args = args.symbolize_keys | |
service_result = (args[:raw_customer].present? ? UpdateCustomerService : CreateCustomerService).new(args).execute | |
if service_result.success? | |
Result.create payload: service_result.payload | |
else | |
Result.create payload: service_result.error, status: :bad_request | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment