⽤ Bootstrap 做⼀個「有 Nav bar 」的 layout
首先寫測試來檢查畫面上是否有 Nav bar 的 CSS,這裡我們要撰寫 Feature spec,要搭配一個 gem Capybara。
安裝 & 配置 Capybara
group :development do
...
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
gem 'capybara', '~> 2.11.0' # <--
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
bundle install
。
編輯 spec/rails_helper.rb
加入兩行 require:
# Add additional requires below this line. Rails is not loaded until this point!
Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |file| require file }
require 'capybara/rspec' # <--
require 'capybara/rails' # <--
# Requires supporting ruby files with custom matchers and macros, etc, in
撰寫測試檢查 Navbar
mkdir -p spec/features
touch spec/features/home_spec.rb
require "rails_helper"
RSpec.describe "Home" do
scenario "has navbar element" do
visit root_url
expect(page).to have_css "nav.navbar"
end
end
跑測試:
$ rspec spec/features/home_spec.rb
F
Failures:
1) Home has navbar element
Failure/Error: expect(page).to have_css "nav.navbar"
expected to find css "nav.navbar" but there were no matches
# ./spec/features/home_spec.rb:7:in `block (2 levels) in <top (required)>'
Finished in 0.19417 seconds (files took 1.8 seconds to load)
1 example, 1 failure
找不到有 navbar
class 的 nav
元素。
修改 app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Classroom</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">
<%= render "common/navbar" %>
<div class="row">
<%= yield %>
</div>
</div>
</body>
</html>
rspec spec/features/home_spec.rb
F
Failures:
1) Home has navbar element
Failure/Error: <%= render "common/navbar" %>
ActionView::Template::Error:
Missing partial common/_navbar with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in:
* "/Users/ana/dev/classroom/app/views"
# ./app/views/layouts/application.html.erb:13:in `_app_views_layouts_application_html_erb__3350428300626981296_70296580724620'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/etag.rb:25:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/conditional_get.rb:25:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/head.rb:12:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/session/abstract/id.rb:222:in `context'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/session/abstract/id.rb:216:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:36:in `call_app'
# /Users/Mac/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:24:in `block in call'
# /Users/Mac/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:24:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/method_override.rb:22:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/runtime.rb:22:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/sendfile.rb:111:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/engine.rb:522:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:68:in `block in call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `each'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `call'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/mock_session.rb:30:in `request'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/test.rb:244:in `process_request'
# /Users/Mac/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/test.rb:58:in `get'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:61:in `process'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:36:in `process_and_follow_redirects'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:22:in `visit'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/driver.rb:43:in `visit'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/session.rb:246:in `visit'
# /Users/Mac/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/home_spec.rb:5:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# ActionView::MissingTemplate:
# Missing partial common/_navbar with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in:
# * "/Users/ana/dev/classroom/app/views"
# ./app/views/layouts/application.html.erb:13:in `_app_views_layouts_application_html_erb__3350428300626981296_70296580724620'
Finished in 0.16901 seconds (files took 1.79 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/features/home_spec.rb:4 # Home has navbar element
找不到 Partial。
新增 app/views/common/_navbar.html.erb
Partial
mkdir -p app/views/common/
touch 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="/">Classroom</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">
<li class="active">
<%= link_to("Courses", courses_path) %>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<% if !current_user %>
<li><%= link_to("Login", new_user_session_path) %></li>
<% else %>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Hi!, <%= current_user.email %>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<%= link_to("Logout", destroy_user_session_path, method: :delete) %>
</li>
</ul>
</li>
<% end %>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
跑測試
$ rspec spec/features/home_spec.rb
F
Failures:
1) Home has navbar element
Failure/Error: <% if !current_user %>
ActionView::Template::Error:
undefined local variable or method `current_user' for #<#<Class:0x007fcbc4fe50c8>:0x007fcbc4fee920>
Did you mean? current_page?
# ./app/views/common/_navbar.html.erb:16:in `_app_views_common__navbar_html_erb___2589854774641260496_70256626731280'
# ./app/views/layouts/application.html.erb:13:in `_app_views_layouts_application_html_erb___3804411040484863998_70256581682180'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/etag.rb:25:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/conditional_get.rb:25:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/head.rb:12:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/session/abstract/id.rb:222:in `context'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/session/abstract/id.rb:216:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:36:in `call_app'
# /Users/ana/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:24:in `block in call'
# /Users/ana/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/rack/logger.rb:24:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/method_override.rb:22:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/runtime.rb:22:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/sendfile.rb:111:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/railties-5.0.0.1/lib/rails/engine.rb:522:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:68:in `block in call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `each'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-2.0.1/lib/rack/urlmap.rb:53:in `call'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/mock_session.rb:30:in `request'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/test.rb:244:in `process_request'
# /Users/ana/.gem/ruby/2.3.1/gems/rack-test-0.6.3/lib/rack/test.rb:58:in `get'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:61:in `process'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:36:in `process_and_follow_redirects'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/browser.rb:22:in `visit'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/rack_test/driver.rb:43:in `visit'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/session.rb:246:in `visit'
# /Users/ana/.gem/ruby/2.3.1/gems/capybara-2.11.0/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/home_spec.rb:5:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# NameError:
# undefined local variable or method `current_user' for #<#<Class:0x007fcbc4fe50c8>:0x007fcbc4fee920>
# Did you mean? current_page?
# ./app/views/common/_navbar.html.erb:16:in `_app_views_common__navbar_html_erb___2589854774641260496_70256626731280'
Finished in 0.18746 seconds (files took 1.89 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/features/home_spec.rb:4 # Home has navbar element
找不到 current_user
,current_user
是 devise gem 實現的一個方法,現在來安裝配置 devise。
安裝 devise
修改 Gemfile
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
gem 'devise', '~> 4.2.0' # <--
然後執行 bundle install
。
接著配置 devise:
rails g devise:install
rails g devise user
rake db:migrate
在 Controller 即 View 就有這些方法可以用:
before_action :authenticate_user!
user_signed_in?
current_user
user_session
測試就跑通了:
$ rspec spec/features/home_spec.rb
.
Finished in 0.20361 seconds (files took 1.99 seconds to load)
1 example, 0 failures
成品
成品會長這樣