Nhập môn Lotus – Phần 2: Hiệu chỉnh layout và cơ bản về Lotus Model

Hướng dẫn Lotus - Phần 2: Chỉnh sửa layout và cơ bản về Lotus Model
Trong bài Nhập môn Lotus trước, chúng ta đã tìm hiểu sơ về Lotus, tạo một app đơn giản và thay đổi layout cho trang web. Lúc đó, mình cũng chưa có nói rõ mục đích của series tutorial này là viết web gì, bữa nay nói luôn. Mục đích cuối cùng sau khi kết thúc mấy bài giới thiệu be bé này là bạn sẽ làm được một ứng dụng web kiểu tương tự như Twitter. Nó bao gồm các tính năng sau:

  • Cho phép người dùng đăng ký tài khoản, đăng nhập…
  • Post một đoạn tweet nhỏ, limit 160 ký tự (hào phóng hơn cả Twitter :D).
  • Nhận diện được #hashtag cũng như cho phép tìm kiếm dựa vào #hashtag.

Tạm thời chỉ đơn giản vậy, có khi sau này làm mở rộng thêm nhiều tính năng khác cũng có :D. Sau khi đọc hết bài này, mình sẽ làm được vài trò sau:

  • Customize lại layout, kiểu như chưa login thì show cái gì, login rồi thì show cái gì.
  • Hiểu được cơ bản về Lotus model cũng như generate model đầu tiên với Lotus.

Trong Rails mà làm mấy trò này thì quá là đơn giản rồi, nhưng đây là Lotus nên hơi bị phiền phức chút xíu, tại mới mà, chưa đủ gem như Rails.

Hiệu chỉnh layout

Ở phần trước mình dừng lại ở chỗ này:

Hướng dẫn Lotus - Layout bài trước

Tranh thủ mình practice TDD luôn ha. Bây giờ mình expect cái layout nó sẽ phải có:

  • Title: thay Project name thành Lotus Tutorial
  • Khi chưa login phải có form để đăng nhập, gồm có email field và password field
  • Khi chưa login thì báo với user là chưa login và có nút Sign Up

Rồi, clarify rõ ràng expectation, giờ mình viết test trước. Lotus mặc định đã có cài sẵn capybara cho mình rồi, bây giờ mình tạo một file mới gọi là visit_home_spec.rb trong folder spec/web/features:

require 'features_helper' là để sử dụng capybara. Đầu tiên mình expect page có title Lotus Tutorial:

Chạy test (dzô terminal, chạy rspec), sure là test sẽ fail.

Tip: để cho tiện thì mấy bạn xài Guard vào để nó monitor mấy cái file spec, mỗi lần mình save lại là nó tự chạy rspec luôn.

Mở file application.html.erb, thay đổi title:

Rồi thay đổi trên thanh Navigation Bar:

Bây giờ chạy lại test thì sure là nó pass.

Tương tự mình viết tiếp expectation cho homepage:

Ở đây mình chia ra theo context, tạm thời chỉ viết spec cho trường hợp user chưa login thôi. Sau test thì implement, đầu tiên thì chỉ cần cho test pass, mọi người xem thêm ở commit này: https://github.com/siliconstraits/lotus-tutorial/commit/e5310307fa217b2d82cddff60cf8cf898fa55511

Sau khi đảm bảo là các test cases đều pass, đây là kết quả:

Hướng dẫn Lotus - After Customize

Giờ thử nghĩ xem có refactor được hay không ha. Ở cái đoạn mà “Please sign in to see your tweets”, mình đâu có hard code chỗ đó được, lỡ người dùng login vào rồi thì nội dung phải khác chứ. Vậy nên chỗ này mình phải sử dụng optional content, tức là phần nội dung đó có thể khác nhau giữa các page. Nó giống như cái yield với content_for bên Rails vậy á. Giờ replace cái chỗ jumbotron thành như này:

Chạy test, và bị fail.

