8 分鐘閱讀

[ Chapter 1. 環境安裝 ]

1-1 確認ruby版本

ruby -v

1-2 安裝 gem rails 4.2.0

gem install rails -v 4.2.0 --no-ri

1-3 建立一個新 Rails 專案 ( 版本 4.2.0 )

rails _4.2.0_ new rails101 -T

1-4 進入新建好的專案, 並建立 git 做版本控制

cd rails101
git init
git add .
git commit -m "Initial Commit"

[ Chapter 2. 建立基礎網站(僅架構) ]

2-1 建立一個討論版(group)

2-1.1 建立一個 controller: groups (要加s)

rails g controller groups

2-1.2 建立一個 Model: group (不加s)

rails g model group title:string description:text

2-1.3 將資料庫建立起來

rake db:migrate

2-1.4 設定 routes 並建立 groups/index 為首頁 ( 內容是 Hello World! )

# [path] config/routes.rb
+ root 'groups#index' # 這行代表把 localhost:3000/groups 這個網址設成首頁
+ resources :groups

這時候啟動 rails s 前往 http://localhost:3000/

2-2 建立必要頁面

2-2.1 設定 action ‘index’

# [path] app/controllers/groups_controller.rb
+ def index
+ end

2-2.2 設定 groups/index.html.erb

<!-- [path] app/views/groups/index.html.erb -->
<h1>Hello World!</h1>

2-3 套入前端套件 Bootstrap

2-3.1 安裝 gem ‘bootstrap-sass’

https://github.com/twbs/bootstrap-sass

# [path] Gemfile
+ gem 'bootstrap-sass'

執行 bundle install

2-3.2 將 Bootstrap 的 CSS 套件裝進專案裡面

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.css.scss
/* app/assets/stylesheets/application.css.scss */
+ @import "bootstrap-sprockets";
+ @import "bootstrap";

重開 rails s

2-4 幫你的專案裝上 Navbar 跟 Footbar

2-4.1 新增兩檔案

<!-- 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="/">Rails 101</a>
    </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>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>
<!-- [path] app/views/common/_footer.html.erb-->
<footer class="container" style="margin-top: 100px;">
  <p class="text-center">Copyright ©2016 Rails101
    <br>Design by <a href="courses.growthschool.com/courses/rails-101/" target=_new>xdite</a>
  </p>
</footer>

2-4.2 修改 app/views/layouts/application.html.erb

<!-- [path] app/views/layouts/application.html.erb --> 
- <%= yield %>
+ <div class="container-fluid">
+   <%= render "common/navbar" %>
+   <%= yield %>
+ </div>
+ <%= render "common/footer" %>

2-5 建立 notice_message

2-5.1 將 Boostrap 的 js 套件: alert 裝進專案裡面

// [path] app/assets/javascripts/application.js
+//= require bootstrap/alert

2-5.2 建立 helper: notice_message

# [path] app/helpers/application_helper.rb
+ def notice_message
+   alert_types = { notice: :success, alert: :danger }
+
+   close_button_options = { class: "close", "data-dismiss" => "alert", "aria-hidden" => true }
+   close_button = content_tag(:button, "×", close_button_options)
+
+   alerts = flash.map do |type, message|
+     alert_content = close_button + message
+
+     alert_type = alert_types[type.to_sym] || type
+     alert_class = "alert alert-#{alert_type} alert-dismissable"
+
+     content_tag(:div, alert_content, class: alert_class)
+   end
+
+   alerts.join("\n").html_safe
+ end

2-5.3 把 notice_message 放進 application.html.erb 裡面

<!-- [path] app/views/layouts/application.html.erb -->
<div class="container-fluid">
  <%= render "common/navbar" %>
+ <%= notice_message %>
  <%= yield %>
</div>

2-5.4 如何使用 notice_message 的功能

# [path] app/controllers/groups_controller.rb
class GroupsController < ApplicationController
  def index
  +  flash[:notice] = "早安!你好!"
 +  flash[:warning] = "這是 warning 訊息!"
  +  flash[:alert] = "晚安!該睡了!"
  end
