+3

Các mẹo hữu ích khi code Ruby

Trong lúc lập trình ta luôn muốn code sao cho thật ngắn gọn và tối ưu nhất có thể. Để có thể dễ đọc, dễ nâng cấp và sửa đổi 1 cách dễ dàng. Bài viết này mình xin giới thiệu một số mẹo nhỏ khi code Ruby. Hi vọng bài viết sẽ giúp ích cho bạn 😄

1. Sử dụng &. thay cho try

Giả sử bạn có 1 account và muốn kiểm tra xem chủ nhân của account đó có địa chỉ hay không. Để an toàn ta thường viết:

if account && account.owner && account.owner.address
...
end

Nhưng đoạn code này khá dài dòng. Để rút gọn hơn ta có thể viết:

if account.try(:owner).try(:address)
...
end

Nó sẽ trả về nil nếu owner không tồn tại hoặc address nil. Chúng ta có thể viết lại đoạn code này cho an toàn hơn bằng cách sử dụng &.

if account&.owner&.address
...
end

Đoạn code này ngắn nhất và tối ưu nhất cho trường hợp trên.

Chúng ta cùng tổng hợp lại qua ví dụ

account = Account.new(owner: nil) # account without an owner

account.owner.address
# => NoMethodError: undefined method `address' for nil:NilClass

account && account.owner && account.owner.address
# => nil

account.try(:owner).try(:address)
# => nil

account&.owner&.address
# => nil

Kết quả trả về giống nhau nhưng chúng ta có thể dùng nhiều cách khác nhau để diễn đạt chúng

2. Sử dụng dig trong try hash

Thay vì phải viết

address = params[:account].try(:[], :owner).try(:[], :address)

# hoặc

address = params[:account].fetch(:owner) .fetch(:address)

Ta có thể dễ dàng sử dụng Hash#dig với chức năng tương tự như trên

address = params.dig(:account, :owner, :address)

3. Sử dụng render view partials

Ở trong controller ta có action index lấy tất cả toàn bộ user ra

# app/controllers/users_controller.rb
def index
  @users = User.all
end

Ở view thay vì ta viết như thế này

<% @users.each do |user| %>
      <p><%=  user.name %></p>
      <p><%= user.email %></p>
<% end %>

Ta có thể thay thế dùng render partial Tạo 1 file chứa thông tin của user

# app/views/users/_user.html.erb
<p><%= user.name %></p>
<p><%= user.email %></p>

Rồi trong file index ta chỉ việc

<%= render @users %>

Thật nhanh gọn phải không. Bạn có thể tham khảo thêm các option ở Partial Render

4. Sử dụng tap

Cho đối tượng vào 1 block và return ra chính đối tượng đó. Để dễ hiểu hơn ta có thể tìm hiểu nó qua ví dụ Ví dụ bạn muốn tạo 1 params. Thay vì viết

def user_params
   params = {}
   ..
   params[:name] = "ABC"
   params[:email] = "XYZ"
   params
end

Ta có thể dùng tap để trả về luôn params

def user_params
    {}.tap do |params|
         params[:name] = "ABC"
         params[:email] = "XYZ"
     end
 end

Tương tự ta có thể sử dụng với mảng [], đối tượng...vv.. Cập nhập hoặc tạo mới đối tượng có các thuộc tính chỉ định

class User < ActiveRecord::Base
    class << self
        def create_or_update(locale, address, attributes)
          find_or_initialize_by(locale: locale, address: address).tap do |target|
            target.attributes = attributes
            target.save!
          end
        end
    end
 end

5. Tạo nhiều bản ghi cùng 1 lúc

Ví dụ ta cần tạo nhiều bản ghi cùng 1 lúc với các giá trị khác nhau. Không thể dùng foreach rồi insert lần lượt được. như vậy performance sẽ rất chậm. Mình xin giới thiệu 1 cách khá hay ho. Ví dụ tạo nhiều bản ghi user_books cùng chung user_id chỉ khác book_id. Ta dùng gem ActiveRecord-import kết hợp với product

book_ids = [1, 2, 3, 4]
user_id = 1
user_books = [user_id].product book_ids
=> trả về 1 mảng params id: [[1, 1], [1, 2], [1, 3], [1, 4]]
# Sau đó ta import vào DB
UserBook.import( :user_id, :book_id], user_books)

6. Tìm ra phần tử thay đổi thuộc tính, phần tử bị xóa, phần tử mới

Ví dụ ta muốn kiểm tra params gửi lên server có bao nhiêu phần tử thay đổi thuộc tính, bao nhiêu phần tử bị xóa, bao nhiêu phần tử mới ta có thể làm theo cách sau: Đầu tiên ta dùng assign_attributes gán cho đối tượng params

    reservation = reservation.assign_attributes(nested_params)
    # Lấy ra room mới
    new_rooms =  reservation.eservation_rooms.select(&:new_record?)
    # Lấy ra room bị xóa
    marked_destroy_rooms = reservation.reservation_rooms.select(&:marked_for_destruction?)
    # Lấy ra room thay đổi thông tin
    changed_rooms =  reservation.rservation_rooms.select(&:changed?)

5. Các mẹo với Array

Trong lúc làm việc với array có rất nhiều hàm mình hay sử dụng, hôm nay mình xin liệt kê 1 số hàm có thể hữu ích với bạn.

  1. Sử dụng compact để loại đi những phần tử nil
[1, 2, nil, nil].compact
=> [1, 2]
  1. Loại bỏ các phần tử trùng nhau với uniq
[1, 1, 2, 2, 4, 0].uniq
=> [1, 2, 4, 0]
  1. Đảo ngược phần tử trong mảng
arr = [1, 2, 3, 4]
arr.reverse
=> [4, 3, 2, 1]
  1. Tìm ra phần tử chung giữa 2 mảng
[1, 3, 4] & [4, 2, 7]
=> [4]
  1. Duyệt phần tử trong mảng
  • Dùng each để duyệt các phần tử trong mảng
arr = [1, 2, 3, 4]
arr.each do |value|
  put "#{value}! " 
end
=> 1! 2! 3! 4!
  • Dùng map để thực hiện các phép toán trong mảng
[1, 2, 3, 4].map{|val| val + 1}
=> [2, 3, 4, 5]
  • Dùng select để lấy phần tử có điều kiện: Lấy các phần tử lớn hơn 1
[1, 2, 3, 4].select{|val| val > 1}
=> [2, 3, 4]
  • Dùng count để đếm phần tử có điều kiện: Đếm các phần tử có giá trị lớn hơn 1
[1, 2, 3, 4].count{|val| val > 1}
=> 3

Bạn có thể tham khảo các phương thức khác ở Array

4. Các mẹo với Hash

  1. has_key?(key) và ``has_value?(value) : Kiểm tra trong hash có key hoặc có value không
h = { "a" => 100, "b" => 200 }
h.has_value?(100)   #=> true
h.has_value?(999)   #=> false
h.has_key?("a")   #=> true
h.has_key?("c")   #=> false
  1. Gộp 2 hash với nhau với merge
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2)   #=> {"a"=>100, "b"=>254, "c"=>300}
  1. . Duyệt phần tử trong mảng cũng giống như Array mình viết bên trên chỉ khác thay vì each do |value| thì chúng ta thay bằng each do |key, value|

Bạn có thể tham khảo thêm tại Hash

Hi vọng bài viết có giúp ích cho bạn.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí