Rails เป็นเฟรมเวิร์กขนาดใหญ่ และเติบโตขึ้นทุกปี สิ่งนี้ทำให้คุณสมบัติที่เป็นประโยชน์บางอย่างง่ายขึ้นโดยไม่มีใครสังเกตเห็น ในชุดนี้ เราจะมาดูฟังก์ชันที่ไม่ค่อยมีใครรู้จักซึ่งรวมอยู่ใน Rails สำหรับงานเฉพาะ
ในบทความแรกในชุดนี้ เราจะมาดูวิธีการเรียก Rails.env.test?
ใช้งานได้จริงภายใต้ประทุนโดยใช้ StringInquirer
. ที่ไม่ค่อยมีใครรู้จัก คลาสจาก ActiveSupport ก้าวไปอีกขั้น เราจะพูดถึง StringInquirer
ซอร์สโค้ดเพื่อดูว่ามันทำงานอย่างไร ซึ่ง (การแจ้งเตือนสปอยเลอร์) เป็นตัวอย่างง่ายๆ ของการเขียนโปรแกรมเมตาของ Ruby ผ่าน method_missing
พิเศษ วิธีการ
ตัวช่วย Rails.env
คุณอาจเคยเห็นโค้ดที่ตรวจสอบสภาพแวดล้อม Rails แล้ว:
if Rails.env.test?
# return hard-coded value...
else
# contact external API...
end
อย่างไรก็ตาม test?
ทำได้จริง และวิธีนี้มาจากไหน
ถ้าเราตรวจสอบ Rails.env
ในคอนโซลจะทำหน้าที่เหมือนสตริง:
=> Rails.env
"development"
สตริงที่นี่คือตัวแปรสภาพแวดล้อม RAILS*ENV ที่ตั้งค่าไว้ มันไม่ใช่ _just* สตริงแม้ว่า เมื่ออ่านชั้นเรียนในคอนโซล เราจะเห็น:
=> Rails.env.class
ActiveSupport::StringInquirer
Rails.env
จริงๆ แล้วเป็น StringInquirer
StringInquirer
รหัสสำหรับ StringInquirer มีทั้งแบบสั้นและจัดทำเป็นเอกสารอย่างดี วิธีการทำงานอาจไม่ชัดเจน เว้นแต่คุณจะคุ้นเคยกับความสามารถในการเขียนโปรแกรมเมตาของ Ruby เราจะพูดถึงสิ่งที่เกิดขึ้นที่นี่
class StringInquirer < String
...
end
อันดับแรก เราจะเห็นว่า StringInquirer เป็นคลาสย่อยของ String นี่คือเหตุผลที่ Rails.env
ทำหน้าที่เหมือนสตริงเมื่อเราเรียกมันว่า เมื่อรับค่าจาก String เราจะได้ฟังก์ชันที่เหมือน String ทั้งหมดโดยอัตโนมัติ ดังนั้นเราจึงจัดการได้เหมือนกับ String Rails.env.upcase
ทำงานเช่นเดียวกับ ActiveRecordModel.find_by(string_column: Rails.env)
.
ขณะที่ Rails.env
เป็นตัวอย่างที่มีประโยชน์ในตัวของ StringInquirer เรามีอิสระที่จะสร้างของเราเอง:
def type
result = "old"
result = "new" if @new
ActiveSupport::StringInquirer.new(result)
end
จากนั้น เราได้รับเมธอดเครื่องหมายคำถามสำหรับค่าที่ส่งคืน:
=> @new = false
=> type.old?
true
=> type.new?
false
=> type.testvalue?
false
=> @new = true
=> type.new?
true
=> type.old?
false
method_missing
method_missing
คือซอสสูตรลับของที่นี่ ใน Ruby ทุกครั้งที่เราเรียกใช้เมธอดบนอ็อบเจ็กต์ Ruby จะเริ่มจากการดูบรรพบุรุษทั้งหมดของอ็อบเจกต์ (เช่น คลาสพื้นฐานหรือโมดูลที่รวมอยู่) สำหรับเมธอด หากไม่พบ Ruby จะเรียก method_missing
, ส่งผ่านชื่อของเมธอดที่กำลังมองหา พร้อมกับอาร์กิวเมนต์ใดๆ
โดยค่าเริ่มต้น วิธีการนี้จะทำให้เกิดข้อยกเว้น เราทุกคนคงคุ้นเคยกับ NoMethodError: undefined method 'test' for nil:NilClass
ประเภทของข้อความแสดงข้อผิดพลาด เราสามารถใช้ method_missing
. ของเราเองได้ ที่ไม่ทำให้เกิดข้อยกเว้น ซึ่งเป็นสิ่งที่ StringInquirer
ทำ:
def method_missing(method_name, *arguments)
if method_name.end_with?("?")
self == method_name[0..-2]
else
super
end
end
สำหรับ ใดๆ ชื่อเมธอดที่ลงท้ายด้วย ?
เราตรวจสอบค่าของ self
(เช่น สตริง) กับชื่อเมธอดที่ไม่มี ?
. กล่าวอีกนัยหนึ่งถ้าเราเรียก StringInquirer.new("test").long_test_method_name?
ค่าที่ส่งคืนคือ "test" == "long_test_method_name"
.
หากชื่อเมธอด ไม่ ลงท้ายด้วยเครื่องหมายคำถาม เราถอยกลับไปที่ method_missing
เดิม (อันที่จะยกข้อยกเว้น)
กำลังตอบกลับ?
มีอีกวิธีหนึ่งในไฟล์:respond_to_missing?
. เราอาจกล่าวได้ว่านี่เป็นวิธีการร่วมในการ method_missing
. แม้ว่า method_missing
ให้ฟังก์ชันการทำงานแก่เรา เราต้องการวิธีบอก Ruby ว่าเรายอมรับวิธีการสิ้นสุดเครื่องหมายคำถามเหล่านี้
def respond_to_missing?(method_name, include_private = false)
method_name.end_with?("?") || super
end
สิ่งนี้จะมีผลถ้าเราเรียก respond_to?
บนวัตถุนี้ ถ้าไม่มีสิ่งนี้ ถ้าเราเรียก StringInquirer.new("test").respond_to?(:test?)
ผลลัพธ์จะเป็น false
เพราะเราไม่มีวิธีการที่ชัดเจนที่เรียกว่า test?
. สิ่งนี้ทำให้เข้าใจผิดอย่างเห็นได้ชัดเพราะฉันคาดหวังว่ามันจะกลับเป็นจริงถ้าฉันเรียก Rails.env.respond_to?(:test?)
.
respond_to_missing?
คือสิ่งที่ทำให้เราพูดว่า "ใช่ ฉันจัดการวิธีนั้นได้" กับ Ruby หากชื่อเมธอดไม่ได้ลงท้ายด้วยเครื่องหมายคำถาม เราจะถอยกลับไปที่เมธอด superclass
กรณีใช้งานจริง
ตอนนี้เรารู้แล้วว่า อย่างไร StringInquirer ใช้งานได้ ลองดูตัวอย่างที่อาจมีประโยชน์:
1. ตัวแปรสภาพแวดล้อม
ตัวแปรสภาพแวดล้อมตรงตามเกณฑ์สองข้อที่ทำให้พวกเขาเป็นตัวเลือกที่ยอดเยี่ยมสำหรับ StringInquirer ประการแรก พวกเขามักจะมีชุดค่าที่เป็นไปได้ที่จำกัดและเป็นที่รู้จัก (เช่น enum
) และอย่างที่สอง เรามักจะมีตรรกะตามเงื่อนไขที่ขึ้นอยู่กับค่าของมัน
สมมติว่าแอปของเราเชื่อมต่อกับ API การชำระเงิน และเรามีข้อมูลประจำตัวที่จัดเก็บไว้ในตัวแปรสภาพแวดล้อม ในระบบการผลิตของเรา เห็นได้ชัดว่านี่เป็น API จริง แต่ในเวอร์ชันการจัดเตรียมหรือเวอร์ชันการพัฒนา เราต้องการใช้ Sandbox API:
# ENV["PAYMENT_API_MODE"] = sandbox/production
class PaymentGateway
def api_mode
# We use ENV.fetch because we want to raise if the value is missing
@api_mode ||= ENV.fetch("PAYMENT_API_MODE").inquiry
end
def api_url
# Pro-tip: We *only* use production if MODE == 'production', and default
# to sandbox if the value is anything else, this prevents us using production
# values if the value is mistyped or incorrect
if api_mode.production?
PRODUCTION_URL
else
SANDBOX_URL
end
end
end
โปรดทราบว่าในข้างต้น เรากำลังใช้ String#inquiry
. ของ ActiveSupport เมธอดซึ่งแปลง String เป็น StringInquirer สำหรับเราอย่างสะดวก
2. การตอบสนองของ API
ต่อจากตัวอย่าง API การชำระเงินของเราจากด้านบน API จะส่งการตอบกลับที่มีสถานะสำเร็จ/ล้มเหลว อีกครั้ง สิ่งนี้ตรงตามเกณฑ์สองข้อที่ทำให้เป็นตัวเลือกสำหรับ StringInquirer:ชุดค่าที่เป็นไปได้ที่จำกัดและตรรกะแบบมีเงื่อนไขที่จะทดสอบค่าเหล่านี้
class PaymentGateway
def create_charge
response = JSON.parse(api_call(...))
result = response["result"].inquiry
if result.success?
...
else
...
end
# result still acts like a string
Rails.logger.info("Payment result was: #{result}")
end
end
บทสรุป
StringInquirer เป็นเครื่องมือที่น่าสนใจที่จะมีไว้ในกระเป๋าหลังของคุณ แต่โดยส่วนตัว ฉันจะไม่หยิบมันบ่อยเกินไป มันมีประโยชน์บางอย่าง แต่โดยส่วนใหญ่ วิธีการที่ชัดเจนบนวัตถุจะให้ผลลัพธ์แบบเดียวกัน วิธีการที่มีชื่ออย่างชัดเจนก็มีประโยชน์สองสามอย่าง หากจำเป็นต้องเปลี่ยนค่า คุณต้องอัปเดตที่เดียวเท่านั้น และ มันทำให้ค้นหาโค้ดเบสได้ง่ายขึ้นหากนักพัฒนาพยายามค้นหาวิธีการ
แม้ว่าบทความนี้จะเน้นไปที่ StringInquirer แต่บทความนี้มีวัตถุประสงค์เพื่อเป็นการแนะนำเบื้องต้นเกี่ยวกับความสามารถในการเขียนโปรแกรมเมตาของ Ruby โดยใช้ method_missing
. ฉันจะบอกว่าคุณอาจไม่ต้องการใช้ method_missing
ในใบสมัครของคุณ อย่างไรก็ตาม มักใช้ในเฟรมเวิร์ก เช่น Rails หรือภาษาเฉพาะโดเมนที่ให้บริการโดย gem ดังนั้นจึงเป็นประโยชน์มากที่จะรู้ว่า "วิธีทำไส้กรอก" เมื่อคุณประสบปัญหา