end

[ Chapter 3. 手動實作出討論版的 CRUD 功能 ]

3-1 安裝 gem “simple_form”

參考:SimpleForm github

3-1.1 加入gem

# [path] gemfile
+ gem "simple_form"
bundle install

3-1.2 安裝 simple_form for bootstrap 的設定

rails generate simple_form:install --bootstrap

重開 rails s

3-2 可以總覽所有討論版 ( 建立 action index )

3-2.1 建立 action index

# [path] app/controllers/groups_controller.rb
  def index
+   @groups = Group.all
  end

3-2.2 建立 index.html.erb

<!-- [path] app/views/groups/index.html.erb -->
<div class="col-md-12">
  <div class="group">
    <%= link_to("New group", new_group_path, class: "btn btn-primary pull-right") %>
  </div>
  <table class="table table-hover">
    <thead>
      <tr>
        <td>#</td>
        <td>Title</td>
        <td>Description</td>
      </tr>
    </thead>
    <tbody>
      <% @groups.each do |group| %>
        <tr>
          <td>#</td>
          <td><%= link_to(group.title, group_path(group)) %></td>
          <td><%= group.description %></td>
          <td>
              <%= link_to("Edit", edit_group_path(group), class: "btn btn-sm btn-default")%>
              <%= link_to("Delete", group_path(group),    class: "btn btn-sm btn-default", 
                          method: :delete, data: { confirm: "Are you sure?" } )%>
          </td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>

3-2 可以新增討論版 ( 建立 action new, create )

3-2.1 action ‘new’

# [path] app/controllers/groups_controller.rb
+ def new
+   @group = Group.new
+ end

3-2.2 erb ‘new’

<!-- [path] app/views/groups/new.html.erb -->
<div class="col-md-4 col-md-offset-4">
  <h1>新增討論版</h1>

  <hr>
  <%= simple_form_for @group do |f| %>
    <div class="form-group">
      <%= f.input :title, input_html: { class: "form-control"} %>
      <%= f.input :description, input_html: { class: "form-control"} %>
    </div>
    <%= f.submit "Submit", class: "btn btn-primary", data: { disable_with: "Submitting..." } %>
  <% end %>
</div>

3-2.3 action ‘create’

# [path] app/controllers/groups_controller.rb
+ def create
+   @group = Group.create(group_params)
+
+   if @group.save
+     redirect_to groups_path
+   else
+     render :new
+   end
+ end
  
+ private
+ 
+ def group_params 
+   params.require(:group).permit(:title, :description)
+ end

3-3 可以瀏覽單一討論版 ( 建立 action show )

3-3.1 action show

# [path] app/controllers/groups_controller.rb
+ def show
+   @group = Group.find(params[:id])
+ end
<!-- [path] app/views/groups/show.html.erb -->
<div class="col-md-12">
  <div class="group">
    <%= link_to("Edit", edit_group_path(@group), class: "btn btn-primary pull-right")%>
  </div>
  <h2><%= @group.title %></h2>
  <p><%= @group.description %></p>
</div>

3-4 修改討論版與儲存已改的設定 ( 建立 action edit, update )

3-4.1 action edit

# [path] app/controllers/groups_controller.rb
+ def edit
+  @group = Group.find(params[:id])
+ end
<!-- [path] app/views/groups/edit.html.erb -->
<div class="col-md-4 col-md-offset-4">
  <h1>修改討論版</h1>

  <hr>
  <%= simple_form_for @group do |f| %>
    <div class="form-group">
      <%= f.input :title %>
      <%= f.input :description %>
    </div>
    <%= f.submit "Submit", class: "btn btn-primary", data: { disable_with: "Submitting..." } %>
  <% end %>
</div>

3-4.2 action update

# [path] app/controllers/groups_controller.rb
+ def update
+   @group = Group.find(params[:id])

+   if @group.update(group_params)
+     redirect_to groups_path, notice: "修改討論版成功"
+   else
+     render :edit
+   end
+ end

