Tiếp theo bài viết thực hành ứng dụng Rails tạo website quản lý bán hàng, bài viết này sẽ hướng dẫn các bạn tạo một blog cá nhân bằng Rails, có tích hợp rich text editor để tạo post. Nội dung của bài viết là một phần thực hành trích từ khóa học Ruby on Rails tại Techmaster
Notes: Bài viết mặc định các bạn đã có môi trường cài đặt ruby on rails để thực hành. Nếu bạn chưa cài đặt, bạn vẫn có thể hiểu được nội dung bài viết và cách Rails hoạt động
Khởi tạo dự án
Rails giúp bạn tự động hóa rất nhiều công việc, và việc đầu tiên khởi tạo dự án
rails new rich-text-blog
Câu lệnh sẽ tạo ra một thư mục mới, đó là rich-text-blog, bên trong có rất nhiều folder và files thành phần. Các bạn sẽ cần tìm hiều folder app để hiểu cấu trúc MVC của Rails, folder db, nơi chứa các định nghĩa về database, và file config/routes.rb, nơi định nghĩa các url của dự án. Đương nhiên, tại thời điểm này, app của chúng ta chưa có nhiều thứ để bạn có thể tìm hiểu. Hãy tiếp tục.
Thiết kế database
Ứng dụng mà chúng ta đang làm là tạo một blog post, có bảng dữ liệu sẽ là posts, có các fields là content:text, title:string
rails g model Post content:text title:string
Câu lệnh này sẽ tạo ra một model Post, được định nghĩa bởi class Post
# app/model/post.rb
class Post < ActiveRecord::Base
validates :title, presence: true
validates :content, presence: true
end
Nếu để ý, các bạn sẽ thấy validations. Rails cung cấp rất nhiều validations cho model của bạn mà tôi không đề cập tới ở trong khuôn khổ của bài viết. Ví dụ trên chúng ta validate name và content phải được khởi tạo khi khai báo, và không chấp nhận giá trị null.
Viết test script
Ngoài ra, chúng ta cũng nên viết test cho những validations đó.
# app/test/models/post_test.rb
require 'test_helper'
class PostTest < ActiveSupport::TestCase
test "should not be valid without title" do
post = Post.new
post.content = 'just a test'
assert_not post.valid?
assert_includes post.errors.full_messages, 'Title can\'t be blank'
end
test "should not be valid without content" do
post = Post.new
post.title = 'just a test'
assert_not post.valid?
assert_includes post.errors.full_messages, 'Content can\'t be blank'
end
end
Và sau khi chạy test
rake test test/models/post_test.rb
# hoặc rake test test/
chúng ta có kết quả như dưới đây
Như vậy chúng ta sẽ không phải lo lắng tới việc có một post nào đó thiếu field, hay ai đó lỡ 'xóa' validates của mình nhờ bộ test này. Ví dụ ở trên còn đơn giản, nhưng với một ứng dụng lớn hơn, bộ test như vậy sẽ rất hữu ích cho việc maintainance và giảm thiểu bugs.
Prototyping
Sau khi tổ chức dữ liệu xong, phần tiếp theo mà tôi muốn làm là prototyping, có nghĩa là tạo ra một số dump view, controller actions và routes để có 1 cái nhìn bao quát về sản phẩm. Về sau, sau khi thành thạo rails, các bạn có thể làm theo thứ tự khác mà không cần làm prototype trước.
Đầu tiên, chúng ta sẽ tạo PostsController class kế thừa AplicationController
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
def new
@post = Post.new
end
end
Các bạn có thể để ý thấy 2 actions được khai báo là index và new. Hai actions này sẽ tương ứng với hành động lấy danh sách các posts và mở trang tạo post mới. Các biến @post và @posts sẽ được dùng trong views ở phần sau. Để Rails làm được điều đó, chúng ta sẽ khai báo routes
# config/routes.rb
Rails.application.routes.draw do
# You can have the root of your site routed with "root"
root 'posts#index'
# New post
get 'posts/new' => 'posts#new'
# list các posts
get 'posts' => 'posts#index'
end
Và chúng ta cần thêm file view để hiển thị tới user. Khi user mở web page của chúng ta ở trang chủ (root_path), Rails thông qua file routes, sẽ truyền request lên action index của PostsController để lấy dữ liệu thông qua model, truyền vào views và hiển thị lại tới user. Các bạn có thể tìm hiểu thêm về MVC để hiểu thêm cơ chế hoạt động của Rails ở đây.
<!-- app/views/posts/index.html.erb -->
<h2> List all Post </h2>
<!-- app/views/posts/new.html.erb -->
<h2> New Post </h2>
Các bạn có thể để ý thấy Rails rất coi trọng việc đặt tên các file views, tương ứng với các action trong controller. Đó gọi là Convention over Configuration. Tức là nếu các bạn làm đúng như qui tắc Rails đưa ra, Rails sẽ làm hết công việc của phần Configuration
Chúng ta đã đi được một chặng đường tương đối dài, giờ hãy khởi động dự án của bạn và check xem mọi thứ có ổn không tại localhost:3000
rails server
Add rich text editor gem và twitter bootstrap
ck-editor là gem sẽ được dùng, nó cung cấp cho chúng ta công cụ để tạo post dễ dàng, gọi là richtext editor
Twitter Boostrap cũng lại là một open source Gem được rất nhiều developers sử dụng. Nó cho phép chúng ta dễ dàng tạo, custom UI cho các trang web của mình.
Trong file Gemfile, các bạn thêm 2 gem trên
# Gemfile
...
gem 'ckeditor_rails'
gem "twitter-bootstrap-rails"
sau đó, các bạn bundle install và tiến hành cài đặt 2 gem này.
bundle install
rails generate bootstrap:install static
include ck-editor vào file application.js
//= require jquery
//= require jquery_ujs
//= require twitter/bootstrap
//= require ckeditor-jquery
//= require turbolinks
//= require_tree .
Tiếp theo, các bạn generate bootstrap theme cho Posts
rails g bootstrap:themed Posts
Bằng một vài chỉnh sửa bootstrap, và thêm class ckeditor vào content part trong form
<!-- app/views/posts/_form -->
<!-- .... -->
<div class="form-group">
<%= f.label :content, :class => 'control-label col-lg-1' %>
<div class="col-lg-11">
<%= f.text_area :content, :class => 'form-control ckeditor' %>
</div>
<%=f.error_span(:content) %>
</div>
<!-- .... -->
Và chúng ta có form để tạo post sử dụng rich text editor
Lưu dữ liệu post, hiển thị danh sách
Các bạn có thể khởi động Rails server và test sản phẩm của chúng ta tính tới thời điểm này tại localhost:3000/posts/new
Khi các bạn cố gắng tạo một post, các bạn sẽ gặp lỗi như này
Rails thông báo cho bạn rằng bạn chưa định nghĩa cction POST, tại địa chỉ '/posts'
Đó là vì khi bạn click button create post, Rails sẽ thực hiện HTTP POST tới url localhost:3000/posts, và đồng thời gọi action create trong controller. Tiếp theo, chúng ta sẽ làm những công việc đó
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
#...
def show
@post = Post.find(params[:id])
end
def create
@post = Post.new(post_param)
if @post.save
# chuyển tới trang show post
redirect_to post_path(@post)
else
# quay lại trang tạo post mới, đồng thời show error
redirect_to new_post_path, flash: { error: @post.errors.full_messages }
end
end
private
def post_param
params.require(:post).permit(:content, :title)
end
end
# config/routes.rb
Rails.application.routes.draw do
#...
root 'posts#index'
resources :posts
# ...
end
Và cuối cùng, chúng ta cần tạo ra view để hiển thị post
<%- model_class = Post -%>
<div class="page-header">
<h1><%=t '.title', :default => model_class.model_name.human.titleize %></h1>
</div>
<dl class="dl-horizontal">
<dt><strong><%= model_class.human_attribute_name(:content) %>:</strong></dt>
<dd><%= @post.content.html_safe %></dd>
<dt><strong><%= model_class.human_attribute_name(:title) %>:</strong></dt>
<dd><%= @post.title %></dd>
</dl>
<%= link_to t('.back', :default => t("helpers.links.back")),
posts_path, :class => 'btn btn-default' %>
Có một điều mà các bạn cần chú ý ở trong file show post, đó là chúng ta dùng .html_safe để hiển thị post content theo html syntax, trong khi chúng ta lưu post content ở dạng text html
Và chúng ta sẽ có được post như hình ảnh dưới đây
Good luck!
Bình luận