Ruby on Rails チュートリアルって長いのでサマリー(3章)〜RSpecとレイアウトテンプレート〜


Ruby on Rails チュートリアル
http://railstutorial.jp/

# 前提
* Mac OS X Yosemite
* Homebrewは入ってる
* gemは入ってる
* gitとかHerokuとかruby以外でも使うものは知ってる

# 準備

## アプリ作成

RSpecを使うため、--skip-test-unitオプションでTest::Unitのtestディレクトリを作成しない。

```
cd rails_projects
rails new sample_app --skip-test-unit
cd sample_app
```

## Gemfile更新とbundle

RSpec、Capybaraのために必要なものを入れる。

```rb:Gemfile
# 追加（HerokuデプロイでWARNINGがでるから）
ruby '2.2.0'

# gem 'sqlite3'
group :development, :test do
  gem 'sqlite3'
  gem 'rspec-rails'
end

# 追加
group :production do
  gem 'pg'
  gem 'rails_12factor'
end

# 追加
group :test do
  gem 'selenium-webdriver'
  gem 'capybara'
end

# gem 'sdoc', '~> 0.4.0', group: :doc
group :doc do
  gem 'sdoc', require: false
end
```

```
bundle install --without production
bundle update
bundle install
```

## セッション変数暗号化の秘密トークン(secret token)更新

以下を追加する。

```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
```

.secretを公開してしまわないように.gitignoreを設定する。

```.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
```

## RSpecを使うように設定

JavaScriptランタイムがインストールされていないというエラーが表示された場合は、Gemfileの`gem 'therubyracer', platforms: :ruby`部分のコメントアウトをはずして`bundle install`するといいらしい。
チュートリアルにはNode.jsをインストールするのがおすすめと書いてある。

```
rails generate rspec:install
```

## commit

```
git init
git add .
git commit -m "Initial commit"
```

## 静的ページ作成

StaticPagesコントローラーをつくる。
アクションはhomeとhelpをつくる。

```
rails generate controller StaticPages home help --no-test-framework
```

以下のファイルを確認。
MVCが命名規約で紐付けられていることに注意。

* config/routes.rb
* app/controllers/static_pages_controller.rb
* app/views/static_pages/home.html.erb
* app/views/static_pages/help.html.erb


## （Tips）railsコマンドで作成したものを元に戻す

rails generate xxx
=>
rails destroy xxx

rake db:migrate
=>
rake db:rollback
or
rake db:migrate VERSION=0
※バージョンを指定して戻す

## 確認

```
rails s
```

```
http://localhost:3000/static_pages/home
http://localhost:3000/static_pages/help
```

## コミット

```
git add .
git commit -m "Add a StaticPages controller"
```

# TDD

**gitの使い方的なこととか、TDDの説明的なこととかは省略。**

## 結合テストの作成

```
rails generate integration_test static_pages
```

## RED（失敗するテスト）#1 home

テストコードの修正。
この段階では、細かいところは気にしないで、何となく何をしてるかわかればいい。
**仕様が変わったらしいので、チュートリアルからコードを変更。**
参考：https://www.relishapp.com/rspec/rspec-rails/v/3-1/docs/feature-specs/feature-spec

```spec/requests/static_pages_spec.rb
require "rails_helper"

feature "Home page" do
  scenario "should have the content 'Sample App'" do
    visit '/static_pages/home'
    expect(page).to have_content('Sample App')
  end
end
```

Capybara DSLをRailsヘルパーファイルに追加する。
**チュートリアルではRSpecヘルパーに書くことになっているが、仕様が変わったらしい。**

```spec/rails_helper.rb
RSpec.configure do |config|
  # 追加
  config.include Capybara::DSL
end
```

**Capybaraの仕様が変わったらしいのでフォルダ名を変更**
http://programming.ironsand.net/2014/how-to-solve-capypara-undefined-method-visit/

```
mv spec/requests/ spec/features
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。

## GREEN（テストをとおす）#1 home

コンテンツに`Sample App`を追加してテストをとおす。

```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>
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストが成功する。


## RED（失敗するテスト）#2 help

以下を追加

```spec/requests/static_pages_spec.rb
feature "Help page" do
  scenario "should have the content 'Help'" do
    visit '/static_pages/help'
      expect(page).to have_content('Help')
  end
end
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。

## GREEN（テストをとおす）#2 help

```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>
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストが成功する。

## RED（失敗するテスト）#3 about

以下を追加。

```spec/requests/static_pages_spec.rb
feature "About page" do
  scenario "should have the content 'About Us'" do
    visit '/static_pages/about'
    expect(page).to have_content('About Us')
  end
end
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。
`No route matches [GET] "/static_pages/about"`と怒られるので、routesを追加。

```config/routes.rb
get "static_pages/about"
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。
`The action 'about' could not be found for StaticPagesController`と怒られるので、controllerにaboutアクションを追加。

```app/controllers/static_pages_controller.rb
def about
end
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。
`Missing template static_pages/about`と怒られる。

## GREEN（テストをとおす）#3 about

viewにaboutビューを追加。

```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>
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストが成功する。

# レイアウトテンプレートを使ったリファクタリング

## RED

以下のようにタイトルのテストを追加（HelpとAboutは省略）。

```spec/requests/static_pages_spec.rb
feature "Home page" do
  scenario "should have the content 'Sample App'" do
    visit '/static_pages/home'
    expect(page).to have_content('Sample App')
  end

  scenario "should have the title 'Home'" do
    visit '/static_pages/home'
    expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home")
  end
end
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストエラーになる。

## GREEN

レイアウトテンプレートを修正。

* `<%= yield %>`部分に各ビューの内容が挿入される
* `<%= yield(:title) %>`部分は:titleというラベルを表示している。:titleラベルは、`provide(:title, 'foo')`のように各ビューからも設定できる。

```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>
```

Viewに:titleラベルの設定を追加する（help, aboutは省略）。

```app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %>
```

RSpecの実行

```
bundle exec rspec spec/features/
```

テストが成功する。


## commit

```
git add .
git commit -m "Finish static pages"
```

# RSpecの高度なセットアップ

Guard, Sporkは試してないので省略。

## Sublime TextでRSpecを実行

https://github.com/maltize/sublime-text-2-ruby-tests

Preferences => Package Control => install
=> RubyTest

`Command-Shift-R`でテストを実行。

私の場合は、うまく動かなくて、次の対応をしました。

1.rspecのパス設定

以下のコマンドでRSpecがみつからない。

```
which rspec
```

rbenvで環境構築した場合は以下のコマンドが必要らしい。

http://ylgbk.hatenablog.com/entry/2014/03/16/112310

```
rbenv rehash
rspec -v
which rspec
```

2.SublimeにRSpecのパスを設定する。

`Command-Shift-R`でテストを実行すると、`$HOME/.rbenv/bin/rspec`を探そうとしている。
パスを設定すればよいらしい。

http://quattro4.hatenablog.com/entry/2013/01/06/220929

Preferences => Package Settings => RubyTest => Settings - User

```json
{
  "run_rspec_command": "$HOME/.rbenv/shims/rspec {relative_path}",
  "run_single_rspec_command": "$HOME/.rbenv/shims/rspec {relative_path}"
}
```