Chào mừng bạn đến với một bài viết mới của Ruby Magic! Trong tập này, chúng ta sẽ xem xét cách Ruby sử dụng đường cú pháp để làm cho một số cú pháp của nó biểu cảm hơn hoặc dễ đọc hơn. Cuối cùng, chúng ta sẽ biết một số thủ thuật của Ruby hoạt động như thế nào và cách viết các phương thức của riêng chúng ta sử dụng một chút đường này.
Khi viết ứng dụng Ruby, người ta thường tương tác với các thuộc tính lớp, mảng và hàm băm theo cách có thể cảm thấy không chuẩn. Chúng ta sẽ xác định các phương thức để gán các thuộc tính và tìm nạp các giá trị từ một mảng hoặc hàm băm như thế nào?
Ruby cung cấp một chút cú pháp để làm cho phương thức này hoạt động khi gọi chúng. Trong bài đăng này, chúng ta sẽ khám phá cách hoạt động của nó.
person1 = Person.new
person1.name = "John"
array = [:foo, :bar]
array[1] # => :bar
hash = { :key => :foo }
hash[:key] # => :foo
hash[:key] = :value
Đường cú pháp?
Đường cú pháp đề cập đến một chút ma thuật ✨ ✨ Ruby cung cấp cho bạn cách viết mã dễ đọc hơn và ngắn gọn hơn. Trong Ruby, điều này có nghĩa là bỏ đi một số ký hiệu, khoảng trắng nhất định hoặc viết một số biểu thức với một trình trợ giúp nào đó.
Tên phương thức
Hãy bắt đầu với tên phương thức. Trong Ruby, chúng ta có thể sử dụng tất cả các loại ký tự và ký hiệu đặc biệt cho các tên phương thức không được hỗ trợ phổ biến trong các ngôn ngữ khác. Nếu bạn đã từng viết một ứng dụng Rails, bạn có thể gặp phải save!
phương pháp. Đây không phải là thứ dành riêng cho Rails, nhưng nó thể hiện sự hỗ trợ cho !
ký tự trong tên phương thức Ruby.
Điều tương tự cũng áp dụng cho các ký hiệu khác như =
, [
, ]
, ?
, %
, &
, |
, <
, >
, *
, -
, +
và /
.
Hỗ trợ cho những ký tự này có nghĩa là chúng tôi có thể kết hợp chúng vào tên phương thức của mình để rõ ràng hơn về những gì chúng dùng để làm:
- Gán các thuộc tính:
person.name = "foo"
- Đặt câu hỏi:
person.alive?
- Gọi các phương thức nguy hiểm:
car.destroy!
- Làm cho các đối tượng hoạt động như một thứ gì đó không giống như chúng:
car[:wheels]
Xác định các phương thức thuộc tính
Khi xác định một thuộc tính trên một lớp với attr_accessor
, Ruby tạo một trình đọc và một phương thức viết cho một biến thể hiện trên lớp.
class Person
attr_accessor :name
end
person = Person.new
person.name = "John"
person.name # => "John"
Về cơ bản, Ruby tạo ra hai phương thức:
-
Person#name
để đọc biến thuộc tính / cá thể trên lớp bằng cách sử dụngattr_reader
và; -
Person#name=
để viết biến thuộc tính / cá thể trên lớp bằng cách sử dụngattr_writer
.
Bây giờ giả sử chúng tôi muốn tùy chỉnh hành vi này. Chúng tôi sẽ không sử dụng attr_accessor
trợ giúp và tự xác định các phương pháp.
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"
Định nghĩa phương thức cho name=
gần giống như cách bạn viết khi gọi phương thức person.name = "Jane"
. Chúng tôi không xác định các khoảng trắng xung quanh dấu bằng =
và không sử dụng dấu ngoặc đơn khi gọi phương thức.
Dấu ngoặc đơn và khoảng trắng tùy chọn
Bạn có thể đã thấy rằng trong Ruby, dấu ngoặc đơn là tùy chọn thường xuyên. Khi truyền đối số cho một phương thức, chúng ta không phải đặt đối số trong dấu ngoặc đơn ()
, nhưng chúng ta có thể làm được nếu nó dễ đọc hơn.
Câu lệnh if là một ví dụ điển hình. Trong nhiều ngôn ngữ, bạn bao bọc biểu thức, câu lệnh if đánh giá bằng dấu ngoặc đơn. Trong Ruby, chúng có thể được bỏ qua.
puts "Hello!" if (true) # With optional parentheses
puts "Hello!" if true # Without parentheses
Điều tương tự cũng áp dụng cho các định nghĩa phương thức và các biểu thức khác.
def greeting name # Parentheses omitted
"Hello #{name}!"
end
greeting("Robin") # With parentheses
greeting "Robin" # Without parentheses
greeting"Robin" # Without parentheses and spaces
Dòng cuối cùng rất khó đọc, nhưng nó hoạt động. Dấu ngoặc đơn và khoảng trắng là tùy chọn ngay cả khi gọi các phương thức.
Chỉ cần lưu ý không bỏ qua mọi dấu ngoặc đơn và khoảng trắng, một số trong số này sẽ giúp Ruby hiểu ý bạn! Khi nghi ngờ, hãy đặt các đối số của bạn trong dấu ngoặc đơn để bạn và Ruby biết đối số nào thuộc về lệnh gọi phương thức nào.
Tất cả các cách gọi phương thức sau đây đều được hỗ trợ, nhưng chúng tôi thường bỏ qua dấu ngoặc đơn và thêm dấu cách để làm cho mã dễ đọc hơn một chút.
# 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!
Bây giờ chúng tôi đã xác định các phương thức trình đọc và ghi thuộc tính tùy chỉnh cho name
thuộc tính. Chúng tôi có thể tùy chỉnh hành vi khi cần thiết và thực hiện chuyển đổi trực tiếp trên giá trị khi gán thuộc tính thay vì phải sử dụng lệnh gọi lại.
Xác định [ ]
phương pháp
Điều tiếp theo chúng ta sẽ xem xét các phương thức dấu ngoặc vuông [ ]
bằng Ruby. Chúng thường được sử dụng để tìm nạp và gán giá trị cho chỉ mục Mảng và khóa băm.
hash = { :foo => :bar, :abc => :def }
hash[:foo] # => :bar
hash[:foo] = :baz # => :baz
array = [:foo, :bar]
array[1] # => :bar
Hãy xem các phương thức này được định nghĩa như thế nào. Khi gọi hash[:foo]
chúng tôi đang sử dụng một số cú pháp Ruby để làm cho nó hoạt động. Một cách viết khác là:
hash = { :foo => :bar }
hash.[](:foo)
hash.[]=(:foo, :baz)
# or even:
hash.send(:[], :foo)
hash.send(:[]=, :foo, :baz)
So với cách chúng ta viết thông thường (hash[:foo]
và hash[:foo] = :baz
) chúng ta đã có thể thấy một số khác biệt. Trong ví dụ đầu tiên (hash.[](:foo)
) Ruby di chuyển đối số đầu tiên giữa các dấu ngoặc vuông (hash[:foo]
). Khi gọi hash.[]=(:foo, :baz)
đối số thứ hai được chuyển cho phương thức dưới dạng giá trị hash[:foo] = :baz
.
Biết được điều này, bây giờ chúng ta có thể xác định [ ]
của riêng mình và [ ]=
theo cách mà Ruby sẽ hiểu nó.
class MyHash
def initialize
@internal_hash = {}
end
def [](key)
@internal_hash[key]
end
def []=(key, value)
@internal_hash[key] = value
end
end
Bây giờ chúng ta biết các phương thức này là các phương thức Ruby bình thường, chúng ta có thể áp dụng logic tương tự cho chúng như bất kỳ phương thức nào khác. Chúng tôi thậm chí có thể khiến nó làm những điều kỳ lạ như cho phép nhiều khóa trong [ ]
phương pháp.
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ạo của riêng bạn
Bây giờ chúng ta đã biết một chút về đường cú pháp của Ruby, chúng ta có thể áp dụng kiến thức này để tạo các phương thức của riêng mình, chẳng hạn như trình viết tùy chỉnh, các lớp giống như Hash và hơn thế nữa.
Bạn có thể ngạc nhiên khi có nhiều gem định nghĩa các phương thức như các phương thức trong dấu ngoặc vuông để tạo cảm giác giống như Array hoặc Hash trong khi nó thực sự không phải như vậy. Một ví dụ là đặt thông báo flash trong ứng dụng Rails với:
flash[:alert] = "An error occurred"
. Trong đá quý AppSignal, chúng tôi tự sử dụng nó trên Config
lớp dưới dạng viết tắt để tìm nạp cấu hình.
Điều này kết thúc cái nhìn ngắn gọn của chúng ta về đường cú pháp để định nghĩa và gọi phương thức trong Ruby. Chúng tôi muốn biết bạn thích bài viết này như thế nào, nếu bạn có bất kỳ câu hỏi nào về nó và những gì bạn muốn đọc tiếp theo, vì vậy hãy nhớ cho chúng tôi biết tại @AppSignal.