Bài viết của tác giả yongsokheng trên viblo.asia.
Giới thiệu
Ruby là một ngôn ngữ lập trình rất phổ biển, nhất với các Ruby on Rails developer. Đối với những người mới bắt đầu, chắc sẽ khá nhiều vấn đề và sự nhầm lẫn trong khi lập trình Ruby.
Sau đây, mình sẽ giới thiệu các kiến thức quan trọng và phổ biến dành cho các Ruby developer ai cũng phải biết.
Instance Variables
Đối với các ngôn ngữ lập trình khác, instance viarables phải được khái báo trước khi nó được assign giá trị. Nhưng trong Ruby, instance variable không cần phải khái báo, nó sẽ tồn tại trong khi nó đã được assign giá trị đầu tiên.
Để tạo instance variable trong Ruby là chỉ cần thêm @
ở trước các local variables.
1 2 3 4 5 6 7 |
class Item def initialize(title) @title = title // instance variable @title end end |
Để có thể truy cập đến các instance variable từ bên ngoài, phải sử dụng getter và setter method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Item def title=(t) @title = t end def title @title end end item = Item.new puts item.title.inspect # => nil item.title = "Chia Ruby" puts item.title # => "Chia Ruby" |
Ví dụ trên constructor initialize
được xoá đi để chứng minh rằng hàm này là optional.
Sử dụng getters và setters càng nhiều và lặp đi lặp lại nhiều lẫn sẽ làm cho code rất dài và không đẹp. Để tránh việc lặp, Ruby cung cấp 3 helper methods sau đây:
#attr_reader
– define instance-level getters#attr_writer
– define instance-level setters#attr_accessor
– define both
Thông thường, các helper method trên thường để ở trên cùng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Thing attr_accessor :foo, :bar attr_reader :baz def initialize @baz = "cat" end end thing = Thing.new thing.foo = 1 thing.bar = 2 puts thing.baz # => "cat" puts thing.foo # => 1 puts thing.bar # => 2 |
Modules
Trong Ruby, module
là container của các methods. Còn class
là một loại module đặc biệt có khả năng tạo ra các instances
và có ancestors
(tổ tiên).
1 2 3 4 5 6 7 |
module MyModule def hello puts "hello from instance" end end |
1 2 3 4 5 6 7 8 9 10 |
class MyClass def hello puts "hello from instance" end end instance = MyClass.new instance.hello # => "hello from instance" |
Methods ở trong module có thể là instance hoặc class methods.
1 2 3 4 5 6 7 8 |
module MyModule def self.hello # hoặc def MyModule.hello puts 'hello from module' end end MyModule.hello # => "hello from module" |
Module không thể tạo các instances nhưng module có thể có instance variables như trong class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module Fooable def self.foo=(value) @foo = value end def self.foo @foo end end Fooable.foo = "baz" puts Fooable.foo # => "baz" |
Các methods hoặc instance variable trong module sẽ được gọi ở trong các class khác với mục đích tài sử dụng code.
Để dùng được ở trong class khác, chúng ta sẽ sử dụng mixins
.
Mixins
Có 2 cách để mix module vào trong class.
#include
– chuyển methods của module thành instance methods.#extend
– chuyển methods của module thành class methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
module Helloable def hello puts "Hello World" end end class IncludeClass include Helloable end class ExtendClass extend Helloable end IncludeClass.new.hello ExtendClass.hello |
Module Gotchas
- Nếu 2 modules define method giống nhau, module thứ 2 include vào sẽ được dùng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module Module1 def hello "hello from module 1" end end module Module2 def hello "hello from module 2" end end class HelloClass include Module1 include Module2 end HelloClass.new.hello # => "hello from module 2" |
- Nếu một module được include 2 lần, inclusion thứ 2 sẽ được bỏ qua.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
module HelloModule def say "hello from module" end end module GoodbyeModule def say "goodbye from module" end end class MyClass include HelloModule include GoodbyeModule include HelloModule end MyClass.new.say # => "goodbye from module" |
- Module methods không thể thay thế các methods đã defined ở trong một class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module HelloModule def hello 'hello from module' end end class HelloClass def hello 'hello from class' end include HelloModule end HelloClass.new.hello # => 'hello from class' |
Chi tiết hơn, bạn có thể tham khoa các tài liệu sau đây:
https://www.sitepoint.com/common-trip-ups-new-rubyists-part/
https://matt.aimonetti.net/posts/2012/07/30/ruby-class-module-mixins/
https://www.vikingcodeschool.com/professional-development-with-ruby/classes-vs-modules
Symbols
Khác với ngôn ngữ lập trình khác, Ruby có một kiểu variable được gọi là Symbol. Để tạo symbol, chúng ta dùng :
ở trước các từ khác như::name, :@foo, ...
Symbol có thể dùng để đặc trưng cho tên method, instance variables và constants. Ví dụ:
1 2 3 4 5 6 7 8 9 |
worker = { :name => "John Doe", :age => "35", :job => "Basically Programmer" } puts worker[:name] # => "John Doe" |
Blocks, Procs, and Lambdas
Method calls trong Ruby có thể dùng {...} hoặc do ... end
ở sau nó. Ví dụ:
1 2 3 4 5 6 7 |
[1,2,3].each { |i| print i } hoặc [1,2,3].each do |i| print i end |
Những code ở giữa được gọi là block
.
Storing Blocks
Ruby blocks có thể gán hoặc lưu giá trị của nó ở trong 2 container: Procs và Lamdas
.
Mình có thể so sánh việc store này giống như anonymous functions
của javascript, tức là mình không cần tạo method, mình có thể tạo các blocks và gán luôn cho biến nào đó.
Có nhiều các để dùng Procs và lambdas như sau:
1 2 3 4 5 6 7 8 |
// Procs add = Proc.new {|a, b| a + b} // Lambdas add = lambda {|a, b| a + b} // hoặc add = -> (a, b) {a + b} |
Để thực hiện, mình dùng hàm #call
1 2 3 |
puts add.call 1, 2 # => 3 |
Scope Resolution Operator (::)
Một vấn đề có thể xảy ra khi bạn có tên module hoặc class giống với tên của core Ruby module.
ví dụ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module MyModule class File end class Thing def exist?(path) File.exist?(path) end end end thing = MyModule::Thing.new thing.exist?('/etc/hosts') # => udefined method `exist?' for MyModule::File:Class |
Để fix lỗi này, bạn cần dùng :: operator ở trước tên mà bị trùng ấy như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
module MyModule class File end class Thing def exist?(path) ::File.exist?(path) end end end thing = MyModule::Thing.new thing.exist?('/etc/hosts') # => true |
Tạo hash từ list của các values
Nếu bạn có danh sách các giá trị, bạn hoàn toàn tạo thành hash bằng sử dụng Hash[...]
1 2 3 4 |
Hash['key1', 'value1', 'key2', 'value2'] # => {"key1"=>"value1", "key2"=>"value2"} |
Double Pipe Equals ||=
Đây là một kỹ thuật viết rất dơn giản và có ích.
Cách viết này thuòng dùng để cache lại kết quả mà đã tính toán hoặc đã thực hiện rồi, tránh việc tính toán hoặc thực hiện lại. Nó sẽ đảm bảo là hàm đó chỉ chạy đúng một lần đầu tiên.
1 2 3 4 5 |
def total @total ||= (1..100000000).to_a.inject(:+) end |
Khi mình gọi hàm total lần đầu, nó sẽ thực hiện tính toán. Nếu mình gọi lại một lần nữa, sẽ lấy giá trị đã có, không cần tính toán lại.
Conclusion
Qua 2 bài đã giới thiệu về những Tips quan trọng trong Ruby xong, mong các bạn sẽ hiểu được nhiều hơn.
Chi tiết xem các tài liệu sau đây:
https://devblast.com/b/ruby-tricks-improve-code
https://www.sitepoint.com/common-trip-ups-new-rubyists/
http://www.rubyinside.com/21-ruby-tricks-902.html