<!-- [path] app/views/groups/edit.html.erb -->
<div class="col-md-4 col-md-offset-4">
  <h1>修改討論版</h1>

  <hr>
  <%= simple_form_for @group do |f| %>
    <div class="form-group">
      <%= f.input :title %>
      <%= f.input :description %>
    </div>
    <%= f.submit "Submit", class: "btn btn-primary", data: { disable_with: "Submitting..." } %>
  <% end %>
</div>

3-5 將討論版刪除 ( action destroy )

3-5.1 建立 action destroy

# [path] app/controllers/groups_controller.rb
+ def destroy
+   @group = Group.find(params[:id])
+   @group.destroy
+   redirect_to groups_path, alert: "討論版已刪除"
+ end

3-6 資料驗證: title 不能空白

# [path] app/models/group.rb
+ validates :title, presence: true

[ Chapter 4. 可以在討論版裡發表文章 ]

4-1 建立 post 相關的 controller, model 關聯 與 routing 設定

4-1.1 建立 Controller: posts (要加s)

rails g controller posts

4-1.2 建立 Model: post (不加s)

rails g model post content:text group_id:integer

4-1.3 將 post 的資料庫建立起來

rake db:migrate

4-2 建立 group 與 post 之間 model 的關聯性 (relationship)

# [path] app/models/group.rb
+ has_many :posts
# [path] app/models/post.rb
+ belongs_to :group

4-3 routing 設定

# [path] config/routes.rb
- resources :groups
+ resources :groups do
+   resources :posts
+ end

4-4 可以在 group 的 show 頁面顯示所擁有的 post

# [path] app/controllers/groups_controller.rb
 def show
    @group = Group.find(params[:id])
+   @posts = @group.posts
  end
<!-- [path] app/views/groups/show.html.erb -->
<div class="col-md-12">
  <div class="group">
+   <%= link_to("New Post", new_group_post_path(@group), class: "btn btn-warning pull-right") %>
    <%= link_to("Edit", edit_group_path(@group), class: "btn btn-primary pull-right")%>
  </div>
  <h2><%= @group.title %></h2>
  <p><%= @group.description %></p>

+ <table class="table">
+   <thead>
+     <tr>
+       <th>文章</th>
+       <th colspan="2"></th>
+     </tr>
+   </thead>
+   <tbody>
+     <% @posts.each do |post| %>
+       <tr>
+         <td><%= post.content %></td>
+         <td>
+           <%= link_to("Edit", edit_group_post_path(post.group, post),
+                       class: "btn btn-default btn-xs")%>
+           <%= link_to("Delete", group_post_path(post.group, post),
+                       class: "btn btn-default btn-xs ", method: :delete, 
+                       data: { confirm: "Are you sure?" } )%>
+         </td>
+       </tr>
+     <% end %>
+   </tbody>
+ </table>
</div>

4-5 可以在討論版裡新增 post ( action new, create )

4-5.1 action ‘new’

# [path] app/controllers/posts_controller.rb
+ def new
+   @group = Group.find(params[:group_id])
+   @post = @group.posts.new
+ end
# [path] app/views/posts/new.html.erb
<h1 class="text-center">新增文章</h1>

<div class="col-md-4 col-md-offset-4">
  <%= simple_form_for [@group,@post] do |f| %>
    <div class="form-group">
      <%= f.input :content, input_html: { class: "form-control"} %>
    </div>
    <div class="form-actions">
      <%= f.submit "Submit", disable_with: "Submiting...", class: "btn btn-primary"%>
    </div>
  <% end %>
</div>

4-5.2 action ‘create’

# [path] app/controllers/posts_controller.rb
+ def create
+   @group = Group.find(params[:group_id])
+   @post = @group.posts.build(post_params)

+   if @post.save
+     redirect_to group_path(@group), notice: "新增文章成功!"
+   else
+     render :new
+   end
+ end

+ private

+ def post_params
+   params.require(:post).permit(:content)
+ end

4-6 可以修改並更新 post ( action edit, update )

4-6.1 設定 action ‘edit’ 與 action ‘update’

# [path] app/controllers/posts_controller.rb
+ def edit
+   @group = Group.find(params[:group_id])
+   @post = @group.posts.find(params[:id])
+ end

