読者です 読者をやめる 読者になる 読者になる

ハイパーニートプログラマーへの道

頑張ったり頑張らなかったり

Rails Tutorial Chapter7をやる 断片的なメモ

英語版をやっていってるのですが、そろそろ日本語版も参考にしないと辛くなってきましたな・・・。

やったこと:

  • debugメソッド経由でデバッグ情報を表示する
  • SassのmixinsでCSSルールのグループをまとめて複数の場所で再利用することができる。
  • Railsには3つの標準的な環境がある。development,test,production
  • REST URLsの標準的なセットを通してresourceとしてユーザーと相互作用する(何を言っているのか(ry)
  • Gravatarを使えばユーザーの画像を表示するのに便利。
  • form_forヘルパーでActive Recordのオブジェクトと相互作用するフォームを作成することができる。
  • サインアップに失敗した際、ユーザーの新規作成画面をレンダリングし、エラーメッセージを出す。そのエラーメッセージはActive Recordにより自動的に決定される。
  • 一時的なメッセージを表示するにはflashが標準的なやり方。
  • サインアップが成功した際にはDBにユーザーを作成し、その詳細ページにリダイレクトする。そしてウェルカムメッセージを表示する。
  • 結合テストを行い、フォームから登録する振る舞いと回帰をキャッチする(何のことを言っているのか(ry)
  • セキュアな伝達のためのSSLと、高いパフォーマンスのためのPuma(今回はUnicorn)を使用するように、本番環境のアプリに設定する。

参考記事:

Chapter 7: Sign up | Ruby on Rails Tutorial (3rd Ed.) | Softcover.io

第7章 ユーザー登録 | Rails チュートリアル

7.1 Showing users

ユーザーのプロフィールページを作っていきます。

画像、ユーザー名、マイクロポスト、フォローしている数/されている数などを載せるのですが、今回はまず画像とユーザー名を表示させる。

7.1.1 Debug and Rails environments

プロフィールページを動的なものにしていくのですけど、デバッグ環境を整えるかと。

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %> # ここ
    </div>
  </body>
</html>

development環境でのみデバッグ

Rails consoleはデフォルトではdevelopment環境になっていると

$ rails c
Loading development environment (Rails 4.2.0)                                                                                                                      
irb(main):001:0> Rails.env                                                                                                                                         
=> "development"                                                                                                                                                   
irb(main):002:0> Rails.env.development?                                                                                                                            
=> true                                                                                                                                                            
irb(main):003:0> Rails.env.test?                                                                                                                                   
=> false

test環境でコンソールを動かしたいときは

$ rails c test
Loading test environment (Rails 4.2.0)                                                                                                                             
irb(main):001:0> Rails.env                                                                                                                                         
=> "test"                                                                                                                                                          
irb(main):002:0> Rails.env.development?                                                                                                                            
=> false                                                                                                                                                           
irb(main):003:0> Rails.env.test?                                                                                                                                   
=> true

Rails serverでもdevelopmentがデフォルトなので、本番環境でサーバー立ち上げたいときは

$ rails server --environment production

このようにするみたい(試してない)
そうなると本番環境のDBがないといけないので

$ bundle exec rake db:migrate RAILS_ENV=production

しかし今回の場合Herokuにデプロイしているのでherokuのコンソールを走らせると良い。

$ heroku run console
  >> Rails.env
  => "production"
  >> Rails.env.production?
  => true

デバッグの見た目をよくするため、スタイルシートに手を加えます。

app/assets/stylesheets/custom.css.scss

@import "bootstrap-sprockets";
@import "bootstrap";

/* mixins, variables, etc. */

$gray-medium-light: #eaeaea;

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

.debug_dump {
  clear: both;
  float: left;
  width: 100%;
  margin-top: 45px;
  @include box_sizing;
}

.debug_dumpクラスでbox_sizingをインクルードしていると。何のことを言っているのか(略)

A mixin allows a group of CSS rules to be packaged up and used for multiple elements

CSSのルールのグループがパッケージされて、複数の要素に使用することができると?
同じborder-boxでも-moz-や-webkit-などのプレフィックスがついているのもあるので、それらをひとまとめにするということかな?

そして.debug_dumpクラスで取り込むと。それにより

.debug_dump {
  .
  .
  .
  @include box_sizing;
}

これはこのように変換される。

.debug_dump {
  .
  .
  .
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}

現在のコントローラとアクションが表示されている。(分かりにくいかw)

f:id:noriyo_tcp:20150315154332p:plain

controller: static_pages
action: home

7.1.2 A Users resource

URL/user/1でユーザーのプロフィールページにアクセスできるようにしたい。/users/1でアクセスしても・・・

f:id:noriyo_tcp:20150315154504p:plain

Started GET "/users/1" for 126.14.159.72 at 2015-02-13 04:39:37 +0000                                                                                              
                                                                                                                                                                   
ActionController::RoutingError (No route matches [GET] "/users/1"):                                                                                                
  web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:22:in `middleware_call'                                                                              
  web-console (2.0.0) lib/action_dispatch/debug_exceptions.rb:13:in `call'                                                                                         
  actionpack (4.2.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'                                                                                
  railties (4.2.0) lib/rails/rack/logger.rb:38:in `call_app'                                                                                                       
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `block in call'                                                                                                  
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'                                                                               
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:26:in `tagged'                                                                                        
  activesupport (4.2.0) lib/active_support/tagged_logging.rb:68:in `tagged'                                                                                        
  railties (4.2.0) lib/rails/rack/logger.rb:20:in `call'

ルートがないよと。

routes.rbにてユーザーをリソースに加える。

  resources :users

すると今度は、アクションないよと。

f:id:noriyo_tcp:20150315154535p:plain

ならばshowアクションを作る。

class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end

  def new
  end
end

それに対応したviewをつくる。
/views/users/show.html.erb

<%= @user.name %>, <%= @user.email %>

単にユーザー名と、メールアドレスを表示するだけ。
そして/users/1にアクセス。

下に表示されるデバッグにも

controller: users
action: show
id: '1'

と出る。

f:id:noriyo_tcp:20150315154605p:plain

7.1.3 Debugger

Rails4.2ではbyebugというgemを使って、debuggerと加えれば良い、みたいな。

  def show
    @user = User.find(params[:id])
    debugger
  end

Rails serverのプロンプトにこのように出力される・・・はず。
なんどやっても504 Gateway Time-outになってしまった。

(byebug) @user.name
"Example User"
(byebug) @user.email
"example@railstutorial.org"
(byebug) params[:id]
"1"

デバッグ終わったら、Ctrl+Dで終了できる。そのあと、コントローラからdebuggerの記述を削除すれば良い。

これで何かしら変なところを調べるときにdebugger仕込めば良いと。

7.1.4 A Gravatar image and a sidebar

ユーザー画像を表示させるためにここではGravatarから画像を取得する方法にしているようだ。まあgithubに登録するときにGravatarにも登録してたので、この方法でいきます。

GravatarのurlはメールアドレスのMD5 hashをベースにしているらしいので、Rubyhexdigestメソッドを使える。

>> email = "MHARTL@example.COM".
>> Digest::MD5::hexdigest(email.downcase)
=> "1fda4469bcbec3badf5418269ffc5968"

emailをdowncaseしてあげて、それをhexdigestに渡す。

gravatar_for ヘルパーというのを作って、それを/users/showページで表示する。

app/helpers/users_helper.rbにgravatarヘルパーを定義

module UsersHelper
  # Returns the Gravatar for the given user.
  def gravatar_for(user)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end

emailからMD5 hashを生成し、それをurlの後ろにつける。
イメージタグヘルパーに、url指定。alt属性には名前、クラスにはgravatarと名付ける。

そして
app/views/users/show.html.erb

<% provide(:title, @user.name) %>
<h1>
  <%= gravatar_for @user %>
  <%= @user.name %>
</h1>

provideはどこで受け取るのだろう? ああ、ここで受け取ってるんだった。

app/views/layouts/application.html.erb

<title><%= full_title(yield(:title)) %></title>

さらにApplicationHelperで定義したfull_titleヘルパーメソッドに渡してるんだった。


(ここからはちょっとわけわからんメモ)

We’ll implement it using the aside tag, which is used for content (such as sidebars) that complements the rest of the page but can also stand alone.

今の場合サイドバーなので本文の部分からは外れている、ということなのかな?

<aside>ってなんだろ。

http://www.marguerite.jp/Nihongo/WWW/RefHTML5/aside.html#SPECIFICATIONS

html5からのようだ。本文から外れたセクション?

HTML5でつまずきやすいasideとsectionの使い方 | KAYAC DESIGNER'S BLOG - デザインやマークアップの話

つまりasideはサイドバーをコーディングする時に使用してもいいという事になります。

(おわり)


f:id:noriyo_tcp:20150315154909p:plain

f:id:noriyo_tcp:20150315154918p:plain

出た・・・出たよ。ここで登録したユーザー名と、github,Gravatarでのユーザー名は微妙に違うけど。まあいいや。

真ん中でなくてサイドバーに表示したいので、Bootstrapのcol-md-*を使う。

app/views/users/show.html.erb

 <% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
  </aside>
</div>

app/assets/stylesheets/custom.css.scss

.
.
.
/* sidebar */

aside {
  section.user_info {
    margin-top: 20px;
  }
  section {
    padding: 10px 0;
    margin-top: 20px;
    &:first-child {
      border: 0;
      padding-top: 0;
    }
    span {
      display: block;
      margin-bottom: 3px;
      line-height: 1;
    }
    h1 {
      font-size: 1.4em;
      text-align: left;
      letter-spacing: -1px;
      margin-bottom: 3px;
      margin-top: 0px;
    }
  }
}

.gravatar {
  float: left;
  margin-right: 10px;
}

.gravatar_edit {
  margin-top: 15px;
}

.gravatar_editなんてあったっけ? 9章で使うみたい。

f:id:noriyo_tcp:20150315155232p:plain

サイドにアイコンが寄っている。

7.2 Signup form

サインアップのための画面を作っていくのですけど、その前に一旦作成したユーザーを削除しようと。手っ取り早いのが、

$ bundle exec rake db:migrate:reset

これでリセットする。

$ rails c
$ > User.all                                                                                                                                          
  User Load (31.5ms)  SELECT "users".* FROM "users"                                                                                                                
=> #<ActiveRecord::Relation []>

Userが空っぽになってる。

form_for ヘルパーを使います。まずusers_controllerのnewアクション内でUser.newします。

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end
end

で新規登録画面の作成。フォームを用意する。

app/views/users/new.html.erb

<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

form_forの引数にnewアクションで生成したuserオブジェクトを渡す。

f:id:noriyo_tcp:20150315155436p:plain

!? まあスタイル書いてないからこんなもんか。

app/assets/stylesheets/custom.css.scss

.
.
.
/* forms */

input, textarea, select, .uneditable-input {
  border: 1px solid #bbb;
  width: 100%;
  margin-bottom: 15px;
  @include box_sizing;
}

f:id:noriyo_tcp:20150315155516p:plain

うん、ましになったぞ。さらに加える。

input {
  height: auto !important;
}

f:id:noriyo_tcp:20150315155538p:plain

ふむ。

(The benefit of using an email field is that some systems treat it differently from a text field; for example, the code type="email" will cause some mobile devices to display a special keyboard optimized for entering email addresses.)

なんかemail_fieldを使うのは色々捗るらしい。モバイル端末の中にはメルアドを入力するのに適した特別なキーボードを表示してくれるようだ。

7.3 Unsuccessful signups

早速フォームからユーザー作成・・・の前に、失敗した場合の処理を書かなくては。

create>保存成功/失敗時の処理

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])    # Not the final implementation!
    if @user.save
      # Handle a successful save.
    else
      render 'new'
    end
  end
end

コメントにもあるようにまだ最終実行ではないよと。ただ与えた:userパラメーターでnewして、それが保存されてるか/されていないかの分岐を書く。
失敗していたらrender 'new'で画面はそのまま。

ここでわざと間違った形式で情報を入力してみるかと。メルアドの形式なんかが間違っとる。

name: "Foo Bar"
email: "foo@invalid" password: "foo" password_confirmation: "bar"

ForbiddenAttributesErrorがでる。

f:id:noriyo_tcp:20150315155615p:plain

Rails serverのコンソール上

Parameters: {"utf8"=>"", "authenticity_token"=>"pxdaY4hpeUJ5Yhs7eZLIe9ehx2XWRzzJPT0EmKc2xJWUkRM6CJK1tlm0WqDQKAo6BfjRHrb9k+1u5Tq6yWXxcw==", "user"=>{"name"=>"Foo
 Bar", "email"=>"foo@invalid", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create my account"}                                    
Completed 500 Internal Server Error in 1358ms                                                                                                                      
                                                                                                                                                                   
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):                                                                                     
  app/controllers/users_controller.rb:11:in `create' 

http://railstutorial.jp/book/ruby-on-rails-tutorial?version=4.0#sec-signup_failure

日本語訳のサイトから引用しますと

ruby @user = User.new(params[:user])

以下とほぼ等価であるということです。

ruby @user = User.new(name: "Foo Bar", email: "foo@invalid", password: "foo", password_confirmation: "bar")

7.3.2 Strong parameters

以前のバージョンのRailsでは、モデル層でattr_accessibleメソッドを使用することで上のような危険を防止していましたが、 Rails 4.0ではコントローラ層でStrong Parametersというテクニックを使用することが推奨されています。Strong Parametersを使用することで、必須のパラメータと許可されたパラメータを指定することができます。

params.require(:user).permit(:name, :email, :password, :password_confirmation)
  • paramsハッシュでは:user属性を必須とする。
  • そして:name,:email,:password,:password_confirmationなどの属性を許可している。

app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

コントローラのuser_paramsメソッド(private)にストロングパラメーターを設定。
それをUse.new(user_params)としてuserオブジェクトに渡す。

/signupにアクセス、また間違った情報を入力。
今度はエラーは出ず、render 'new'によって新規作成画面のまま、となる。
しかしエラーメッセージを出さないとですね。

7.3.3 Signup error messages

error messagesをパーシャルで表示する。

ここでは、’shared/error_messages’というパーシャルをrender (レンダリング) している点に注目してください。これはRails全般の慣習で、パーシャルは複数のコントローラにわたるビューに対し、専用のshared/ディレクトリを使用するようにしています

そうなんや。app/views/shared/_error_messagesを作成しないといけない。

form-controlクラスはBootstrapのクラス

app/views/shared/_error_messages.thml.erb

<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-error">
      The form contains <%= pluralize(@user.errors.count, "error" %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li>* <%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

pluralizeってなんだろな。

pluralizeの最初の引数に整数が与えられると、それに基づいて2番目の引数の英単語を複数形に変更したものを返します。このメソッドの背後には強力なインフレクター (活用形生成) があり、不規則活用を含むさまざまな単語を複数形にすることができます。

app/assets/stylesheets/custom.css.scss

.
.
.
/* forms */
.
.
.
#error_explanation {
  color: red;
  ul {
    color: red;
    margin: 0 0 30px 0;
  }
}

.field_with_errors {
  @extend .has-error;
  .form-control {
    color: $state-danger-text;
  }
}

さらにRailsは、エラーページにある、divで囲まれたエラーCSSクラスfield_with_errorsを適用しています。

ん、これどこにあるん?

In addition, after an invalid submission Rails automatically wraps the fields with errors in divs with the CSS class field_with_errors. These labels then allow us to style the error messages with the SCSS shown in Listing 7.20, which makes use of Sass’s @extend function to include the functionality of the Bootstrap class has-error.

Railsでは自動的にdiv内のエラーフィールドをfield_with_errorsクラスでラップする? よしなにやってくれるということかな。
field_with_errorsではBootstrapの.has-errorクラスの機能をインクルードしている。

こんなんでました。

f:id:noriyo_tcp:20150315155954p:plain

7.3.4 A test for invalid submission

assert_no_difference 'User.count' do
  post users_path, user: { name:  "",
                           email: "user@invalid",
                           password:              "foo",
                           password_confirmation: "bar" }
end

errorとなった場合、ユーザーは登録されないので、ユーザー数は変わらないはず。

before_count = User.count
post users_path, ...
after_count  = User.count
assert_equal before_count, after_count

と同じようなことをassert_no_difference 'User.count' doでできるということか。

test/integration/users_signup_test.rb

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, user: { name:  "",
                               email: "user@invalid",
                               password:              "foo",
                               password_confirmation: "bar" }
    end
    assert_template 'users/new'
  end
end

We’ve also included a call to assert_template to check that a failed submission re-renders the new action.

登録失敗したら、そのままnewページをレンダリングすることも確かめる。 assert_template 'users/new'

7.4 Successful signups

今度はサインアップに成功した場合ですが、そのためのテンプレートをまだ作っていません。

class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

redirect_to @user saveした後にユーザーのプロフィールページにリダイレクトする。そのままオブジェクト渡せばいいのか。

      flash[:success] = "Welcome to the sample app!"
      redirect_to @user

redirectする前にflashメッセージを定義。:successというキーを指定。
そしてそれをレイアウトに仕込んで、画面に表示する。


irb(main):001:0> flash = { :success => "Successed!", :danger => "Failed!" }                                                                                        
=> {:success=>"Successed!", :danger=>"Failed!"}                                                                                                                    
irb(main):002:0> flash.each do |key, value|                                                                                                                        
irb(main):003:1* puts "#{key}"                                                                                                                                     
irb(main):004:1> puts "#{value}"                                                                                                                                   
irb(main):005:1> end                                                                                                                                               
success                                                                                                                                                            
Successed!                                                                                                                                                         
danger                                                                                                                                                             
Failed!

Rails console上で試してみる。:success,:dangerというキーを使う。


flashメッセージを画面に出す。

app/views/layouts/application.html.erb

<html>
.
.
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
  </body>
</html>

body>containerの頭にメッセージを仕込む。
flashのキー(:success or :danger)をmessage_typeに渡し、それをalert-<%= message_type>という形で埋め込む。つまり

alert-success
alert-danger

になる。これらはBootstrapのクラス。

7.4.3 The first signup

Rails Tutorialの名前とロゴが使えるそうなので、ユーザー登録に使う。パスワードは適宜。

Name: Rails Tutorial
Email: example@railstutorial.org

成功すると/users/1にリダイレクトして、このように表示される。

f:id:noriyo_tcp:20150315160403p:plain

コンソールで確認。

> User.find_by(email: "example@railstutorial.org")                                                                                                  
  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT 1  [["email", "example@railstutorial.org"]]                                    
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2015-02-14 05:33:02", updated_at: "2015-02-14 05:33:02", password_digest:
 "$2a$10$/x5W4mCkcQraL/Iqmc8vhu.XbuYYeFFzyHWAchK5wSM...">

routeを確認すると、users_path post users#createというふうに紐付いている。

f:id:noriyo_tcp:20150315160437p:plain

というわけでテストでもpostで投げるよと。

7.4.4 A test for valid submission

test/integration/users_signup_test.rb

class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    assert_difference 'User.count', 1 do
      post_via_redirect users_path, user: { name: "Example User",
                              email: "user@example.com",
                              password: "password",
                              password_confirmation: "password"
      }
    end
    assert_template 'users/show'
  end
end

assert_difference
post_via_redirect
assert_template 'users/show'

が先ほど(失敗した場合のテスト)と違う点。

  • ユーザー登録がされれば、ユーザーが1個増えて「(数に)違いがでる」
  • 成功後は「リダイレクトされる」
  • リダイレクト先は「ユーザーの詳細(show)ページ」

ということかと。

7.5 Professional-grade deployment

なんだかんだで調べながらで時間かかりましたが、とうとう行きます。

$ git add -A
$ git commit -m "Finish user signup"
$ git checkout master
$ git merge sign-up

チュートリアルでは最後にまとめてコミットしてるようですが、実際にはこまめにコミットしていくことになるかと。とにかくマスターにマージですね。

SSL導入

デプロイされたアプリケーションが期待どおりに動作するために、SSLが本番環境で動作するための行を1つ追加します。そのためには、config/environments/production.rbファイルを リスト7.29のように変更します。

config/environments/production.rb

SampleApp::Application.configure do
  .
  .
  .
  # Force all access to the app over SSL, use Strict-Transport-Security,
  # and use secure cookies.
  config.force_ssl = true
  .
  .
  .
end

コメントアウトされている部分を外すだけです。

リモートサーバーにSSLを設定する必要があるのだけれど、Herokuなら大丈夫だぜ! みたいなことが。

and luckily we won’t need it here: for an application running on a Heroku domain (such as the sample application), we can piggyback on Heroku’s SSL certificate. As a result, when we deploy the application in Section 7.5.2, SSL will automatically be enabled.

この後Unicorn設定しようず! とありますが、日本語版はここでHerokuにプッシュしちゃいますね。先にやろうかな。

$ git add -A
$ git commit -am "Use SSL"
$ git push
$ git push heroku
$ heroku run rake db:migrate

Bitbucketにプッシュしたあと、herokuにデプロイ。忘れずにherokuでマイグレーションを行う。


remote: ###### WARNING:                                                                                                                                          
remote:        No Procfile detected, using the default web server (webrick)                                                                                      
remote:        https://devcenter.heroku.com/articles/ruby-default-web-server

herokuにプッシュしている間、よく見るとワーニングが出ている。
webrickじゃなくてunicornにしたほうがいいんですかねー?


デプロイしたページを確認してみる。Sign up now!ボタンを押すとユーザー登録ページに遷移する。
ユーザーの詳細ページはまだ登録後にしか出ないようになってる。

自分を登録してみた。Rails Tutorialも。

f:id:noriyo_tcp:20150315161820p:plain

f:id:noriyo_tcp:20150315161836p:plain


7.5.2 Unicorn in production

Ruby Default Web Server | Heroku Dev Center

Why not WEBrick

By default WEBrick is single threaded, single process. This means that if two requests come in at the same time, the second must wait for the first to finish.

WEBrickはあまり本番環境には適してないらしいです。

Rails TutorialではUnicornだが、HerokuではPuma推しだそうです。

Deploying Rails Applications with Unicorn | Heroku Dev Center

Heroku recommends using the Puma web server instead of Unicorn. If you are using Unicorn, your application is not protected against a slow client attack.

!? んー、でもとりあえずUnicornにするか・・・。

Gemfileに追加(のちに最初からコメントアウトされているのに気づきましたが、それは置いといて・・・)

group :production do
  gem 'pg'
  gem 'rails_12factor'
  gem 'unicorn'
end
$ bundle install
.
.
Installing unicorn 4.8.3 
.
.

config/unicorn.rbというファイルを作成

$ touch config/unicorn.rb

Deploying Rails Applications with Unicorn | Heroku Dev Center

上記ページにある設定をそのままコピペ。

# config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

アプリのルートディレクトリにてProcfileというのを作ります。

$ touch ./Procfile 

これをheroku公式からコピペ。

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

一応テスト走らせたのち、

$ git add -A
$ git commit -am "Use Unicorn in production"
$ git push
$ git push heroku

マイグレーションはもうしてあるから良いと。


content_tagヘルパーを使う

app/views/layouts/application.html.erbにてflashを実装した部分

      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>

これはあまり美しくないのでcontent_tagヘルパーを使用する。

<% flash.each do |message_type, message| %>
        <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
      <% end %>

比較

Before:

<div class="alert alert-<%= message_type %>"><%= message %></div>

After:

<%= content_tag(:div, message, class: "alert alert-#{message_type}") %>

クラスの値でmessage_typeを式展開させる。


Sublime Text2を使用しているのですけど

SublimeText(Windows、Mac)をSassで使いやすい設定にする | Web制作者のためのSassの教科書 - 公式サポートサイト

+Tabでスニペットってのが効かないなあ。tabをスペースに設定しているのが悪いのかなあ?