ยินดีต้อนรับสู่ Ruby Magic article ใหม่! ตอนนี้เราจะดูว่า Ruby ใช้ syntactic sugar ทำให้ syntax บางส่วน expressive กว่าหรืออ่านง่ายกว่า ในท้ายเราจะรู้ว่า tricks บางอย่างของ Ruby ทำงานยังไงใต้ hood และเขียน methods ของเราเองที่ใช้ sugar นี้ได้อย่างไร
เวลาเขียน Ruby apps มัก interact กับ class attributes, arrays และ hashes แบบที่อาจรู้สึก non-standard เราจะ define methods เพื่อ assign attributes และ fetch values จาก array หรือ hash ยังไง?
Ruby ให้ syntactic sugar นิดหน่อยทำให้ method เหล่านี้ทำงานเมื่อเรียก ในโพสต์นี้เราจะสำรวจว่ามันทำงานยังไง
person1 = Person.new
person1.name = "John"
array = [:foo, :bar]
array[1] # => :bar
hash = { :key => :foo }
hash[:key] # => :foo
hash[:key] = :value
Syntactic Sugar คืออะไร?
Syntactic sugar หมายถึง ✨ magic ✨ นิดหน่อยที่ Ruby ให้คุณเขียนโค้ดอ่านง่ายและกระชับกว่า ใน Ruby หมายถึงการละสัญลักษณ์ บาง symbols, spaces หรือเขียน expression ด้วย helper บางอย่าง
Method Names
เริ่มจาก method names ใน Ruby เราใช้ characters และ special symbols หลากหลายสำหรับ method names ที่ไม่รองรับทั่วไปในภาษาอื่น หากเคยเขียน Rails app คุณน่าจะเจอ method save! นี่ไม่ได้เฉพาะ Rails แต่ demonstrate รองรับ character ! ใน Ruby method names
สิ่งเดียวกันใช้กับ symbols อื่นๆ เช่น =, [, ], ?, %, &, |, <, >, *, -, + และ /
การรองรับ characters เหล่านี้หมายถึงเราสามารถ incorporate เข้า method names เพื่อ explicit ว่ามันทำอะไร:
- Assigning attributes:
person.name = "foo" - Ask questions:
person.alive? - Call dangerous methods:
car.destroy! - Making objects act like something they're not:
car[:wheels]
Defining Attribute Methods
เมื่อ define attribute บน class ด้วย attr_accessor Ruby สร้าง reader และ writer method สำหรับ instance variable บน class
class Person
attr_accessor :name
end
person = Person.new
person.name = "John"
person.name # => "John"
ใต้ hood Ruby สร้าง 2 methods:
Person#nameสำหรับอ่าน attribute/instance variable บน class ใช้attr_readerPerson#name=สำหรับเขียน attribute/instance variable บน class ใช้attr_writer
ตอนนี้สมมติเราอยาก customize behavior นี้ เราจะไม่ใช้ attr_accessor helper และ define methods เอง
class AwesomePerson
def name
"Person name: #{@name}"
end
def name=(value)
@name = "Awesome #{value}"
end
end
person = AwesomePerson.new
person.name = "Jane"
person.name # => "Person name: Awesome Jane"
Method definition สำหรับ name= ประมาณวิธีเขียนเมื่อเรียก method person.name = "Jane" เราไม่ define spaces รอบ equals sign = และไม่ใช้ parentheses เมื่อเรียก method
Optional Parentheses และ Spaces
คุณอาจเห็นว่าใน Ruby parentheses optional บ่อยครั้ง เมื่อส่ง argument ให้ method เราไม่ต้อง wrap argument ใน parentheses () แต่ทำได้ถ้าอ่านง่ายกว่า
If-statement เป็นตัวอย่างดี ในหลายภาษา wrap expression ที่ if-statement evaluate ด้วย parentheses ใน Ruby อาจละได้
puts "Hello!" if (true) # With optional parentheses
puts "Hello!" if true # Without parentheses
สิ่งเดียวกันใช้กับ method definitions และ expressions อื่นๆ
def greeting name # Parentheses omitted
"Hello #{name}!"
end
greeting("Robin") # With parentheses
greeting "Robin" # Without parentheses
greeting"Robin" # Without parentheses and spaces
บรรทัดสุดท้ายอ่านยากแต่ทำงาน Parentheses และ spaces optional แม้ตอนเรียก methods
แค่ระวังอย่าละ parentheses และ spaces ทุกอย่าง บางอย่างช่วย Ruby เข้าใจว่า你是什么意思! เมื่อสงสัย ให้ wrap arguments ใน parentheses เพื่อคุณและ Ruby รู้ว่า argument ไหน屬于 method call ไหน
วิธีเรียก method ทั้งหมดด้านล่างรองรับทั้งหมด แต่เรามักละ parentheses และเพิ่ม spaces เพื่ออ่านง่ายขึ้น
# Previous method definition:
# def name=(value)
# @name = "Awesome #{value}"
# end
person.name = "Jane"
person.name="Jane"
person.name=("Jane") # That looks a lot like the method definition!
ตอนนี้เรา define custom attribute reader และ writer methods สำหรับ name attribute แล้ว เราสามารถ customize behavior ตามต้องการ และ perform transformations บน value โดยตรงตอน assign attribute แทนที่จะใช้ callbacks
Defining [ ] Methods
ต่อไปเราจะดู square bracket methods [ ] ใน Ruby มักใช้ fetch และ assign values ให้ Array indexes และ Hash keys
hash = { :foo => :bar, :abc => :def }
hash[:foo] # => :bar
hash[:foo] = :baz # => :baz
array = [:foo, :bar]
array[1] # => :bar
มาดูว่า methods นี้ define ยังไง เมื่อเรียก hash[:foo] เราใช้ Ruby syntactic sugar นิดหน่อยทำให้มันทำงาน อีกวิธีเขียนคือ:
hash = { :foo => :bar }
hash.[](:foo)
hash.[]=(:foo, :baz)
# or even:
hash.send(:[], :foo)
hash.send(:[]=, :foo, :baz)
เปรียบเทียบกับวิธีปกติ (hash[:foo] และ hash[:foo] = :baz) เราเห็นความแตกต่างบ้าง ในตัวอย่างแรก (hash.[](:foo)) Ruby ย้าย argument แรกเข้า square brackets (hash[:foo]) เมื่อเรียก hash.[]=(:foo, :baz) argument ที่สองส่งเป็น value hash[:foo] = :baz
รู้แล้วนี้ เราสามารถ define [ ] และ [ ]= methods เองได้แบบที่ Ruby เข้าใจ
class MyHash
def initialize
@internal_hash = {}
end
def [](key)
@internal_hash[key]
end
def []=(key, value)
@internal_hash[key] = value
end
end
ตอนนี้เรารู้ว่า methods นี้เป็น normal Ruby methods เราสามารถ apply logic เดียวกับ method อื่นๆ ได้ เราสามารถทำให้มันทำ weird things ได้ เช่น allow multiple keys ใน [ ] method
class MyHash
def initialize
@internal_hash = { :foo => :bar, :abc => :def }
end
def [](*keys)
@internal_hash.values_at(*keys)
end
end
hash = MyHash.new
hash[:foo, :abc] # => [:bar, :def]
สร้างของคุณเอง
ตอนนี้เรารู้ syntactic sugar ของ Ruby บ้าง เราสามารถ apply knowledge นี้สร้าง methods เอง เช่น custom writers, Hash-like classes และมากกว่า
คุณอาจประหลาดใจว่ากี่ gems define methods อย่าง square brackets methods ทำให้รู้สึกเหมือน Array หรือ Hash แต่จริงๆ ไม่ใช่ ตัวอย่างคือ set flash message ใน Rails app ด้วย flash[:alert] = "An error occurred" ใน AppSignal gem เราใช้บน Config class เป็น shorthand สำหรับ fetch configuration
นี่คือสรุปการดู syntactic sugar สำหรับ method definition และ calling ใน Ruby เรายินดีรู้ความคิดเห็นว่าชอบบทความนี้หรือไม่ มีคำถามอะไร และอยากอ่านเรื่องไหนต่อไป บอกเราที่ @AppSignal ได้เลย