Ruby on Rails チュートリアルをやってみる:[第3章]静的なページの作成+α編
第3章
ここでは簡単なサンプルアプリケーションを開発して今後のチュートリアルを楽に理解できるようにしていくようです。
詳しく言うと静的なページを作成していくようです。自動化テストの雰囲気も掴んでいきましょう!
ここから難しくなるのか理解できない人はとばしてどんどん進んでくれとのこと。思い立ったときに戻ってこれば理解できているらしい・・・
ではプロジェクトをつくっていきます。
$ cd rails_projects $ rails new sample_app --skip-test-unit $ cd sample_app
ここでいきなり出てくる、--skip-test-unitというオプションはtestディレクトリを作成しないためのものです。別のフレームワークでテストを行うため、Railsのテストは不要だそうです。
Gemfileを変更していきます。今回、新たに2つのgemが出てきます。
/Gemfile
source 'https://rubygems.org' ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0 gem 'rails', '4.0.5' group :development, :test do gem 'sqlite3', '1.3.8' gem 'rspec-rails', '2.13.1' end group :test do gem 'selenium-webdriver', '2.35.1' gem 'capybara', '2.1.0' end gem 'sass-rails', '4.0.5' gem 'uglifier', '2.1.1' gem 'coffee-rails', '4.0.1' gem 'jquery-rails', '3.0.4' gem 'turbolinks', '1.1.1' gem 'jbuilder', '1.0.2' group :doc do gem 'sdoc', '0.3.20', require: false end group :production do gem 'pg', '0.15.1' gem 'rails_12factor', '0.0.2' end
gemを実際にインストール
$ bundle install --without production $ bundle update $ bundle install
このサンプルアプリケーションの重大なセキュリティ上の問題が発生する可能性を回避するために以下を記述します。
config/initializers/secret_token.rb
# Be sure to restart your server when you modify this file. # Your secret key is used for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. # You can use `rake secret` to generate a secure secret key. # Make sure your secret_key_base is kept private # if you're sharing your code publicly. require 'securerandom' def secure_token token_file = Rails.root.join('.secret') if File.exist?(token_file) # Use the existing token. File.read(token_file).chomp else # Generate a new token and store it in token_file. token = SecureRandom.hex(64) File.write(token_file, token) token end end SampleApp::Application.config.secret_key_base = secure_token
/.gitignore
# Ignore bundler config. /.bundle # Ignore the default SQLite database. /db/*.sqlite3 /db/*.sqlite3-journal # Ignore all logfiles and tempfiles. /log/*.log /tmp # Ignore other unneeded files. doc/ *.swp *~ .project .DS_Store .idea .secret
つぎにテストのためにRailsの設定をを変更します。
$ rails generate rspec:install
ここまで終わったらGitリポジトリを初期化します。
$ git init $ git add . $ git commit -m "Initial commit"
最初に、アプリケーションのルートディレクトリに置かれているREADMEファイルを書き換えます。
/README.rdoc
# Ruby on Rails チュートリアル:サンプルアプリケーション これは、以下のためのサンプルアプリケーションです。 [*Ruby on Rails Tutorial*](http://railstutorial.jp/) by [Michael Hartl](http://www.michaelhartl.com/).
次に、拡張子を.mdに変更し、コミットします。
$ git mv README.rdoc README.md $ git commit -am "Improve the README"
このチュートリアルは今後、このサンプルアプリケーションを使っていくそうです。GitHub上にリポジトリを作成し、プッシュしましょう。
$ git remote add origin https://github.com/<ユーザー名>/sample_app.git $ git push -u origin master
ここまで完了したらいよいよサンプルアプリケーションの開発です。
静的ページ
まずはGitでトピックブランチを作ります。これはイメージとしてメインではない枝分かれのようなもので、今回のように新しい機能やページを作成するときに使います。
git checkout -b static-pages
HomeページとHelpページ、Aboutページで使用するアクションをgenerateというスクリプトを使い、作成してみます。まずは最初の2つのアクションをつくります。
$ rails generate controller StaticPages home help --no-test-framework
先ほどと同じようにテストを自動生成しないようにしています。
ではアクセスしてみましょう。
$ rails server
サーバーを起動して、http://localhost:3000/static_pages_homeにアクセスしてみます。
今回の場合は、homeアクションが空のため、アクセスしても単に対応するビューが表示されるだけです。
このページで表示されている(Find me in app/views/static_pages/home.html.erb
)場所にビューがあるようです。
ここで一旦StaticPagesコントローラファイルをGitリポジトリに追加しておきます。
$ git add . $ git commit -m "Add a StaticPages controller"
最初のテスト
Railsチュートリアルではアプリケーションの振る舞いをテストしながら実装する手法を採用してます。この開発手法は、テスト駆動開発から派生した振舞駆動開発と知られてます。これから頻繁に使うツールは、結合テスト (integration test) と単体テスト (unit test) の2つだそうです。
この章では結合テストを使っていきます。結合テストはユーザーがアプリケーションを使う際の一連のアクションをシミュレーションします。
テスト駆動開発の定義とは、最初にテストを作成し、次にコードを作成することです。
この節では、RSpec gemによって提供されるrspecコマンドを使ってテストを実行します。
それではいくつかのコンテンツをHomeページに追加してみます。まず、静的なページに対する結合テストを生成します。
$ rails generate integration_test static_pages
これにより、spec/requestsディレクトリにstatic_pages_spec.rbが生成されます。
このファイルにコードを以下のように書いていきます。
spec/requests/static_pages_spec.rb
require 'spec_helper' describe "Static pages" do describe "Home page" do it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end end end
テストが正常に実行されるようにするためには、以下の1行をspec_helper.rbに追加する必要があります。
# This file is copied to spec/ when you run 'rails generate rspec:install' . . . RSpec.configure do |config| . . . config.include Capybara::DSL #追記 end
テストではさまざまなオプションが使用できます。今回は、コマンドラインでrspecコマンドを実行してみます。なお、bundle execをこのコマンドの前に置くことで、Gemfile内で定義された環境でRSpecが実行されるように指示できます。
$ bundle exec rspec spec/requests/static_pages_spec.rb
実行すると以下のように結果が帰ってくると思います。
内容は失敗したというものです。
Failures: 1) Static pages Home page should have the content 'Sample App' Failure/Error: expect(page).to have_content('Sample App') expected #has_content?("Sample App") to return true, got false # ./spec/requests/static_pages_spec.rb:9:in `block (3 levels) in <top (required)>' Finished in 0.95141 seconds 1 example, 1 failure Failed examples: rspec ./spec/requests/static_pages_spec.rb:7 # Static pages Home page should have the content 'Sample App' Randomized with seed 6905
テストをパスするために、Homeページのビューを書き換えてみます。
app/views/static_pages/home.html.erb
<h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p>
先ほどはテストでアクセスした際に、正しいコンテンツ(Sample App)が表示されていないとエラーが出たため、トップの見出しをSample Appに変更しました。
もう一度テストを実行してみます。
$ bundle exec rspec spec/requests/static_pages_spec.rb
今度は赤色(エラー)が出ないはずです。
Helpページも同様にやっていきます。
spec/requests/static_pages_spec.rb
require 'spec_helper' describe "Static pages" do describe "Home page" do it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end end describe "Help page" do it "should have the content 'Help'" do visit '/static_pages/help' expect(page).to have_content('Help') end end end
テストを実行。
$ bundle exec rspec spec/requests/static_pages_spec.rb
同じくエラーが出ます。ビューを直しましょう。
app/views/static_pages/help.html.erb
<h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="http://railstutorial.jp/book">Rails Tutorial book</a>. </p>
もう一度テストを実行してみると、パスできるはずです。
では先ほどあえて先送りにしたAboutページをテスト駆動開発の手法で新しく追加します。
まずはAboutページのテストをつくります。
spec/requests/static_pages_spec.rb
require 'spec_helper' describe "Static pages" do describe "Home page" do it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end end describe "Help page" do it "should have the content 'Help'" do visit '/static_pages/help' expect(page).to have_content('Help') end end describe "About page" do it "should have the content 'About Us'" do visit '/static_pages/about' expect(page).to have_content('About Us') end end end
まずはテストを実行します。
$ bundle exec rspec spec/requests/static_pages_spec.rb
エラーが出るはずです。その中に以下の警告があるはずです。
No route matches [GET] "/static_pages/about"
このメッセージは、/static_pages/aboutというルートをroutesファイルに追加する必要があるということを示してます。
追加していきましょう。
config/routes.rb
SampleApp::Application.routes.draw do get "static_pages/home" get "static_pages/help" get "static_pages/about" . . . end
再びテストを実行。
先ほどのエラーは消えているはずです。
次は以下のエラーを直していきます。
The action 'about' could not be found for StaticPagesController
これはaboutアクションが無いために出ているのでStaticPagesコントローラの中にaboutアクションを追加します。
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController def home end def help end def about end end
再びテストを実行。
今度はテンプレートが見当たらないと表示されるはずです。
テンプレート、いわゆるビューを作成します。
具体的には、app/views/static_pagesディレクトリの中にabout.html.erbというファイルを作成し、以下の内容を書き込みます。
app/views/static_pages/about.html.erb
<h1>About Us</h1> <p> The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>
これでテストを実行して緑色が出れば終わりになります。
実際にブラウザからもアクセスできるかなど確かめることもできます。
以上で静的ページの作成は終わりです。
少しだけ動的なページ
次は、ページの内容を反映したタイトルを持ち、ページごとに内容が変化する、少しだけ動的なページを作成してみます。
最初にタイトルのテストを作成し、次にタイトルを追加し、最後にレイアウトファイルを使ってリファクタリングと重複の排除を行います。
まずは自動生成されているレイアウトファイルを無効にするために適当にファイル名を変更しておきます。
$ mv app/views/layouts/application.html.erb foobar # 一時的な変更
(mvはUnixのコマンドです。Windowsでファイル名を変更するには、ファイルブラウザから行うか、renameコマンドを使ってください)
テストを作成していきますが、内容はHTMLのtitleタグの内容を確認するもので、have_titleメソッドを使います。
以下のようにspecファイルを書き足していきます。
spec/requests/static_pages_spec.rb
require 'spec_helper' describe "Static pages" do describe "Home page" do it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end it "should have the title 'Home'" do visit '/static_pages/home' expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home") end end describe "Help page" do it "should have the content 'Help'" do visit '/static_pages/help' expect(page).to have_content('Help') end it "should have the title 'Help'" do visit '/static_pages/help' expect(page).to have_title("Ruby on Rails Tutorial Sample App | Help") end end describe "About page" do it "should have the content 'About Us'" do visit '/static_pages/about' expect(page).to have_content('About Us') end it "should have the title 'About Us'" do visit '/static_pages/about' expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us") end end end
テストを実行し、赤色が出ればとりあえず大丈夫です。
エラーを消していきましょう。
それと同時にWebページを正しく表示させるためにHTMLをすべて追加していきます。
まずはHomeページのビューから。
app/views/static_pages/home.html.erb
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | Home</title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
テストを実行するとエラーが1つ減っているのが分かります。同じようにHelpとAboutもパスしていきます。
app/views/static_pages/help.html.erb
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | Help</title> </head> <body> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="http://railstutorial.org/book">Rails Tutorial book</a>. </p> </body> </html>
app/views/static_pages/about.html.erb
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | About Us</title> </head> <body> <h1>About Us</h1> <p> The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p> </body> </html>
これでテストは完了です。
しかし、ここまででコードが甚だしく重複しています。
重複を取り除くテクニックの1つとして、ビューで埋め込みRubyを使用します。
それではHomeのビューのタイトルに含まれるHomeという文字を置き換えて、動作を確認します。
app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
これはERbというRubyの埋め込みの最初の例です。ファイルの拡張子が.html.erbとなっている理由はこのためです。
書き換えましたが表示される内容は以前と同じです。テストを実行してみてもエラーが出ないはずです。
続いてHelpページとAboutページも同様に、
app/views/static_pages/help.html.erb
<% provide(:title, 'Help') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <body> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="http://railstutorial.jp/book">Rails Tutorial book</a>. </p> </body> </html>
app/views/static_pages/about.html.erb
<% provide(:title, 'About Us') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <body> <h1>About Us</h1> <p> The <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p> </body> </html>
ここまで書き換えると、ビューのページは大体同じ形になっています。
Railsには、共通の構造をまとめるためのapplication.html.erbという特別なレイアウトファイルがあります。
最初に名前を変えて無効にしていたので元に戻します。
$ mv foobar app/views/layouts/application.html.erb
以下のようにレイアウトファイルを変更します。
app/views/layouts/application.html.erb
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>
ここで重要なのは「<%= yield %>」というコードです。
このコードは各ページの内容をレイアウト挿入するためのものです。簡単に説明するとHTMLの基本構造をこのレイアウトファイルに記述し、内容部分だけこの「<%= yield %>」
の部分に置き換えて各ページをつくるというものです。
3つのページを直していきます。
app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </p>
app/views/static_pages/help.html.erb
<% provide(:title, 'Help') %> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>. To get help on this sample app, see the <a href="http://railstutorial.jp/book">Rails Tutorial book</a>. </p>
app/views/static_pages/about.html.erb
<% provide(:title, 'About Us') %> <h1>About Us</h1> <p> The <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>
以上でコードの重複を大きく削減できました。テスト結果もパスできているはずです。
この章でやることはここで終わりです。
最後に差分をコミットしてマスターブランチにマージしておきましょう。
$ git add . $ git commit -m "Finish static pages"
次にmasterブランチに移動し、差分をマージ。
$ git checkout master $ git merge static-pages
中継点まで達したら、コードをリモートリポジトリにアップロードしておきましょう。
$ git push
以上で3章終了です。
チュートリアルではここから毎回演習がついているようです。
より理解を深めるために挑戦してみるのも良いと思います。
でも、分からないようならとばして、最後まで読み終えてから挑戦するのも一つの手だと思います。