Chào mừng bài Ruby Magic mới! Episode này xem cách Ruby dùng syntactic sugar làm syntax dễ đọc, expressive hơn. Cuối bài, ta biết cách một số trick Ruby hoạt động under the hood và viết method dùng chút sugar này.
Khi viết Ruby app, thường interact với class attributes, arrays và hashes cách có vẻ non-standard. Làm sao define method assign attribute và fetch value từ array/hash?
Ruby cung cấp syntactic sugar làm method này work khi gọi. Bài này khám phá cách đó hoạt động.
person1 = Person.new person1.name = "John" array = [:foo, :bar] array[1] # => :bar hash = { :key => :foo } hash[:key] # => :foo hash[:key] = :valueSyntactic Sugar Là Gì?
Syntactic sugar指那些让代码更易读、更简洁的语法糖。在Ruby中,这意味着省略某些符号、空格或用helper写表达式。
Tên Method
Bắt đầu với tên method. Trong Ruby, ta dùng nhiều ký tự và special symbols cho method name không phổ biến trong ngôn ngữ khác. Nếu từng viết Rails app, bạn gặp method save!. Đây không specific Rails, mà demo support ký tự ! trong Ruby method name.
Tương tự áp dụng cho symbols khác: =, [, ], ?, %, &, |, <, >, *, -, + và /.
Support các ký tự này nghĩa là ta incorporate vào method name để explicit về mục đích:
- Assign attribute:
person.name = "foo" - Hỏi hỏi:
person.alive? - Gọi dangerous method:
car.destroy! - Làm object act như cái khác:
car[:wheels]
Define Attribute Methods
Khi define attribute trên class với attr_accessor, Ruby tạo reader và writer method cho instance variable trên class.
class Person attr_accessor :name end person = Person.new person.name = "John" person.name # => "John"Under the hood, Ruby tạo 2 method:
Person#nameđọc attribute/instance variable dùngattr_readerPerson#name=ghi attribute/instance variable dùngattr_writer
Giả sử ta muốn customize behavior. Không dùng attr_accessor helper, tự define method.
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 cho name= giống cách viết khi gọi method person.name = "Jane". Ta không define spaces quanh equals sign = và không dùng parentheses khi gọi method.
Parentheses và Spaces Tùy Chọn
Bạn thấy trong Ruby parentheses tùy chọn nhiều lúc. Khi pass argument cho method, không phải wrap argument trong parentheses (), nhưng có thể nếu dễ đọc hơn.
If-statement là ví dụ tốt. Nhiều language wrap expression if-statement evaluate với parentheses. Ruby có thể bỏ.
puts "Hello!" if (true) # With optional parentheses puts "Hello!" if true # Without parenthesesTương tự áp dụng cho method definitions và expressions khác.
def greeting name # Parentheses omitted "Hello #{name}!" end greeting("Robin") # With parentheses greeting "Robin" # Without parentheses greeting"Robin" # Without parentheses and spacesDòng cuối khó đọc nhưng work. Parentheses và spaces tùy chọn ngay cả khi gọi method.
Chỉ cẩn thận không bỏ mọi parentheses và space, một số giúp Ruby hiểu ý bạn! Khi nghi ngờ, wrap arguments trong parentheses để bạn và Ruby biết argument thuộc method call nào.
Tất cả cách gọi method sau supported, nhưng thường bỏ parentheses và thêm spaces cho readable.
# 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!Ta đã define custom attribute reader và writer cho attribute name. Có thể customize behavior và transform value trực tiếp khi assign attribute thay vì dùng callbacks.
Define [ ] Methods
Điều tiếp theo xem là square bracket methods [ ] trong Ruby. Thường dùng fetch và assign value cho Array indexes và Hash keys.
hash = { :foo => :bar, :abc => :def } hash[:foo] # => :bar hash[:foo] = :baz # => :baz array = [:foo, :bar] array[1] # => :barXem cách define method này. Khi gọi hash[:foo] ta dùng Ruby syntactic sugar làm nó work. Cách viết khác:
hash = { :foo => :bar } hash.[](:foo) hash.[]=(:foo, :baz) # or even: hash.send(:[], :foo) hash.send(:[]=, :foo, :baz)So với cách viết thường (hash[:foo] và hash[:foo] = :baz) đã thấy khác biệt. Ví dụ đầu (hash.[](:foo)) Ruby move first argument giữa square brackets (hash[:foo]). Khi gọi hash.[]=(:foo, :baz) argument thứ 2 pass as value hash[:foo] = :baz.
Biết này, ta define own [ ] và [ ]= methods cách Ruby hiểu.
class MyHash def initialize @internal_hash = {} end def [](key) @internal_hash[key] end def []=(key, value) @internal_hash[key] = value end endBây giờ biết method này là normal Ruby method, ta apply logic giống method khác. Có thể làm điều kỳ lạ như allow multiple keys trong [ ] 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]Tự Tạo Của Bạn
Bây giờ biết chút về Ruby syntactic sugar, ta apply knowledge tạo own methods như custom writers, Hash-like classes và hơn.
Bạn có thể surprised bao nhiêu gems define methods như square brackets để cái gì đó feel như Array/Hash khi thực ra không. Ví dụ set flash message trong Rails app: flash[:alert] = "An error occurred". Trong gem AppSignal, ta dùng trên class Config như shorthand fetch configuration.
Kết thúc brief look tại syntactic sugar cho method definition và calling trong Ruby. Chúng tôi rất muốn biết bạn thấy bài này như thế nào, nếu có câu hỏi, và bạn muốn đọc gì tiếp theo, hãy cho biết @AppSignal.