การปล่อย Ruby ใหม่ในวันคริสต์มาสกลายเป็นประเพณีแล้ว
ในบทความนี้ ผมอยากครอบคลุมการเปลี่ยนแปลงน่าสนใจใน Ruby 2.4 เพื่อให้คุณทันข่าวสาร 🙂
Float#round รองรับ keyword argument
ถ้าคุณใช้ float ในแอป ผมหวังว่าคุณใช้ floor หรือ ceil สำหรับการปัดเศษ เพราะพฤติกรรมเริ่มต้นของ Float#round เปลี่ยนไปใน Ruby 2.4
ตัวอย่าง:
# Ruby 2.3
(2.5).round # 3
# Ruby 2.4
(2.5).round # 2
พฤติกรรมเริ่มต้นตอนนี้คือ "round-to-nearest-even" (ปัดเป็นเลขคู่ที่ใกล้ที่สุด)
อัปเดต: พฤติกรรมเริ่มต้นของ
Float#roundกลับมาเป็น "ปัดขึ้น" ใน Ruby 2.4 เวอร์ชันสุดท้าย นี่คือการตัดสินใจของ Matz หลังจากบทความนี้เผยแพร่ไปแล้ว
เพิ่มเติม Float#round ตอนนี้รับอาร์กิวเมนต์ keyword เพื่อกำหนดประเภทการปัดเศษ:
:even:up:down
ตัวอย่าง:
(4.5).round(half: :up) # 5
นอกจากนี้ Float#floor, Float#ceil และ Float#truncate ตอนนี้รับ optional argument เพื่อกำหนดความแม่นยำ (precision)
Flag chomp สำหรับเมธอด IO
ถ้าคุณเคยใช้เมธอดอย่าง gets หรือ each_line คุณคงจำได้ว่าต้องจัดการกับตัวอักษรขึ้นบรรทัดใหม่ (\n) ที่รบกวน
นี่คือสิ่งที่มือใหม่มักมีปัญหา แต่ฟีเจอร์ใหม่นี้อาจช่วยได้!
ตัวอย่างเดิม:
input = gets.chomp # "abc\n"
ตอนนี้ใน Ruby 2.4 คุณสามารถตั้ง keyword argument chomp ให้ gets ตัดตัวอักษรขึ้นบรรทัดใหม่ให้อัตโนมัติ
ตัวอย่าง:
input = gets(chomp: true) # "abc"
ทำงานกับค่าใดๆ ที่ไม่ใช่ false หรือ nil (ค่า "falsy" เพียงสองอย่างใน Ruby)
ดังนั้นนี้ก็ทำงานได้ (แต่ไม่แนะนำเพราะสับสน):
input = gets(chomp: 1234) # "abc"
ไม่ได้เป็นเรื่องใหญ่ แต่ประหยัดการเรียกเมธอดหนึ่งครั้ง 🙂
Pathname#empty?
Ruby 2.4 เพิ่ม Dir#empty? และ File#empty? สำหรับตรวจสอบว่าไดเรกทอรีหรือไฟล์ว่างเปล่า
แต่ Pathname#empty? ก็ถูกเพิ่มเข้ามาเช่นกัน
กรณีที่คุณไม่คุ้นเคยกับ Pathname มันคือคลาสที่รวมฟังก์ชันของ Dir และ File ไว้ด้วยกัน
เพิ่มเติม มัน "OO" (Object Oriented) มากกว่า เพราะคืนค่าเป็นออบเจ็กต์ Pathname แทนที่จะเป็นสตริง
ตัวอย่าง:
Pathname.empty?("file or directory name")
Commit: https://github.com/ruby/ruby/commit/9373c5efb993dd8cae0526118805449b19af2c22
Set#compare_by_identity
Set เป็นโครงสร้างข้อมูลใน standard library ช่วยเก็บคอลเลกชันของไอเทมที่ไม่ซ้ำกัน
โดยค่าเริ่มต้น ออบเจ็กต์ถูกเปรียบเทียบจากค่า (หรือแม่นยำกว่าคือ hash value)
แต่ใน Ruby 2.4 สามารถมี Set ของออบเจ็กต์ที่ไม่ซ้ำกันตาม object id ได้
ตัวอย่าง:
require 'set'
# Set ปกติ
set = Set.new
set << 123 << 123 << "abc" << "abc"
# [123, "abc"]
# Identity set
set = Set.new.compare_by_identity
set << 123 << 123 << "abc" << "abc"
# [123, "abc", "abc"]
ถ้าใครรู้ use case น่าสนใจของฟีเจอร์นี้ ลองคอมเมนต์บอกกัน 🙂
Commit: https://github.com/ruby/ruby/commit/76977611dd68e384fdce8c546efda5e1931e67a6
Refinements รองรับ Kernel#send, BasicObject#__send__, Symbol#to_proc
ผมไม่ได้ลงลึกเรื่อง refinements ที่นี่ แต่อย่างง่ายคือการเพิ่มเมธอดให้คลาสอย่าง String พร้อมเก็บการเปลี่ยนแปลงไว้ "เฉพาะที่" (localized) ในไฟล์หรือคลาสเดียว
ตั้งแต่ Ruby 2.4 เมธอดที่กำหนดผ่าน refinements จะเข้าถึงได้เมื่อเรียกผ่าน Kernel#send และ Symbol#to_proc
ตัวอย่าง:
module TenTimes
refine String do
def ten_times
puts self * 10
end
end
end
class Thing
using TenTimes
"abc".send(:ten_times)
end
ถ้าลองใน 2.3 หรือต่ำกว่า จะได้ error 'undefined method'
Commit: https://github.com/ruby/ruby/commit/35a29390197750abf97ef16fa0740e377764daef
Hash#transform_values
อีกเมธอดหนึ่งที่มาจาก Rails เข้าสู่ Ruby core คือ Hash#transform_values ทำงานคล้าย Array#map
ตัวอย่าง:
h = {a: 1, b: 2, c: 3}
h.transform_values { |v| v * 10 } # {a: 10, b: 20, c: 30}
ยังมี Hash#transform_values! สำหรับการ mutate แบบ in-place
Kernel#clone รับ keyword argument ทางเลือก
คุณรู้ไหมว่าสามารถ copy ออบเจ็กต์ Ruby ได้? มีประโยชน์เพราะออบเจ็กต์ส่วนใหญ่เป็น mutable และคุณอาจอยากหลีกเลี่ยงการเปลี่ยนแปลงออบเจ็กต์ต้นฉบับ
มีสองเมธอดสำหรับ copy:
clonedup
มีความต่างเล็กน้อยระหว่าง clone และ dup แต่สรุปง่ายๆ คือ clone เก็บสถานะ "frozen" ของออบเจ็กต์ต้นฉบับ ส่วน dup ไม่เก็บ
ใหม่ใน 2.4 คือ เรียก clone พร้อม flag "freeze" ได้
ตัวอย่าง:
foo = "test".freeze
boo = foo.clone(freeze: false)
boo.frozen? # false
ไม่แน่ใจว่ามีประโยชน์มากแค่ไหน แต่มีตัวเลือกมากกว่าไม่ได้ทำอะไร 🙂
Commit: https://github.com/ruby/ruby/commit/320ae01c5fb091eab0926c186f304a9caeda1ace
Thread.report_on_exception
ฟีเจอร์ใหม่อีกอย่างคือ Thread.report_on_exception 提出มากเพราะข้อยกเว้นในเธรดเป็น silent โดยค่าเริ่มต้น ซึ่งอาจซ่อนปัญหาในโค้ดของคุณ
ค่าเริ่มต้นคือ false แต่ถ้าแอปของคุณใช้เธรด ควรลองเปิดใช้ตอนอัปเกรดเป็น Ruby 2.4
ตัวอย่าง:
Thread.report_on_exception = true
t1 = Thread.new do
puts "In new thread"
raise "Exception from thread"
end
sleep(1)
puts "In the main thread"
จะแสดงข้อยกเว้น แต่ไม่ทำให้โปรแกรม crash ตัวเลือกอีกอย่างคือ Thread.abort_on_exception ที่มีมาตลอด
สังเกตว่ามี instance version ด้วย จึงตั้งค่าได้ต่อเธรด ไม่ใช่ทั้งหมด
Binding#irb
คุณแฟนของ binding.pry สำหรับ debug ไหม? ตอนนี้มี binding.irb ที่ทำงานคล้ายกัน
แต่มันเป็น irb จึงไม่ได้ฟีเจอร์ดีๆ อย่าง syntax highlighting ของ pry
ดีกว่าไม่มีอะไรเลย ถ้าคุณไม่มี pry ใช้งาน 🙂
Commit: https://github.com/ruby/ruby/commit/493e48897421d176a8faf0f0820323d79ecdf94a
สรุป
Ruby เวอร์ชันใหม่นี้นำฟีเจอร์น่าสนใจหลายอย่างมา อย่าลืมลองดูตอนพร้อมใช้งาน
อย่าลืม แชร์บทความนี้ ให้เพื่อนๆ รู้จักฟีเจอร์ใหม่ใน Ruby 2.4!