Sử dụng gem Cells hiệu quả trong Ruby on Rails

Từ khi Rails ra đời cho tới nay các nhà phát triển rất chú trọng đến việc xây dựng các ứng dụng theo mô hình OOP, vừa để hướng theo những tư duy Code tiến bộ và cũng giảm bớt ghánh nặng cho các lập trình viên.

Trong mô hình MVC của Rails, mối quan hệ View - Models là rất quan trọng và có thể xây dựng nó theo OOP để việc tái sử dụng các hàm logic phức tạp trở lên dễ dàng, đồng thời giảm nhẹ và tối ưu code cho Models trong các dự án lớn.

Method “partial” có lẽ đã trở lên rất quen thuộc đối với mỗi lập trình viên Ruby on Rails, ưu điểm của nó là có thể gộp và tái sử dụng code View nhiều lần, tuy nhiên khi cần phải thao tác với nhiều hàm logic và muốn gộp chúng lại thành một Package để sử dụng thì vấn đề trở lên rất phức tạp và không khả thi.

Cells là một lựa chọn tuyệt vời cho bạn để giải quyết vấn đề này.

Ở bài viết này chúng ta cùng tìm hiểu và sử dụng gem Cells, đồng thời so sánh một vài thay đổi khi Cells 3.x được cập nhật lên phiên bản mới Cells 4.x

I - Cells là gì?

Cells là 1 gem được sử dụng để gộp cả view lẫn controller lại thành 1 package, phát huy tối đa tính đóng gói, kế thừa, kiểm thử, đặc biệt với những view có logic phức tạp, thì cells sẽ giúp code trở nên dễ đọc và dễ dàng tái sử dụng hơn.

**II - Khi nào nên sử dụng Cells? **

  • xử lý các logic phức tạp, tái sử dụng lại code trong View - Models
  • hiển thị popup login
  • hiển thị ranking ở side menu
  • hiển thị comment
  • ...

III - Cài đặt gem Cells vào ứng dụng Rails

Thêm vào Gemfile trong ứng dụng của bạn :

gem 'cells'

Để sử dụng bạn cần thông qua generate :

rails generate cell popup_login show -e haml

Gọi một package trong Cells :

= render_cell :popup_login, :show, :property => @property_name

IV - Ví dụ với Popup Login

Khi sử dụng partial :

header_sign_in.html.haml

- unless user_signed_in?
  .header
    .header__login-button
      = link_to 'login', 'javascript:void(0);'
= render partial: 'popup_login'

_popup_login.html.haml

- unless user_signed_in?
  .popup-login
    = form_for(@user, url:user_session_path) do |f|
      %dl
        %dt= f.label :email
        %dd= f.email_field :email
        %dt= f.label :password
        %dd= f.password_field :password
      = f.submit 'Login'
   :javascript
     $('.header__login-button a').click(function(){
       $('.popup-login').css({ 'display' : 'block'});
     });

Sẽ có lỗi xảy ra nếu object @user dùng trong file _popup_login.html.haml chưa được định nghĩa trong controller.

Ta có thể định nghĩa @user trong tất cả các action có gọi _popup_login, tuy nhiên một giải pháp tốt hơn là định nghĩa trong application_controller.

application_controller.rb

class ApplicationController < ActionController::Base
  before_action: :pre_load
  def pre_load
    @user = User.new unless current_user.present?
  end
end

Nhưng nếu logi phức tạp hơn như comment, ranking...thì chắc chắn code không chỉ đơn giản và ngắn gọn như vậy.

Giải pháp triệt để cho vấn đề này là ta dùng Cells :

Cấu trúc thư mục Cells sẽ như sau :

/app
/assets
/cells
/popup_login
show.html.haml
popup_login_cell.rb

Cụ thể, chức năng popup_login sẽ được viết như sau :

app/cells/popup_login_cells.rb

class PopupLoginCell < Cell::Rails
  def show
    @user = User.new unless current_user.present?
    render
  end
end

app/cells/popup_login/show.html.haml

- unless user_signed_in?
  .popup-login
    = form_for(@user, url:user_session_path) do |f|
      %dl
        %dt= f.label :email
        %dd= f.email_field :email
        %dt= f.label :password
        %dd= f.password_field :password
      = f.submit 'Login'
   :javascript
     $('.header__login-button a').click(function(){
       $('.popup-login').css({ 'display' : 'block'});
     });

Khi partial show được gọi, cells sẽ tự động gọi controller show tương ứng và thực hiện các login cần thiết.

Với cách làm này, những hàm logic sẽ chỉ được thực hiện khi gọi cells (tối ưu hơn hẳn khi dùng application_controller), và ta chỉ cần quan tâm đến việc gọi partial trong view là đủ.

V - So sánh phiên bản Cells 3.x với Cells 4.x

Khi phiên bản Cells 4.0 ra đời với tiêu chí : “Goodbye Rails! Hello Ruby!” đã hứa hẹn mang lại nhiều tiện ích mới cho các nhà phát triển ứng dụng.

Tuy nhiên trong phiên bản mới này Cells đã bỏ việc include các tính năng của ActiveView nhằm cải thiện tốc độ hiển thị. Để sử dụng các tính năng này bạn cần include thêm vào ứng dụng của mình :

cells/application.rb

class ApplicationCell < Cell::ViewModel
  # helper tự định nghĩa
  include ApplicationHelper

  # helper của devise
  include DeviseHelper

  # helper của các gem
  include FontAwesome::Rails::IconHelper
  include BreadcrumbsOnRails::ControllerMixin::HelperMethods
  include LazyHighCharts::LayoutHelper

  # asset (image_path)
  include ActionView::Helpers::AssetUrlHelper
  include Sprockets::Rails::Helper
  # chỉ định phạm vi tìm kiếm của asset
  self.assets_prefix      = Rails.application.config.assets.prefix
  self.assets_environment = Rails.application.assets
  ## asset digest
  self.digest_assets      = Rails.application.config.assets[:digest]

  # form
  include ActionView::RecordIdentifier
  include ActionView::Helpers::FormHelper
  include ActionView::Helpers::FormOptionsHelper

  ## form_for
  String.send :include, ActionView::Helpers::TextHelper
  String.send :include, ActionView::Context

  ## form_for
  def form_for(model, options, &block)
    raw(super)
  end

  ## fields_for
  def fields_for(model, &block)
    raw(super)
  end

  # i18n
  include ActionView::Helpers::TranslationHelper

  def scope_key_by_partial(key)
    if key.to_s.first=="."
      "cells.#{self.class.to_s.underscore}#{key}"
    else
      key
    end
  end

  # model ở dạng số nhiều thì dùng alias
  def models
    model
  end

  # flash
  def flash
    parent_controller.flash
  end

end

Nguồn tài liệu tham khảo :