Rails 實戰——圖書管理系統——基礎建設

趙龍1043發表於2018-03-26

目標

搭建 rails 專案的基礎

注意兩個 “+….” 符號中間的程式碼,是新增程式碼

1、確認操作環境

進入終端頁面

ruby -v
#=> ruby 2.3.1p112

rails -v
#=> Rails 5.1.4

常用終端指令

git status  # 檢視 git 狀態
rake routes # 檢視路由

2、建立 Rails 專案

rails new library01
cd library01
git init
git add .
git commit -m "First Commit"

2.1 安裝 Mysql 資料庫

git checkout -b ch01
# 建立新分支

修改 Gemfile 檔案

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.1.4'
- # Use sqlite3 as the database for Active Record
- gem 'sqlite3'
+ # Use mysql2 as the database for Active Record
+ gem 'mysql2'

2.2 修改資料庫配置檔案

檔案 config/database.yml 修改為下面形式

development:
  adapter: mysql2       #資料庫型別
  database: rails01     #資料庫名字(需要修改)
  username: root        #使用者名稱
  password:             #使用者密碼
  host: localhost       #ip地址


production:
  adapter: mysql2
  database: rails01
  username: root
  password:
  host: localhost
# 啟動 Mysql 資料庫
mysql.server start

# 建立資料庫
rake db:create

git status
git add .
git commit -m "install mysql"

3、建立 Welcome 頁面

在檔案 config/routes.rb 新增 welcome 頁面路由

Rails.application.routes.draw do
  root 'welcome#index' # 確定首頁路由
end

新建檔案 touch app/controllers/welcome_controller.rb

class WelcomeController < ApplicationController
  def index
  end
end

新建資料夾 mkdir app/views/welcome
新建檔案 touch app/views/welcome/index.html.erb

<h1>Hello World</h1>

再開一個終端頁面,執行 rails s
開啟 http://localhost:3000 頁面

git add .
git commit -m "implement welcome#html"

4、佈置網站樣式

4.1 將 Bootstrap 的 CSS 套件裝進專案裡面

Gemfile

+ gem 'bootstrap-sass'

group :development, :test do

修改檔名,終端輸入

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss

然後修改 app/assets/stylesheets/application.scss 檔案,在最後加入以下兩行

/*
 * ...(一堆註解)
 *= require_tree .
 *= require_self
 */

+ @import "bootstrap-sprockets";
+ @import "bootstrap";

將變更 commit 進 git 裡面
git add .
git commit -m "add bootstrap to project"

4.2 修改 Views 樣式

修改檔案 app/views/layouts/application.html.erb

  <head>
-   <title>Rails05</title>
+   <title>圖書管理系統</title>

  <body>
-   <%= yield %>
+......
    <div class="container-fluid">
      <%= render "common/navbar" %>
      <%= yield %>
    </div>
    <%#= render "common/footer" %>
+......   
  </body>

4.3新增 navbar

新增 app/views/common 資料夾
mkdir app/views/common

新增 navbar 導航欄
touch app/views/common/_navbar.html.erb

填入 app/views/common/_navbar.html.erb

<nav class="navbar navbar-default" role="navigation">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <a class="navbar-brand" href="/">圖書管理系統</a>
            <!-- href="#"是點選按鈕跳轉路徑,將符號"#"替換為相對網址路徑,例如"/"表示首頁路徑 -->
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav navbar-right">
                <li><%= link_to("登入", '#') %></li>
                <!-- 在 rails 中 "link_to" 是 "<a>" 的封裝 -->
            </ul>
        </div>
        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container-fluid -->
</nav>

touch app/views/common/_footer.html.erb

填入app/views/common/_footer.html.erb

<footer class="container-fluid navbar-fixed-bottom" style="margin-top: 100px;">
    <p class="text-center">圖書管理系統
        <br>Planck1043
    </p>
</footer>
git add .
git commit -m "update view style"

參考資料:

5、安裝 flash 訊息提示功能

5.1 將 Boostrap 的 js 提示套件 bootstrap/alert “掛”進專案裡面