Trong lotus thì các method trong view class sẽ access được trong template. Mình cần hiển thị cái phần content đó ở trang home thôi, nên mình thêm một method header vào view class của home#index. Mở file apps/web/views/home/index.rb, thêm vào method sau:

Rồi, bây giờ thử chạy lại test, bạn sẽ thấy mọi thứ lại xanh tươi như trước :D (nếu không xanh thì… mấy bạn xem lại thử coi có gì sai ko nha :-s)

User model

Bây giờ đến tiết mục implement phần user authentication. Trước hết phải có model user đã. Mọi người có thể xem qua overview của Lotus model ở đây. Tóm tắt ngắn gọn thì Lotus có xây dựng một ORM riêng, gọi là lotus-model và xây dựng dựa trên sequel. Lotus model cung cấp cho bạn các thành phần chính sau:

  • Entity – An object defined by its identity.
  • Repository – An object that mediates between the entities and the persistence layer.
  • Data Mapper – A persistence mapper that keep entities independent from database details.
  • Adapter – A database adapter.
  • Query – An object that represents a database query.

Config database

Trong bài tutorial này mình dùng postgresql, việc trước tiên cần làm là bảo cho Lotus biết mình dùng adapter nào, mở file lotus-tutorial.rb, bạn sẽ thấy phần comment giới thiệu cũng như hướng dẫn cấu hình. Lotus hỗ trợ 3 loại adapters:

  • File system (default)
  • Memory
  • SQL

Thay đổi cấu hình adapter như sau:

Ở đây mình sẽ dùng environment variable thay vì hardcode, lý do là vì tùy vào môi trường mà mình sẽ có các config khác nhau, ví dụ development thì trỏ vào database khác, rồi test , rồi production, thậm chí cả staging nữa. Như ở phần 1 mình có nói, mỗi environment sẽ có một file .env tương ứng: .env.test, .env.development… Configuration của mình như sau:

Ở đây mình giả sử là bạn cài postgresql với các default settings, bạn có thể sẽ phải thay đổi tùy theo settings của bạn (ví dụ như bạn có đặt thêm user, password, port…). Để test xem url của bạn có đúng không thì chỉ cần chạy lệnh lotus db create, nếu mọi thứ đều ổn thì database sẽ được tạo (development environment), không thì bạn phải xem lại và thay đổi đường dẫn cho đúng.

Tips: xem thêm các database command trong Lotus: http://lotusrb.org/guides/command-line/database/

Generate migration

Chạy lệnh lotus generate migration create_user, Lotus migration cũng tương tự như Rails migration thôi, không có gì đặc biệt hết, về syntax thì mọi người xem thêm ở đây. Lúc này lotus sẽ tạo dùm bạn một file mới trong folder db/migrations, prefix sẽ là timestamp lúc generate ra.

Trong bài tutorial này mình chỉ cần table user lưu trữ vài thông tin cơ bản thôi, bao gồm:

  • Id (primary key)
  • Name
  • Email
  • Password
Tất nhiên trong trường hợp thực tế ta sẽ không lưu raw password như vậy mà sẽ lưu password_hash, nhưng do đây là tutorial nên tạm thời bỏ qua phần đó. Xong phần define table schema, giờ chạy lệnh lotus db migrate để tạo table trong database. Nếu mọi việc suông sẽ thì bạn sẽ không thấy có gì đặc biệt hết, ngược lại màn hình terminal của bạn sẽ hiện ra một đống chữ :D.

Generate model

Lotus cung cấp cho developer command để generate tất cả những thứ liên quan đến model như entity, repository, syntax: lotus generate model [model_name]. Ở đây mình cần tạo model user nên mình chạy lệnh

Kết quả:

Gồi, bây giờ viết test cho entity user trước. Xem thêm về lotus entity ở đây. Tóm tắt thì Lotus entity cũng chỉ là business object bình thường thôi, không như trong Rails, phải gánh quá nhiều thứ. Một class khi include Lotus::Entity module thì sẽ có thêm 3 methods nữa: – #id: trả về id trong database – #id=: set id cho record – #initialize(attributes = {}): custom constructor nhận vào parameter là 1 Hash object

