课程目标
接下来会以一个“讨论版”为主题,实际让同学了解如何“从零打造”手写出一个网站。
这个讨论版会有:
- 使用者注册以及登录功能
- 开新讨论区、留言功能
- 加入讨论区、退出讨论区功能
- 权限管理功能
- 热门文章排序功能
- 了解什么是 RESTful
当你把这次应用程序实作完成,并上线后,你会学会:
- 怎么把需求变成会动的 Rails 应用程序
- 如何在 Rails 套版
- 如何制作使用者登录功能
- 重要 Rails 观念以及 API
- CRUD
- RESTful
- member, collection
- helper
- partial
- scope
- 找到 Rails 第三方 gem 的技巧
- 要怎么把你的应用程序放到网络上
这几乎是一个 Rails 新手应该学的一切了,有了这些基础技巧之后,你再学更难的技巧,或者是要自己做出复杂的东西,就不会那么容易失败了。
课程复盘
1. 建立一个新 Rails 项目,并建立 git 做版本控制
1 2 3 4 5
| rails new rails101 cd rails101 git init git add . git commit -m "Initial Commit"
|
2. 帮 Rails 项目穿上衣服“Bootstrap”
1 2 3 4 5 6 7 8 9 10
| gem 'bootstrap-sass' bundle install mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss @import "bootstrap-sprockets"; @import "bootstrap"; git add . git commit -m "add bootstrap to project"
|
3. 套用 Bootstrap 的 html 样式
(1)新增 navbar
1 2
| mkdir app/views/common touch app/views/common/_navbar.html.erb
|
填入
app/views/common/_navbar.html.erb1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <nav class="navbar navbar-default" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="/">Rails 101</a> </div>
<div class="collapse navbar-collapse"> <ul class="nav navbar-nav navbar-right"> <li> <%= link_to("登录", '#') %> </li> </ul> </div> </div> </nav> <!-- 以上部分,不需要记忆 -->
|
PS:
class1 2 3 4 5 6 7 8 9 10 11
| 前者是 100% 宽度布局,后者是 固定宽度 布局。 ```<div class="collapse navbar-collapse">```中 collapse的用法: 这是Bootstrap 折叠(Collapse)插件,为了给导航栏添加响应式特性,要折叠的内容必须包裹在带有 class .collapse、.navbar-collapse 的 <nav> 中。 参考:[Collapse](http://v3.bootcss.com/javascript/#collapse)。 对应导航栏务必使用 <nav> 元素,或者,如果使用的是通用的 <div> 元素的话,务必为导航条设置 role="navigation" 属性,这样能够让使用辅助设备的用户明确知道这是一个导航区域。 (2)新增 footer 新增 footer
|
touch app/views/common/_footer.html.erb
1 2 3 4 5 6 7 8 9 10 11
| 填入 ```ruby app/views/common/_footer.html.erb <footer class="container" style="margin-top: 100px;"> <p class="text-center">Copyright ©2017 Rails101 <br>Design by <a href="http://courses.growthschool.com/courses/rails-101/" target=_new>xdite</a> </p> </footer>
|
以上部分,不需要记忆
(3)修改全域 HTML 样式 application.html.erb
app/views/layouts/application.html.erb1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <!DOCTYPE html> <html> <head> <title>Rails101</title> <%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <div class="container-fluid"> <%= render "common/navbar" %> <%= yield %> </div>
<%= render "common/footer" %>
</body> </html>
|
PS:上述代码
部分,如下命令的用途:
<%= csrf_meta_tags %>
<%= stylesheet_link_tag ‘application’, media: ‘all’, ‘data-turbolinks-track’: ‘reload’ %>
<%= javascript_include_tag ‘application’, ‘data-turbolinks-track’: ‘reload’ %>
(4)产生一个新的空 Hello World 页面
1 2
| rails g controller welcome touch app/views/welcome/index.html.erb
|
填入
修改 config/routes.rb,改成以下内容
config/routes.rb1 2 3
| Rails.application.routes.draw do root 'welcome#index' end
|
(5) git 进度存档
1 2
| git add . git commit -m "add bootstrap html"
|
4. 制作漂亮的“提示信息” (以下内容无需记忆)
(1)在 require_tree 上加入 //= require bootstrap/alert
app/assets/javascripts/application.js1 2 3 4 5 6
| ... (一堆注解) //= require jquery //= require jquery_ujs //= require turbolinks +//= require bootstrap/alert //= require_tree .
|
(2)新增 app/views/common/_flashes.html.erb
1
| touch app/views/common/_flashes.html.erb
|
填入
app/views/common/_flashes.html.erb1 2 3 4 5 6 7 8
| <% 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 %>
|
这里的user_facing_flashes和flash_class(key) 会在相应的flashes.helper.rb中定义。
(3)加入 app/helpers/flashes_helper.rb
1
| touch app/helpers/flashes_helper.rb
|
加入以下内容:
app/helpers/flashes_helper.rb1 2 3 4 5 6 7 8 9 10 11 12 13
| 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
|
上述代码每一行具体是什么意思?
PS:
这里的.freeze和.fetch以及flash.to_hash.slice “alert”, “notice”, “warning” 的用法:
(4)在 application.html.erb 内加入 flash 这个 partial
在 <%= yield %> 前加入 <%= render “common/flashes” %>
app/views/layouts/application.html.erb1 2
| <%= render "common/flashes" %> <%= yield %>
|
(5)git 存档
1 2
| git add . git commit -m "add bootstrap flash function"
|
5. 经典四部曲:model-controller-action-view-routes
以建立讨论群的架构为例。
(1)建立model和相应的数据库
1 2
| rails g model group title:string description:text rake db:migrate
|
提示:model的名称一般为单数(group),每次产生 migration 之后都要运行 rake db:migrate,然后生成的数据表名称一般为复数。
(2)产生 groups controller
1
| rails g controller groups
|
提示:controller的名称一般为复数(groups)
(3)建立 index action
app/controllers/groups_controller.rb1 2 3 4 5
| class GroupsController < ApplicationController def index @groups = Group.all end end
|
(4)建立 index 的 view
app/views/groups/index.html.erb```1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 加入如下内容: ```ruby 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>
|
这里的
1
| <% @groups.each do |group| %>
|
中@groups也可以看出从@groups数据表名称为复数。
(5)在 routing 上挂上 groups
config/routes.rb1 2 3 4
| Rails.application.routes.draw do + resources :groups root 'welcome#index' end
|
这个网站讲解Bootstrap更为透彻:http://www.runoob.com/
6. 输入框与栅格系统
app/views/groups/new.html.erb1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div class="col-md-4 col-md-offset-4"> <h2>新增讨论版</h2>
<hr> <%= form_for @group do |f| %>
标题 <%= f.text_field :title %> <br> 叙述 <br> <%= f.text_area :description %> <br>
<%= f.submit "Submit", :disable_with => 'Submitting...' %> <% end %>
</div>
|
上述代码中,col-md-4和col-md-offset-4是Bootstrap响应式格栅系统。col-md-offset-*是 列偏移。
使用 .col-md-offset- 类可以将列向右侧偏移。这些类实际是通过使用 选择器为当前元素增加了左侧的边距(margin)。例如,.col-md-offset-4 类将 .col-md-4 元素向右侧偏移了4个列(column)的宽度。
示例:
1 2 3 4 5 6 7 8 9 10 11
| <div class="row"> <div class="col-md-4">.col-md-4</div> <div class="col-md-4 col-md-offset-4">.col-md-4 .col-md-offset-4</div> </div> <div class="row"> <div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div> <div class="col-md-3 col-md-offset-3">.col-md-3 .col-md-offset-3</div> </div> <div class="row"> <div class="col-md-6 col-md-offset-3">.col-md-6 .col-md-offset-3</div> </div>
|
7. Group CRUD
groups_controller.rb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| class GroupsController < ApplicationController def index @groups = Group.all end def new @group = Group.new end def show @group = Group.find(params[:id]) end def edit @group = Group.find(params[:id]) end def create @group = Group.new(group_params) if @group.save redirect_to groups_path else render :new end end def update @group = Group.find(params[:id]) if @group.update(group_params) redirect_to groups_path, notice: 'Update Success' else render :edit end end def destroy @group = Group.find(params[:id]) @group.destroy redirect_to groups_path, alert: 'Group deleted' end private def group_params params.require(:group).permit(:title, :description) end end
|
8. 在 Group model 加入“标题限制”
app/models/group.rb1 2 3
| class Group < ApplicationRecord validates :title, presence: true end
|
9. 错误提示消息
app/views/groups/edit.html.erb1 2 3 4 5 6 7
| <% if @group.errors.any? %> <ul> <% @group.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> <% end %>
|
10. 共用“表单”
app/views/groups/edit.html.erb1 2 3 4 5 6
| <div class="col-md-4 col-md-offset-4"> <h2>编辑讨论版</h2> <hr>
<%= render "form" %> </div>
|
打开 Gemfile,然后新增一行 gem ‘simple_form’
Gemfile1 2
| gem 'bootstrap-sass' + gem 'simple_form'
|
然后执行
安装 gem。
安装 simple_form for bootstrap 的设定
执行:
1
| rails generate simple_form:install --bootstrap
|
这个指令会产生两个文件
- config/initializers/simple_form.rb
- config/initializers/simple_form_bootstrap.rb (这个是 Bootstrap 表单的“布景”)
把内容改成
app/views/groups/_form.html.erb1 2 3 4 5 6 7
| <%= 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: "Submiting..." } %> <% end %>
|
simple_form 提供的错误信息,直接是在栏位旁边的。更明显更直观。
1 2
| git add . git commit -m "replace form with simple_form bootstrap template"
|
12. 符号的用法
下面2个代码效果相同
1 2 3
| <%= f.label "title", :class => "string optional control-label" %> <%= f.label "title", class: "string optional control-label" %> # 注意冒号与双引号直接的空格,:key => value == key: value 只是语法糖而已
|