requre_tree 上加入一行 //= require bootstrap/alert
app/assets/javascripts/application.js

... (一堆註解)
//= require jquery_ujs
//= require turbolinks
+//= require bootstrap/alert
//= require_tree .

5.2 新增 app/views/common/_flashes.html.erb

touch app/views/common/_flashes.html.erb

填入 app/views/common/_flashes.html.erb

<% if flash.any? %>
  <% user_facing_flashes.each do |key, value| %>
    <div class="alert alert-dismissable alert-<%= flash_class(key) %>">
      <button class="close" data-dismiss="alert">×</button>
      <%= value %>
    </div>
  <% end %>
<% end %>

5.3 加入 app/helpers/flashes_helper.rb

touch app/helpers/flashes_helper.rb

填入以下內容:
app/helpers/flashes_helper.rb

module FlashesHelper
  FLASH_CLASSES = { alert: "danger", notice: "success", warning: "warning"}.freeze

  def flash_class(key)
    FLASH_CLASSES.fetch key.to_sym, key
  end

  def user_facing_flashes
    flash.to_hash.slice "alert", "notice","warning" 
  end
end

5.4 在 application.html.erb 內加入 flash 這個 partial

<%= yield %> 前加入 <%= render "common/flashes" %>

app/views/layouts/application.html.erb

+ <%= render "common/flashes" %>
  <%= yield %>

5.5 git 存檔

git add .
git commit -m "add bootstrap flash function"

5.6 測試 flash helper 的功能(可以略過)

加入 flash[:notice] = “早安!你好!”。你應該可以看到系統跳出“綠色”提示窗。

app/controllers/welcome_controller.rb

class WelcomeController < ApplicationController
  def index
    flash[:notice] = "早安!你好!"
  end
end

6、安裝 Sorcery(輕量級會員系統)

devisesorcery 都是會員系統。

devise是重量級的,sorcery是輕量級的。

安裝gem

Gemfile

+ gem 'sorcery'

group :development, :test do

終端執行 $ bundle install
重啟 $ rails s

初始化安裝

$ rails g sorcery:install

如何安裝sorcery提供的子功能(可以忽略)

比如下面的重置密碼功能,可以參考config/initializers/sorcery.rb檔案
$ rails generate sorcery:install reset_password --only-submodules

配置引數檔案

配置檔案/config/initializers/sorcery.rb,這些地方需要配置,將啟用功能刪除,減少配置的複雜度

user.user_activation_mailer = nil
user.activation_mailer_disabled = true
user.prevent_non_active_users_to_login = false

Sorcery具體使用

sorcery只有針對密碼的加密功能,但是沒有針對密碼的校驗和密碼一致性的校驗。執行上面的模組建立方式:

$ rails g sorcery:install remember_me reset_password user_activation --only-submodules
config/initializers/sorcery.rb參考如上的配置方式。

修改檔案 config/routes.rb

Rails.application.routes.draw do
  root 'welcome#index'
  resources :users
  resources :sessions
end

終端執行 rake db:migrate

配置檔案 model/user.rb

class User < ApplicationRecord
  authenticates_with_sorcery!
  attr_accessor :password, :password_confirmation

  validates_presence_of :email, message: "郵箱不能為空"
  validates :email, uniqueness: true
  validates_presence_of :password, message: "密碼不能為空",
    if: :need_validate_password
  validates_presence_of :password_confirmation, message: "密碼確認不能為空",
    if: :need_validate_password
  validates_confirmation_of :password, message: "密碼不一致",
    if: :need_validate_password
  validates_length_of :password, message: "密碼最短6位",
    minimum: 6,
    if: :need_validate_password

  private

  def need_validate_password
    self.new_record? ||
      (!self.password.nil? or !self.password_confirmation.nil?)
  end
end