Nói chung ai làm Rails rồi thì sẽ quen với mấy cái này :D.

Từ schema của table user, mình sẽ expect user entity phải có các attributes sau:

  • id
  • name
  • email
  • password

Vậy nên user_spec.rb của mình sẽ như sau:

Chạy test và fail. Thực ra thì cái này có vẻ hơi thừa thải, nhưng cũng kệ, ai chưa quen viết spec hay TDD có thể tranh thủ practice.

Để test pass thì mình chỉ cần define các attribute này trong user.rb:

Xong, chạy lại test và pass. Rất đơn giản.

Data Mapper

Để lotus-model biết được là phải map column nào dưới database lên attribute nào của entity, ta cần phải map chúng lại với nhau. Trong folder config, mình có sẵn một file mapping.rb, là nơi bạn sẽ khai báo tất cả các mapping giữa entity với table. Thay đổi nội dung file này thành:

Giải thích:

  • collection :users => table users trong database
  • entity User => mỗi record trong table users ứng với một instance của class User
  • repository UserRepository => khai báo repository cho User, giúp tách rời nhiệm vụ persist data, query logic ra khỏi model, nếu mọi người chưa hiểu repository pattern là gì thì có thể tham khảo thêm tại đây.
  • attribute ...: map column và attribute giữa database với Ruby object.

Tuy nhiên bạn cần phải config thêm trong file lotus-tutorial.rb để báo cho Lotus biết là bạn định nghĩa mapping trong file mapping.rb, còn nếu không nó sẽ nghĩ bạn khai báo inline luôn, bạn chỉ việc mở file lotus-tutorial.rb và uncomment dòng:

Vậy là xong phần cấu hình mapping.

Repository

Như đã nói ở trên, repository sẽ làm nhiệm vụ persist data cũng như query logic. Xem đầy đủ hướng dẫn ở đây. Nói ngắn gọn về cách sử dụng repository thì nó là như này. Nếu bạn nào đã làm Rails quen thì khi muốn lưu data xuống database, ta gọi method #save của object. Bây giờ thì đẩy việc đó qua cho repository:

  • UserRepository.create(user) => user chính là một instance của entity User mà ta đã định nghĩa ở trên. Method này sẽ tạo và lưu user xuống database.
  • UserRepository.persist(user) => Lưu thay đổi của user xuống database.

Việc query data cũng không giống như ActiveRecord nữa. Trong repository thì query là một private component. Nghĩa là các query logic của bạn sẽ được encapsulated, khi dùng thì chỉ cần gọi ra method cần thiết của Repository class thôi, còn logic như nào thì không visible nữa.

Hiện tại ở version 0.4.x, lotus model vẫn chưa support association, nhưng theo roadmap thì tính năng này sẽ có mặt ở phiên bản 0.5.0, dự kiến phát hành trong tháng 9/2015.

Bây giờ nếu muốn thử thì mọi người có thể vào terminal, gõ lệnh lotus console, nó giống như rails console thôi. Bạn có thể thử khởi tạo và persist user record xuống database.

Tạm kết bài

Bài này giới thiệu khá nhiều, chưa có đi sâu vào code được, tại cứ đụng mấy cái lý thuyết là phải giải thích :(. Ở phần sau mình sẽ implement cơ chế authentication cho Lotus, cũng đơn giản thôi, và implement các tính năng cơ bản của user như registration, sign in, sign out. Chào thân ái và quyết thắng! :D

Nếu muốn mọi người có thể xem code (đã implement trước nhiều cái) trên github.

SSS Full-stack Engineer

Love Silicon Straits and want to know more about our company culture, working environment or job vacancies?
Find out more at careers.siliconstraits.vn.

Silicon Straits
Be Challenged. Be Inspired. Be Different.




Follow us for more later

or subscribe with