课程目标
两个主题:
- 什么是 Computational Thinking
- 如何把脑袋的点子落地变成代码
做第一个简单的网站:一个招聘网站。这个网站会跟 Rails 101 很像。你会发现,将脑袋的点子落地变成代码,并不是想像中的难
课程复盘
1. 实作 Admin 的 CRUD
1
| rails g controller admin::jobs
|
然后修改 routing
加入
config/routes.rb1 2 3
| namespace :admin do resources :jobs end
|
Step 2 : 实作 admin::jobs 的 CRUD
app/controllers/admin/jobs_controller.rb1 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 51 52
| class Admin::JobsController < ApplicationController before_action :authenticate_user!, only: [:new, :create, :update, :edit, :destroy] def show @job = Job.find(params[:id]) end def index @jobs = Job.all end def new @job = Job.new end def create @job = Job.new(job_params) if @job.save redirect_to admin_jobs_path else render :new end end def edit @job = Job.find(params[:id]) end def update @job = Job.find(params[:id]) if @job.update(job_params) redirect_to admin_jobs_path else render :edit end end def destroy @job = Job.find(params[:id]) @job.destroy redirect_to admin_jobs_path end private def job_params params.require(:job).permit(:title, :description) end end
|
新增 app/views/admin/jobs/index.html.erb
app/views/admin/jobs/index.html.erb1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div class="pull-right"> <%= link_to("Add a job", new_admin_job_path, :class => "btn btn-default" ) %> </div>
<br><br>
<table class="table table-bordered"> <% @jobs.each do |job| %> <tr> <td> <%= link_to(job.title, admin_job_path(job)) %> </td> <td> <%= link_to("Edit", edit_admin_job_path(job)) %> <%= link_to("Destroy", admin_job_path(job), :method => :delete, :data => { :confirm => "Are you sure?" }) %> </td> <td> <%= job.created_at %> </td> </tr> <% end %> </table>
|
新增 app/views/admin/jobs/new.html.erb
app/views/admin/jobs/new.html.erb1 2 3 4 5 6 7 8 9
| <h1> Add a job </h1>
<%= simple_form_for [:admin,@job] do |f| %>
<%= f.input :title %> <%= f.input :description %>
<%= f.submit "Submit" %> <% end %>
|
新增 app/views/admin/jobs/show.html.erb
app/views/admin/jobs/show.html.erb1 2 3 4 5 6
| <h1> <%= @job.title %> </h1>
<p> <%= simple_format(@job.description) %>
</p>
|
新增 app/views/admin/jobs/edit.html.erb
app/views/admin/jobs/edit.html.erb1 2 3 4 5 6 7 8 9
| <h1> Edit a job </h1>
<%= simple_form_for [:admin, @job ] do |f| %>
<%= f.input :title %> <%= f.input :description %>
<%= f.submit "Submit" %> <% end %>
|
Step 3: 把通往后台的选单加到下拉选单里:
可以这么做
app/views/common/_navbar.html.erb1 2
| <li> <%= link_to("Admin Panel", admin_jobs_path) %> </li> <li> <%= link_to("登出", destroy_user_session_path, method: :delete) %> </li>
|
Step 4: 限定只有 admin 才可以进后台
这时候我们要设计一个新的 method 叫 require_is_admin
app/controllers/admin/jobs_controller.rb1 2 3
| class Admin::JobsController < ApplicationController before_action :authenticate_user!, only: [:new, :create, :update, :edit, :destroy] before_action :require_is_admin
|
然后呢,再加入一个 require_is_admin 的条件
这里我们姑且把条件叫做 email 是“管理员的 email”(你可以改成你自己的)
app/controllers/admin/jobs_controller.rb1 2 3 4 5 6
| def require_is_admin if current_user.email != 'xdite@growth.school' flash[:alert] = 'You are not admin' redirect_to root_path end end
|
这样其他使用者,就不能进去 admin panel 了。
Step 5: 重构 admin 条件,放到 model 里面
接着呢, current_user.email != ‘xdite@growth.school’ 这样不是很好看,我们应该要把条件改成 if !current_user.admin?
这样比较美观。
所以呢,你可以把刚刚的那一段改成:
app/controllers/admin/jobs_controller.rb1 2 3 4 5 6
| def require_is_admin if !current_user.admin? flash[:alert] = 'You are not admin' redirect_to root_path end end
|
然后在 app/models/user.rb 加入一段 admin? 的自定义
app/models/user.rb1 2 3 4 5 6
| class User < ApplicationRecord def admin? email == 'xdite@growth.school' end
|
这样就做了一次“整理的动作”。这样以后你就可以自己重新定义里面的条件。
Step 6: 把 admin? 的条件改为由资料库判断
其实呢,admin 的这个条件,最好不要写死在代码中,比较好的方式,是记在数据库中。所以我们可以这样做:
1
| rails g migration add_is_admin_to_user
|
然后修改 migration 档,变成
db/migrate/xxxxx.rb1 2 3 4 5
| class AddIsAdminToUser < ActiveRecord::Migration[5.0] def change add_column :users, :is_admin, :boolean, default: false end end
|
再
接着我们再更改 app/models/user.rb 的 admin? 的定义,唯有 user 的 is_admin 这个栏位是 true 时,这个人才会被判定为 admin
app/models/user.rb1 2 3
| def admin? is_admin end
|
Step 7: 把你的帐户提升为 admin
不过我们系统现在都是一般 user 啊,所以我们要把系统刚刚建的 user 提升为 admin。
打开
输入:
1 2 3 4
| u = User.first u.is_admin = true u.save exit
|
这样你的 user 就会变成 admin 了
Step 8 限制只有 admin 才可以看到选单:
刚刚我们有做一个下拉选单嘛,应该要限制是 admin 才能看到下拉选单,那你现在就可以这样改了:
app/views/common/_navbar.html.erb1 2 3 4
| <% if current_user.admin? %> <li> <%= link_to("Admin Panel", admin_jobs_path) %> </li> <% end %> <li> <%= link_to("登出", destroy_user_session_path, method: :delete) %> </li>
|
Step 9: 把 require_is_admin 移到 application_controller
因为 require_is_admin 将来很多地方会用到,其实你是可以这样做的,把原本在 admin/jobs_controller.rb
app/controllers/admin/jobs_controller.rb1 2 3 4 5 6 7 8
| class Admin::JobsController < ApplicationController def require_is_admin if !current_user.admin? flash[:alert] = 'You are not admin' redirect_to root_path end end end
|
这段,搬到 application_controller.rb
app/controllers/application_controller.rb1 2 3 4 5 6 7 8 9 10
| class ApplicationController < ActionController::Base def require_is_admin if !current_user.admin? flash[:alert] = 'You are not admin' redirect_to root_path end end end
|
这样之后就更多人可以调用这个 method。
Step 10: git 存档
1 2
| git add . git commit -m "implement part 2"
|
2. 新增“薪资上限”“薪资下限”“联络 Email”
新增三个栏位:
- wage_upper_bound (数字)
- wage_lower_bound (数字)
- contact_email (字串)
1
| rails g migration add_more_detail_to_job
|
修改 db/migrate/XXX_add_more_detail_to_job.rb
db/migrate/XXX_add_more_detail_to_job.rb1 2 3 4 5 6 7
| class AddMoreDetailToJob < ActiveRecord::Migration[5.0] def change add_column :jobs, :wage_upper_bound, :integer add_column :jobs, :wage_lower_bound, :integer add_column :jobs, :contact_email, :string end end
|
然后
3. 新增栏位,并加上栏位白名单
新增以下栏位
1 2 3
| <%= f.input :wage_lower_bound, :label => "薪资下限" %> <%= f.input :wage_upper_bound, :label => "薪资上限" %> <%= f.input :contact_email %>
|
加上栏位白名单
既然新增了栏位,我们也要去改
- app/controllers/admin/jobs_controller.rb
- app/controllers/jobs_controller.rb
修改下面这一段,加入 :wage_upper_bound, :wage_lower_bound, :contact_email
1 2 3 4 5 6 7 8 9 10 11 12 13
| def job_params params.require(:job).permit(:title, :description, :wage_upper_bound, :wage_lower_bound, :contact_email) end ```
### 4. 薪资不能为空,最低薪资至少要大于 0
修改 app/models/job.rb
```ruby app/models/job.rb validates :wage_upper_bound, presence: true validates :wage_lower_bound, presence: true validates :wage_lower_bound, numericality: { greater_than: 0}
|
5. 新增 is_hidden 栏位到 Job
1
| rails g migration add_is_hidden_to_job
|
“预设为隐藏”
db/migrate/(一串数字)_add_is_hidden_to_job.rb1 2 3 4 5
| class AddIsHiddenToJob < ActiveRecord::Migration[5.0] def change add_column :jobs, :is_hidden, :boolean, default: true end end
|
修改所有 new/edit 的表单加上这个栏位
1
| <%= f.input :is_hidden %>
|
6. 让前后台的 Job List 长得不一样
修改
app/views/admin/jobs/index.html.erb1 2 3 4 5 6 7 8 9 10
| <td> <% if job.is_hidden %> (Hidden) <% else %> (Public) <% end %> <%= link_to(job.title, admin_job_path(job)) %> </td>
|
html.erb中直接用(Hidden)即可。
翻修到 Helper
在 View 里面写一堆 Ruby Code 实在太丑了,因此我们应该要把这段代码重构成 Helper 。
所以我们应该把刚刚那一段判断式砍掉,换成这样
app/views/admin/jobs/index.html.erb1 2 3 4 5
| <td> <%= render_job_status(job) %> <%= link_to(job.title, admin_job_path(job)) %> </td>
|
然后加入这一段:
app/helpers/jobs_helper.rb1 2 3 4 5 6 7 8 9 10
| module JobsHelper def render_job_status(job) if job.is_hidden "(Hidden)" else "(Public)" end end end
|
helper中用 “(Hidden)” 这里的字符串需要带双引号。
7. 哪里有好用的 Gems
https://www.ruby-toolbox.com/
http://railscasts.com/
8. ERB ( Embedded Ruby )
在 HTML 中嵌入 RUBY 语法的 ERB ( Embedded Ruby )。
1 2
| <%= @post.title %> # 显示 @post.title <% @post.title %> # 执行 @post.title
|
实际的例子:
1 2 3 4 5
| <% sum = 0 %> <% for i in 1..10 %> <% sum = sum + i %> <% end %> <%= sum %>
|
以上的代码表示:执行 1+2+3+4+5+6+7+8+9+10 的运算后,印出 55。
9. 利用 layout 可以做出不同的view
先建立新的layout admin app/views/layouts/admin.html.erb,然后在相应的controller中指定layout。
app/controllers/admin/jobs_controller.rb1 2 3 4
| class Admin::JobsController < ApplicationController before_action :authenticate_user!, only: [:new, :create, :update, :edit, :destroy] before_action :require_is_admin layout "admin"
|
10. 使用 FontAwesome
网站:http://fontawesome.io/icons/
安装 FontAwesome
1
| gem 'font-awesome-rails'
|
然后执行
接着修改 app/assets/stylesheets/application.scss,因为与外面有关。
加入:
也加入:
然后重开 rails server
接着我们要换掉 Helper,修改 app/helpers/jobs_helper.rb 当中的 render_job_status(job)
app/helpers/jobs_helper.rb1 2 3 4 5 6 7
| def render_job_status(job) if job.is_hidden content_tag(:span, "", :class => "fa fa-lock") else content_tag(:span, "", :class => "fa fa-globe") end end
|
11. 实作“被隐藏的工作”,不可以被查看到。
把 app/controllers/jobs_controller.rb 当中的 show action
改成
app/controllers/jobs_controller.rb1 2 3 4 5 6 7 8
| def show @job = Job.find(params[:id]) if @job.is_hidden flash[:warning] = "This Job already archived" redirect_to root_path end end
|
使用普通用户登录,在浏览器地址栏输入
1
| http://localhost:3000/jobs/x (x是被隐藏的工作的ID编号)
|
会提示不可查看。
12. erb文件中加入按钮
设计按钮,将“隐藏职缺”改成是一个按钮,按下去“隐藏职缺”,再按一次“显示职缺”
app/views/admin/jobs/index.html.erb1 2 3 4 5 6
| <% if job.is_hidden %> <%= link_to("Publish", publish_admin_job_path(job) , :method => :post, :class => "btn btn-xs btn-default") %> <% else %> <%= link_to("Hide", hide_admin_job_path(job), :method => :post, :class => "btn btn-xs btn-default") %> <% end %>
|
Bootstrap中的
1
| <%= link_to("Publish", publish_admin_job_path(job) , :method => :post, :class => "btn btn-xs btn-default") %>
|
自动显示为按钮button, 目标路径 publish_admin_job_path,动作 :method => :post。
13. routing 加上去 publish 与 hide
config/routes.rb1 2 3 4 5 6 7 8
| namespace :admin do resources :jobs do member do post :publish post :hide end end end
|
设计 publish / hide 的 action
所以你还得在 app/controllers/admin/jobs_controller.rb 加入:
app/controllers/admin/jobs_controller.rb1 2 3 4 5 6 7 8 9 10 11 12 13
| def publish @job = Job.find(params[:id]) @job.is_hidden = false @job.save redirect_to :back end def hide @job = Job.find(params[:id]) @job.is_hidden = true @job.save redirect_to :back end
|
但是controller中加入很多与数据库相关的操作不符合Rails 风格。将上述内容重构至model中。
首先到 app/models/job.rb 实作 publish! 与 hide!
app/models/job.rb1 2 3 4 5 6 7 8 9 10 11
| class Job < ApplicationRecord def publish! self.is_hidden = false self.save end def hide! self.is_hidden = true self.save end end
|
再去把 app/controllers/admin/jobs_controller.rb 中的两个 action 换掉:
app/controllers/admin/jobs_controller.rb1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def publish @job = Job.find(params[:id]) @job.publish! redirect_to :back end def hide @job = Job.find(params[:id]) @job.hide! redirect_to :back end
|
参考:Rails 风格指南