RubyMine で Rails Tutorial 10章

10章

まず、partial に切り出すところはこんな感じになるでしょうか。

new.html.erb にて、form_for 周りを選択します。

右クリックから Refactor → Extract → Partial で切り出します。

名前をつけます。チュートリアルに従って _form.html.erb とします。

これで切り出せました。

あとは少々調整します。new.html.erb と edit.html.erb の差分を見て、共通化できる部品を活かして、違うところは provide で渡します。
このスクリーンキャプチャでは form_for の一つ上も切り出してしまったのですが、gravatar_for があることに気が付いて調整しています。

new.html.erb

<% provide(:title, 'Sign up') %>
<% provide(:url, signup_path) %>
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 offset-md-3">
    <%= render 'form' %>
  </div>
</div>

edit.html.erb

<% provide(:title, 'Edit user') %>
<% provide(:url, user_path) %>
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>

<div class="row">
  <div class="col-md-6 offset-md-3">
    <%= render 'form' %>

    <div class="gravatar_edit">
      <%= gravatar_for @user %>
      <a href="http://gravatar.com/emails" target="_blank">Change</a>
    </div>
  </div>
</div>

_form.html.erb

<%= form_for(@user, url: yield(:url)) do |f| %>
  <%= render 'shared/error_messages', object: @user %>

  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>

  <%= f.label :email %>
  <%= f.email_field :email, class: 'form-control' %>

  <%= f.label :password %>
  <%= f.password_field :password, class: 'form-control' %>

  <%= f.label :password_confirmation, "Confirmation" %>
  <%= f.password_field :password_confirmation, class: 'form-control' %>

  <%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>

10.1.3 の演習では、テストを追加します。
わたしはブラウザに Chrome を使っているのですが、実際にエラー画面を表示させて右クリックから「検証」をするとこのようになります。

div.alert を指定すればいいということがわかるので、assert_test に追加します。

require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "unsuccessful edit" do
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { name:  "",
                                              email: "foo@invalid",
                                              password:              "foo",
                                              password_confirmation: "bar" } }

    assert_template 'users/edit'
    assert_select "div.alert", "The form contains 4 errors."
  end
end

10.2.3 の演習でもテストを追加するのがありました。

/users/1/edit にアクセスする → ログインが求められる → ログインしたら /users/1/edit に戻って編集できるようになる、という確認で、ログイン前後での session[:forwarding_url] の値をチェックしておきましょうというテストです。
こんな感じにしてみました。

  test "successful edit with friendly forwarding" do
    get edit_user_path(@user)
    assert_equal edit_user_url(@user), session[:forwarding_url]
    log_in_as(@user)
    assert_redirected_to edit_user_url(@user)
    assert_nil session[:forwarding_url]
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end

リスト 10.39 の CSS では $gray-lighter を使っていますが、Bootstrap 4 にはありません。上の方で定義しておきます。

@import "bootstrap";

/* mixins, variables, etc. */

$gray: #555;
$gray-light: #777;
$gray-medium-light: #eaeaea;
$gray-darker: #222;
$state-danger-text: #f00;
$gray-lighter: #eee;

@mixin box_sizing {
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}
...

10.3.1 のテストは、右上のメニューのところがログインしていないときとログインしたときで変わるというのを追加してみました。

ログインしていないとき

ログインしたとき

site_layout_test.rb

require 'test_helper'

class SiteLayoutTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "layout links without login" do
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", users_path, count: 0
    assert_select "a[href=?]", logout_path, count: 0

    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path

    get contact_path
    assert_select "title", full_title("Contact")
  end

  test "layout links with login" do
    log_in_as(@user)
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", users_path
    assert_select "a[href=?]", user_path(@user)
    assert_select "a[href=?]", edit_user_path(@user)
    assert_select "a[href=?]", logout_path

    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
  end
end

10.3.3 では will_paginate を使います。bootstrap-will_paginate では Bootstrap 4 に対応していないようなので、will_paginate-bootstrap4 を使ってみました。

10章で Gemfile に追加した部分

# section 10
gem 'faker'
gem 'will_paginate'
#gem 'bootstrap-will_paginate'
gem 'will_paginate-bootstrap4'

リスト 10.48 では div.pagination の要素があることを確認していますが、will_paginate-bootstrap4 では div ではなく ul になっていました。

require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "index including pagination" do
    log_in_as(@user)
    get users_path
    assert_template 'users/index'
    assert_select 'ul.pagination'
    User.paginate(page: 1).each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
    end
  end
end

リスト 10.56 のテストはこう書きました。admin 属性を変更しようとしてもできません、というものです。

  test "should not allow the admin attribute to be edited via the web" do
    log_in_as(@other_user)
    assert_not @other_user.admin?
    patch user_path(@other_user), params: {
        user: { password:              "password",
                password_confirmation: "password",
                admin: true } }
    assert_not @other_user.admin?
  end

10章は長い道のりでした。テストを書きながら実際に動かして進めていくと楽しいですね。

RubyMine で Rails Tutorial 9章

9章

特に RubyMine の使い方としてどうこうということもなくなってきました。
淡々と進めていきます。内容は結構難しいと思います。

リスト 9.27

class SessionsController < ApplicationController
  def new
  end

  def create
    @user = User.find_by(email: params[:session][:email].downcase)
    if @user && @user.authenticate(params[:session][:password])
      log_in @user
      params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
      redirect_to @user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out if logged_in?
    redirect_to root_url
  end
end

リスト 9.28

  test "login with remembering" do
    log_in_as(@user, remember_me: '1')
    assert_equal cookies['remember_token'], assigns(:user).remember_token
  end

全般的に難しいセクションだったと思います。

if (user_id = session[:user_id]) … というコードはわたしはあまり好きではないです。バグを産みやすいです。

RubyMine で Rails Tutorial 8章

8章 はログインについて。

もうだんだん RubyMine ではというものはなくなってきました。
淡々とチュートリアルをこなしていきます。

リスト 8.19 の _header.html.erb では、Bootstrap 4 だとドロップダウンのリンクがうまく出ません。
このように直してみました。

<header class="navbar navbar-fixed-top navbar-dark bg-dark">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav justify-content-end">
        <li class="nav-item"><%= link_to "Home", root_path, :class => "nav-link" %></li>
        <li class="nav-item"><%= link_to "Help", help_path, :class => "nav-link" %></li>
        <% if logged_in? %>
          <li class="nav-item"><%= link_to "Users", '#', :class => "nav-link" %></li>
          <li class="nav-item dropdown">
            <a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">
              Account <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
              <li class="dropdown-item"><%= link_to "Profile", current_user %></li>
              <li class="dropdown-item"><%= link_to "Settings", '#' %></li>
              <li class="dropdown-divider"></li>
              <li class="dropdown-item">
                <%= link_to "Log out", logout_path, method: :delete %>
              </li>
            </ul>
          </li>
        <% else %>
          <li class="nav-item"><%= link_to "Log in", login_path, :class => "nav-link" %></li>
        <% end %>
      </ul>
    </nav>
  </div>
</header>

リスト 8.20 の app/assets/javascripts/application.js については、特に記述不要です。

こんな感じで表示されるようになりました。