Tạo Decorator đơn giản với Gem Draper và kết hợp cùng Gem Kaminari

1. Decorator là gì ?

Như chúng ta đã biết, Decorator là lớp phụ trợ, là cầu nối giữa Model và View. Hiện tại nó thường được xếp vào dạng “Presenter”. Ưu điểm của Decorator là :

  • Thêm những xử lý cho 1 object mà không cần viết thêm những đoạn mã logic thừa thãi trong View và Model.
  • Phòng tránh tình trạng phức tạp hoá Helper.

Một ví dụ về Decorator:

Bạn có một model Article và một helper như sau :

# app/helpers/articles_helper.rb
def publication_status article
  if article.published?
    "Published at #{article.published_at.strftime('%A, %B %e')}"
  else
    "Unpublished"
  end
end

Thay vì việc mỗi lần mỗi lần sử dụng phương thức publication_status ta phải truyền vào 1 article ta có thể khai báo publication_status như là một phương thức của object:

<%= @article.publication_status %>

Nếu không sử dụng Decorator bạn sẽ phải khai báo publication_status trong model Article. Điều này có thể làm cho model của chúng ta trở nên quá nặng nề và khó quản lý. Thay vào đó bạn khai báo trong Decorator:

# app/decorators/article_decorator.rb
class ArticleDecorator < Draper::Decorator
  delegate_all

  def publication_status
    if published?
      "Published at #{published_at}"
    else
      "Unpublished"
    end
  end

  def published_at
    object.published_at.strftime("%A, %B %e")
  end
end

Trên view bạn có thể gọi các phương thức giống hệt như gọi trong model

Bài viết này sẽ hướng dẫn các bạn dùng gem Draper để tạo Decorator một cách đơn giản và kết hợp với gem Kaminari.

2. Cài đặt

  • Thêm vào Gemfile:
gem "draper"
  • Run bundle

3. Tạo Decorator

Tạo 1 class Decortor trong thư mục app/decorators, kế thừa từ Draper::Decorator và có tên giống với model mong muốn. Ví dụ tạo Decorators cho model Article

class ArticleDecorator < Draper::Decorator
  delegate_all
end

Hoặc chạy dòng lệnh :

rails generate decorator Article

khi bạn đã có sẵn model Article, để tạo ArticleDecorator

4. Decorate

Sau khi đã có Decorator, bạn muốn sử dụng các hàm được viết trong đó. Đơn giản bạn chỉ việc gọi phương thức decorate.

Có 2 trường hợp sử dụng decorate ở đây đó là :

  • Sử dụng cho single object :
@article = Article.first.decorate
  • Sử dụng cho collection :
@articles = ArticleDecorator.decorate_collection(Article.all)

hoặc nếu collection là một query trả về ActiveRecords :

 @articles = Article.all.decorate

5. Sử dụng Decorator với Kaminari

Khi sử dụng phương thức decorate sẽ trả về Decorating Objects trong khi đó nếu bạn sử dụng gem Kaminari để phân trang sẽ không thể hiểu được Decorating Objects vì scope page chỉ có thể sử dụng bởi ActiveRecords. Có một cách hết sức đơn giản để giải quyết vấn đề đó. Trước hết bạn tạo 1 class có như sau :

class PaginatingDecorator < Draper::CollectionDecorator
  delegate :current_page, :total_pages, :limit_value, :entry_name, :total_count, :offset_value, :last_page?
end

Sau đó trong Decorator mà bạn đã tạo phía trên thêm vào :

def self.collection_decorator_class
  PaginatingDecorator
end

Còn đối với gem will paginate thì bạn thay đoạn delegate trên bằng :

delegate :current_page, :per_page, :offset, :total_entries, :total_pages

Như vậy bạn đã tạo ra 1 Decorator một cách hết sức đơn giản

Chúc các bạn thành công !!!


All Rights Reserved