+  def update
+   @group = Group.find(params[:group_id])
+   @post = @group.posts.find(params[:id])

+   if @post.update(post_params)
+     redirect_to group_path(@group), notice: "文章修改成功!"
+   else
+     render :edit
+   end
+ end
<!-- [path] app/views/posts/edit.html.erb -->
<h1 class="text-center">修改文章</h1>

<div class="col-md-4 col-md-offset-4">
  <%= simple_form_for [@group,@post] do |f| %>
    <div class="form-group">
      <%= f.input :content, input_html: { class: "form-control"} %>
    </div>
    <div class="form-actions">
      <%= f.submit "Submit", disable_with: "Submiting...", class: "btn btn-primary"%>
    </div>
  <% end %>
</div>

4-7 可以刪除 post ( action destroy )

4-7.1 action ‘destroy’

# [path] app/controllers/posts_controller.rb
+ def destroy
+   @group = Group.find(params[:group_id])
+   @post = @group.posts.find(params[:id])
+
+   @post.destroy
+   redirect_to group_path(@group), alert: "文章已刪除"
+ end

4-8 資料驗證: post 必須要有內容才能儲存 / 新增

# [path] app/models/post.rb
+ validates :content, presence: true

4-9 用 before_action 來整理重複的程式碼

# [path] app/controllers/posts_controller.rb
+ before_action :find_group

  private
+ def find_group
+   @group = Group.find(params[:group_id])
+ end

將所有@group = Group.find(params[:group_id])刪掉(new edit create update destory)

before_action 可以用 only,指定某些 action 執行: before_action :find_group, only: [:edit, :update] 或者使用 except,排除某些 action 不執行:

[ Chapter 5. 第五章:(1) 建立基本使用者功能 ]

5-1 安裝 gem “devise”

https://github.com/plataformatec/devise

# [path] gemfile
gem "devise", "~> 3.4.1"

執行bundle install

5-2 設定 Devise

5-2.1 devise 安裝

rails g devise:install

5-2.2 建立 user 功能

rails g devise user
rake db:migrate

5-2.3 叫出(原本是隱藏的) devise views,以便未來可以客製化修改

rails g devise:views

5-2.4 重開rails server

rails s

[ Chapter 5. 第五章:(2) 客製化 devise => 新增一個 “name” 的欄位 ]

5-3 客製化欄位:name

5-3.1 客製化 devise => 新增一個 “name” 的欄位

rails g migration add_name_to_user
# [path] db/migrate/(一堆數字)_add_name_to_user.rb
  def change
+   add_column :users, :name, :string
  end
rake db:migrate

5-4 客製化 devise 的 views => 註冊頁面新增 name 欄位

# [path] app/views/devise/registrations/new.html.erb
  <div class="form-inputs">
    <%= f.input :email, required: true, autofocus: true %>
+   <%= f.input :name,  required: true %>
    <%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @validatable) %>
    <%= f.input :password_confirmation, required: true %>
  </div>

5-5 加入 strong_parameters 與 devise 整合的 hack

# [path] app/controller/application_controller.rb
+ before_filter :configure_permitted_parameters, if: :devise_controller?

+ protected

+  def configure_permitted_parameters
+   devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
+  end

5-6 navbar 上的 hi! email 改成 hi! name

# [path] app/views/common/_navbar.html.erb
-           Hi!, <%= current_user.email %>
+           Hi!, <%= current_user.name %>  

5-7 新增 “帳號設定” 功能,並能修改自己帳號的 name

<!-- [path] app/views/common/_navbar.html.erb -->
            <ul class="dropdown-menu">
+             <li> <%= link_to("帳號設定", edit_user_registration_path )%></li>
              <li> <%= link_to("登出", destroy_user_session_path, method: :delete) %> </li>
            </ul>
# [path] app/controller/application_controller
   def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
+   devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :password_confirmation, :current_password) }
   end
<!-- [path] app/views/devise/registrations/edit.html.erb -->
+   <%= f.input :name %>

留言