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

Học lập trình Ruby on Rails cơ bản đến nâng cao

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

Học lập trình Ruby on Rails cơ bản đến nâng cao

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@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

Học lập trình Ruby on Rails cơ bản đến nâng cao

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

Học lập trình Ruby on Rails cơ bản đến nâng cao

 

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

Học lập trình Ruby on Rails cơ bản đến nâng cao

Good luck!