ActiveRecord refactoring (P3) - Presenters
Bài đăng này đã không được cập nhật trong 3 năm
Mở đầu
Như vậy là thông qua việc dịch chuỗi bài viết ActiveRecord Refactoring của tác giả Luke Morton, mình đã cùng các bạn đã lần lượt tìm hiểu bài dịch về concerns và services.
Hôm nay mình xin giới thiệu đến các bạn bài viết cuối cùng trong chuỗi bài viết về ActiveRecord Refactoring
này - Presenters
.
Phần 3. Presenters
Presenters
cung cấp cho chúng ta một cách thức của việc vá chức năng bổ sung cho model
của bạn trước khi chúng ta truyền nó đến một khung nhìn. Thường thì presenters
sẽ là cho từng trang cụ thể nhưng nó cũng có thể mang tính tổng quát hơn và cũng dựa module hoặc dựa thành phần.
Nếu như không phải là Rails
, presenter
thường được biết đến như là mẫu View Model
hơn. Khái niệm View Model
giúp chúng ta giải thích rằng các mẫu là cho cái gì. Ví dụ : một khung nhìn miền model
cụ thể, một nơi để đưa logic chỉnh sửa một miền model
cho một khung nhìn cụ thể.
Thông thường, presenters
là một đối tượng ủy thác đến miền model
. Nói cách khác, một presenters
sẽ chứa một model
và với tất cả các phương thức mà không tồn tại trong presenters
thì nó sẽ ủy thác ngược trở lại cho đối tượng ban đầu, đối tượng gốc và gọi đến phương thức ở trên đó.
Presenters
cũng là một hình thức của decorator
, một đối tượng thêm các chức năng vào đối tượng mà nó decorate
. Trong trường hợp này, đối tượng presenter
đang decorate
khung nhìn một chức năng cụ thể vào một model
. Hãy cũng phân tích sâu hơn với một ví dụ cho dễ hiểu nhé.
Một presenter
cụ thể - UserProfilePresenter
, nó có gọi đến model User
cho hành động UsersController#view
.
class UsersController < ApplicationController
def view
@user = UserProfilePresenter.new(User.find(params[:id]))
end
end
Như đã thấy ở trên, chúng ta truyền model
vào presenter
trong hành động của controller
. Và sau đó, view
sẽ sử dụng biến @user
giống như là bạn đang sử dụng trực tiếp một model
vậy. Bạn có thể truyền User
trong biến @user
và bạn cần viết một số logic cụ thể cho view
.
Giả sử chúng ta cần sử dụng username
nếu như full name
không tông tại trong trang tiêu đề của trang hồ sơ cá nhân.
require "delegate"
class UserProfilePresenter < SimpleDelegator
def page_title
I18n.t("user.profile.page_title", name: full_name || username)
end
end
Ở đây chúng ta sử dụng một trình dịch và truyền vào full_name || username
vào như là biến. Tất nhiên chúng ta có thể đặt dòng này ở trong view
.
Nếu như chúng ta muốn hiển thị cho dù là người dùng đang online
hay là thời gian họ online
lần cuối, chúng ta có thể đặt logic này ở trong một phương thức #online_status
.
require "delegate"
class UserProfilePresenter < SimpleDelegator
def online_status
if online?
I18n.t("user.profile.online_status.online")
else
I18n.t("user.profile.online_status.last_online", last_online: last_online)
end
end
end
Một lần nữa chúng ta gọi đến trình dịch, nhưng có sử dụng thêm một chút logic để quyết định xem là sẽ hiển thị một thông báo trực tuyến hay là lần online
cuối cùng.
Đến đây thì có lẽ bạn sẽ thắc mắc là các phương thức full_name
, username
, online?
và last_online
thì ở đâu ra? Và đó là SimpleDelegator
. Lớp này có trong thư viện chuẩn của Ruby và nó cung cấp một phương thức initialize
.
Như bạn đã thấy ở UsersController
bên trên kia, chúng ta đã truyền vào một đối tượng User
. Bất kỳ phương thức nào không tồn tại trong UserProfilePresenter
thì sẽ được gọi đến trong đối tượng User
mà chúng ta đã truyền vào. Điều đó có nghĩa là presenter
sẽ có tất cả các phương thức mà model
có, bất kỳ phương thức ghi đè hay là chúng ta tự định nghĩa mới.
Nó rất hữu ích vì chúng ta sẽ thường xuyên muốn sử dụng trực tiếp phương thức của model
mà không qua sửa đổi.
Thỉnh thoảng bạn sẽ có những phương thức được sử dụng trong nhiều view
có liên quan đến User
. Hãy xem phương thức online_status
từ ví dụ trên.
Nếu cần thiết trong nhiều view
, chúng ta có vài lựa chọn :
-
Di chuyển nó vào đối tượng
User
. Tuy nhiên, nếu bạn muốn giữview
liên quan đến logic, đặc biệt là khi gọi đến trình dịchI18n.t
, sau đó ta có thể lại muốn đặt nó ở một nơi khác. -
Sử dụng
helper
,UserHelper
cung có thể là một phương thức như vậy:
module UserHelper
def user_online_status
if @user.online?
I18n.t("user.profile.online_status.online")
else
I18n.t("user.profile.online_status.last_online", last_online: @user.last_online)
end
end
end
Tuy nhiên tôi lại thích tạo một mixin
UserPresenter
hơn :
class UserPresenter
module Mixin
def online_status
if online?
I18n.t("user.profile.online_status.online")
else
I18n.t("user.profile.online_status.last_online", last_online: last_online)
end
end
end
include Mixin
end
Ở đây, chúng ta có UserPresenter
mà có thể sử dụng độc lập; 'UserPresenter::Mixin
thì có thể chứa nhiều presenter
cụ thể như là UserProfilePresenter
. Tuyệt vời!!!
Đối tượng presenter
thực hiện kiểm tra các khía cạnh phức tạp của các trường hợp khung nhìn một cách đơn giản hơn. Nó không thay thế được test sử dụng capybara
nhưng nó sẽ giúp giảm tải việc cần thiết phải test view
trong unit test.
Trên đây mình đã trình bày xong chuỗi 3 bài viết này, hi vọng mọi người sẽ có được một cái nhìn tổng quát hơn về ActiveRecord refactoring
.
Bài viết liên quan
Cảm ơn bạn đã theo dõi bài viết
tribeo
All rights reserved