Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

3 วิธี Config Ruby API Wrappers ที่พิสูจน์แล้ว

ตอน用 Ruby wrap API ต้องมีวิธี config อาจต้องการ username และ secret key หรือแค่ host

มีหลายวิธีจัดการ 选哪个?

วิธีง่าย Global

อาจอยากให้ service รู้สึกว่ามีอยู่เสมอ ไม่ว่าจะอยู่ไหนใน app พร้อม用ทันที ไม่งั้นจะ config 3 บรรทัดต่อ 1 บรรทัดที่用!

ทำ config เป็น global ด้วย constants หรือ class attributes:

config/initializers/product_api.rb
ProductApi.root = "https://staging-host.example.com/" ProductApi.user = "justin" ProductApi.secret = "mysecret123"
app/controllers/products_controller.rb
def show @product = ProductApi.find(params[:id]) end

Gems เยอะ用 pattern นี้ เขียนง่าย 用ง่าย แตมีปัญหาใหญ่:

  • มีได้แค่หนึ่ง ProductApi

    อยาก用 Product API เป็น user คนละคน หรือ hit servers ต่างๆ จาก app เดียว ไม่ได้

  • ProductApi มี global data ที่เปลี่ยนง่ายโดยไม่ได้ตั้งใจ

    ถ้า thread หรือส่วนของ app เปลี่ยน ProductApi.user ทุกอย่างที่用 ProductApi จะพัง และ bug แบบนี้ ติดตามยากมาก

Class variables มีปัญหา ถ้า config instances ของ Product API class แทนล่ะ?

长什么样用 #initialize?

ถ้า用 instances สร้างและ config API wrapper ตอนต้องการ:

app/controllers/products_controller.rb
def show product_api = ProductApi.new( root: "https://staging-host.example.com/", user: "justin", secret: "mysecret123") @product = product_api.find(params[:id]) end

ตอนนี้ส่งรายละเอียดต่างๆ ไปให้ API ตอน用ได้ ไม่มี method หรือ thread อื่น用 instance นี้ ไม่กังวลเปลี่ยนโดยไม่รู้

ดูดีกว่า แตยังไม่สะดวกพอ เพราะต้อง config API ทุกครั้ง ที่用

ส่วนใหญ่ไม่สนใจ API setup ยังไง แค่用กับ sane options แตตอน用 instances ทุกส่วนของ app ที่用 API ต้องรู้ setup ยังไง

แตมีวิธีได้ convenience ของ global access ด้วย good defaults ยังสามารถเปลี่ยนได้

Pattern นี้ปรากฏบ่อยในที่น่าสนใจ: OS X และ iOS development

ได้ Good Defaults และ Flexibility ยังไง?

ถ้า config แต่ละ instance ของ API wrapper ได้ และยังมี global "default" instance ตอนไม่สนใจ?

Pattern "defaultSomething" หรือ "sharedWhatever" เห็นทั่ว iOS และ Mac OS SDKs:

[[NSURLSession sharedSession] downloadTaskWithURL:@"https://www.google.com"]; [[NSFileManager defaultManager] removeItemAtPath:...];

และยังขอ instances ได้ถ้าต้องการมากกว่า default:

NSURLSession *session = [NSURLSession sessionWithConfiguration:...]; NSFileManager fileManager = [[NSFileManager alloc] init];

สร้างใน Ruby ด้วย class method default_api:

app/controllers/products_controller.rb
def show @product = ProductApi.default_product_api.find(params[:id]) end ... def show_special special_product_api = ProductApi.new( root: "https://special-product-host.example.com/" user: "justin" secret: "mysecret123") @special_product = special_product_api.find(params[:id]) end

Implementation อาจเป็นแบบนี้:

class ProductApi def initialize(root:, user:, secret:) @root, @user, @secret = root, user, secret end def self.default_api @default_api ||= new( root: ENV['PRODUCT_API_ROOT'], user: ENV['PRODUCT_API_USER'], secret: ENV['PRODUCT_API_SECRET']) end def find(product_id) ... end end

ที่นี่用 environment variables ใน default_api แต用 config files ได้ และเปลี่ยน ||= ให้用 thread- หรือ request-local storage แทน

แตเป็นจุดเริ่มที่ดี

Gems เยอะ เช่น Twitter gem จะให้ config และสร้าง API object ตอนต้องการ ใช้ได้ (แตเห็นมากว่ามัน assign ไป globals อยู่แล้ว)

แตถ้าไปอีกขั้น ใช้ pre-configured default object ด้วย จะสบายใจมากกว่า