配置檔案 app/controllers/users_controller.rb

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  #使用者註冊
  def create
    @user = User.new(params.require(:user)
      .permit(:email, :password, :password_confirmation))

    if @user.save
      flash[:notice] = '註冊成功,請登入'
      redirect_to new_session_path
    else
      render action: :new
    end
  end

  #使用者密碼修改
  def change_password
    if current_user.valid_password?(params[:old_password])
      current_user.password_confirmation = params[:password_confirmation]

      if current_user.change_password!(params[:password])
        render json: {status: 'ok', msg: '密碼修改成功'}
      else
        render json: {status: 'error', msg: current_user.errors.messages.values.flatten}
      end
    else
      render json: {status: 'error', msg: '舊密碼不正確'}
    end
  end

  #使用者傳送忘記密碼郵件,這裡需要對郵件進行配置
  def send_forget_password_email
    @user = User.find_by(email: params[:email])
    if @user
      @user.deliver_reset_password_instructions!
      render json: {status: 'ok', msg: "重置密碼的連結已傳送到上面的郵箱"}
    else
      render json: {status: 'error', msg: '指定郵箱不存在'}
    end
  end

  #密碼重置
  def reset_password
    @user = User.load_from_reset_password_token(params[:token])
    if @user
      @user.password_confirmation = params[:password_confirmation]
      if @user.change_password!(params[:password])
        render json: {status: 'ok', msg: '密碼修改成功'}
      else
        render json: {status: 'error', msg: @user.errors.messages.values.flatten}
      end
    else
      render json: {status: 'error', msg: 'token不正確或者已過期'}
    end
  end
end

配置檔案 app/views/users/new.html.erb

<h1>使用者註冊</h1>
<div>
  <% unless @user.errors.empty? %>
    <ul>
      <% @user.errors.messages.values.flatten.each do |error| %>
        <li><%= error %></li>
      <% end -%>
    </ul>
  <% end -%>
</div>
<%= form_for :user, url: users_path, method: :post, html: { class: 'form-horizontal', id: "user_form"} do |f| %>
  <div class="form-group">
    <div class="col-lg-12">郵箱 *</div>
    <div class="col-lg-12">
      <%= f.text_field :email, class: "form-control" %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">密碼 *</div>
    <div class="col-lg-12">
      <%= f.password_field :password, class: "form-control" %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">密碼確認 *</div>
    <div class="col-lg-12">
      <%= f.password_field :password_confirmation, class: "form-control" %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">
      <div class="text-center">
        <a href="<%= new_session_path %>">登入</a>
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">
      <input type="submit" name="create_user_submit" value="建立賬戶" class="col-xs-12 btn btn-primary">
    </div>
  </div>
<% end %>

配置檔案 app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  before_action :auth_user, except: [:destroy]

  def new
  end

  def create
    #login是sorcery的登入方法
    if user = login(params[:email], params[:password])
      flash[:notice] = '登陸成功'
      redirect_to root_path
    else
      flash[:notice] = "郵箱或者密碼錯誤"
      redirect_to new_session_path
    end
  end

  def destroy
    #logout是sorcery的退出方法
    logout
    flash[:notice] = "退出登陸"
    redirect_to root_path
  end

  private

  def auth_user
    #logged_in?是sorcery的判斷登入方法
    if logged_in?
      redirect_to root_path
    end
  end
end

配置檔案 app/views/sessions/new.html.erb

<h1>使用者登入</h1>
<%= form_tag sessions_path, method: :post, class: "form-horizontal" do %>
  <div class="form-group">
    <div class="col-lg-12">郵箱</div>
    <div class="col-lg-12">
      <%= text_field_tag :email, nil, class: "form-control" %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">密碼</div>
    <div class="col-lg-12">
      <%= password_field_tag :password, nil, class: "form-control" %>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">
      <div class="text-center">
        <a href="<%= new_user_path %>">註冊</a>
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="col-lg-12">
      <input type="submit" name="sign_submit" value="登陸" class="col-xs-12 btn btn-primary">
    </div>
  </div>
<% end %>

參考資料:
- gem sorcery的使用
- 參考部落格原話:我的github上一個專案用的這個gem,懶得研究的同學直接去我我的github上面copy程式碼吧…
- https://github.com/mypassword99/my-shop
- Sorcery具體使用

相關文章