Restfulってなんだ?
RESTfulなアーキテクチャの場合、URLは同一のものを使うかわりに、HTTPリクエストメソッドにそれぞれ、GET、POST、PATCH、DELETEを使って異なるアクションに結びつけるようです。
こうすることでURLを名詞とし、HTTPリクエストメソッドを動詞とすることができ
シンプルにURLをいろいろなアクションに結びつけることができるようになります。
ところで、現在のブラウザにはPATCHやDELETEといったメソッドはないようです。
Railsは存在しないHTTPメソッドを存在するかのように見せかけ
RESTfulなアーキテクチャの実装を実現しているようです。
なんでRestfulなアーキテクチャがいいのか?
Progateのrailsアプリを作った時もそうだったけど、
現在使われているHTTPリクエストメソッドは、GETとPOSTのため
普通は、showアクションは/users_show/1、updateアクションは/users_update/1、
destroyアクションは/users_destroy/1とかのURLを考えますよね。
昔Django(pythonのwebフレームワーク)でアプリを作った時は実際にそうしてました。
でもそうするとurlを管理するファイルの中がアクションの数によっては大変なことになったのを
記憶しています。
これをresources :users
とすることで、ファイルを見やすくすることができる。
ユーザー登録機能を実装する
まずは開発環境でのみデバッグ情報を表示するようにしてみよう
<!DOCTYPE html>
<html>
.
.
.
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
</body>
</html>
<%= debug(params) if Rails.env.development? %>
ここではデバッグメソッドが使われている。paramsでデバッグ情報を受け取り、開発環境でのみそれを表示させる。putsメソッドと似ている。ついでに、後置if文を使うときは1行で済む時に使われることが多い。2行以上の時は前置if文を使おう。
ルーティングを設定しよう
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
resources :users
end
ここで、どのurlがどのアクションに対応するのか知りたい。
そんな時は、
$ rails routes
このコマンドを打つと、
ec2-user:~/environment/sample_app (sign-up) $ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / static_pages#home
help GET /help(.:format) static_pages#help
about GET /about(.:format) static_pages#about
contact GET /contact(.:format) static_pages#contact
signup GET /signup(.:format) users#new
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
というように、urlと対応するアクションを調べることができる。
PUTリクエストとPATCHリクエストの違いって?
どちらもupdateアクションを指定しているが、PUTリクエストは基本的に全ての情報を更新する時に使う。PATCHリクエストは全部、または一部の情報を更新する時に使う。そのため情報の更新にはPATCHリクエストの方が適切であると言える。
ローカル変数とインスタンス変数
user ローカル変数
ローカル変数のスコープはメソッド内。
@userはインスタンス変数
インスタンス変数は、method外、例えばviewで使うことができる。
Userリソースのshowアクションを実装
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
end
end
showアクションが実行される条件は、GET /users/:id
そのため、showアクションが実行される時は必ずidがurlに含まれている。
paramsにはハッシュで情報が保存されるので、params[:id]という書き方で情報を取得する。
params[:id]超わかりやすく解説
・User.new ~ @user.saveでインスタンスが保存される。
・@userは {id: 1}というハッシュの情報を持っている。
・で、例えば/users/1というurlにアクセスしたとする。
・このurlは/users/:idという型に当てはまるので{id: 1}という前提でshowアクションが呼び出される。
・重要なのは、urlにアクセスすると、{id: 1}という情報が送られ、それをparamsで取得することができるということ。
debuggerメソッド
def show
@user = User.find(params[:id])
debugger
end
def new
end
end
debuggerメソッドはブレイクポイントみたいなもの。
そこで処理を止めて何が起きてるかrails sをしたターミナルに表示される。
ユーザー登録機能を作ろう
form_forを使ったUser登録フォーム
<% 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>
例えば、
<%= f.label :email %>
<%= f.email_field :email %>
は、ユーザーがemail欄に書いたvalueを:emailというキーに対応させますよーって意味。
<%= f.label :password_confirmation, "Confirmation" %>
はpassword_confirmationという文字列だと長いので、"Confirmation"に上書きするよーって意味。
表示される文字がConfirmationになる。
<%= f.submit "Create my account", class: "btn btn-primary" %>
このボタンを押すと、フォームの中身がparamsに代入される。
createアクションを見てみよう
def create
@user = User.new(params[:user]) # 実装は終わっていないことに注意!
if @user.save
# 保存の成功をここで扱う。
else
render 'new'
end
end
本来User登録は、
User.new(name: ~~, email: ~~....)という感じ。
で、paramsには
{user: {name: , email:}}というハッシュが代入されている。
なので、params[:user]とすれば、:userをキーとするハッシュが代入されてインスタンスを作れるが、、、、、
このままだとクラッキングされてしまう。
{ admin: true }などのメッセージを入れられると、管理者権限を付与することになってしまう。
そこで、、
Strong Parametersを使おう
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
こうすることで、permitで指定したキー以外は扱わないよー、変なキーと値が入ってたら弾くよーってしている。
エラーメッセージを出そう。
validationに引っかかって登録に失敗した時、errors.full_messagesオブジェクトは、エラーメッセージの配列を持っています。
なので、
>> user.errors.full_messages
=> ["Email is invalid", "Password is too short (minimum is 6 characters)"]
このように、失敗した理由を出すことができる。エラー要因が複数あれば複数渡してくれる。
登録フォームのviewにエラーメッセージを表示させよう。
<% 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| %>
<%= render 'shared/error_messages' %>
<%= 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 "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
render 'shared/error_messages'これはエラーメッセージを表示するviewをパーシャル化しますよーってこと。
ディレクトリの作成
$ mkdir app/views/shared
$ touch app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
signup(失敗時)の統合テストを書いていこう。
$ rails generate integration_test users_signup
インテグレーションテストの名前の付け方は、この場合、signupという一連の動作を確認するものだから、users_signupとしている。
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
end
end
post users_pathは/usersにpostリクエストを送っていますよー。
その際に、params: { user: ~~~}を送ってますよーって意味。
上記のテストはユーザー登録が失敗することを期待している。
じゃあ、どうやってそれを判断するのか?
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_no_difference は引数(この場合、User.count)がdo end を実行する前と後では変更ないよね?っていうアサーション。
この場合、validationを設定しているのでuserインスタンスは登録されず、テストは通る。
ユーザー登録成功
まずはcreateアクションの中身を埋めよう
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これ気になる。
rails routesを見てみよう
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
/users/:id は名前付きルートでuser_pathで表すことができる。
本来は user_path(@user.id)で実現できる。
しかし、user_pathはデフォルトで:idに値を入れて渡す
そのため、user_path(@user)というように引数を渡すことでuserインスタンスの情報を渡すことができる。
これをさらに省略すると、
redirect_to @user というようにredirect_toメソッドの引数に直接Userオブジェクトを渡すことで、showアクションへリクエストできる。
補足すると、redirect_toは基本的に指定したurlにgetリクエストを送るという考えでいいと思う。
flash 成功時に一時的なメッセージを出そう!
flashは特殊な変数で、使いたい時はflashという特殊な変数が最初から用意されていると考えるとわかりやすい。実際はメソッド。
usersコントローラに実装してみよう
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
flashはキーと値を設定すると、それが次のリクエストまで残り、次の次のリクエストが来た時に消えてくれるという特徴をもつ。
flashメッセージを画面に表示するには?
flashはいろいろなところで使われるので、共通のapplicationテンプレートに表示するためのコードをかくと便利。
具体的には、
<!DOCTYPE html>
<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>
<% flash.each do |message_type, message| %>には先ほどコントローラで設定したキーと値が入っている。
この場合、キーがmessage_type 値が、messageに代入されてeachメソッドが実行される。
先ほどは キーに :successを入れた。
実はbootstrapで alert-successというclassが元から用意されているため、キーをsuccessにした。これはcssによって、緑色の文字と縁を作る。
そのため、実際に表示されるのはmessageに代入された値だけとなる。
成功時のテスト
class UsersSignupTest < ActionDispatch::IntegrationTest
.
.
.
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password" } }
end
follow_redirect!
assert_template 'users/show'
end
end
follow_redirect!は「POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド」だそうです。ちなみに、このアプリケーションではユーザー登録がうまくいった場合そのユーザーのページ(users/show.html.erb)にリダイレクトするようにしています。assert_template 'users/show'はそれをチェックしているわけですね。
つまり、 redirect_toする前のテストの結果を精査してから、redirect_to後のテストに進みたい時に使うってこと?
ch7には以下のように書いてある。
ここで、users_pathにPOSTリクエストを送信した後に、follow_redirect!というメソッドを使っていることに注目してください。このメソッドは、POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッドです。したがって、この行の直後では'users/show'テンプレートが表示されているはずです。
これが わかりやすい!!!!!!!
つまり、follow_redirect!は、assert_difference内で、users_pathへのPOSTリクエスト(URL:/users、アクション:create)を送信した結果(レスポンス)を見て、controllerで指定しているリダイレクト先のuser_url @user(ユーザ登録完了後のユーザ画面)へ移動している。
これにより、assert_template 'users/show'がテストされるのはpostリクエストがうまくいった時のみとなる。重要なのはredirect_toの前と後どちらもテストが存在するということ。
SSLを使ったデプロイ
SSLを使うと、http から httpsになる。
httpsは流れる情報が暗号化されるらしい。
herokuのサブドメインの場合は問題ないが、自分で独自ドメインを設定する際はSSL証明書を発行する必要がある。
Railsではありがたいことに、本番環境用の設定ファイルであるproduction.rbのコードをたった1行変更するだけでSSLを強制し、httpsによる安全な通信を確立できます。具体的には次のリスト 7.36に示すように、config.force_sslをtrueに設定するだけで完了です。
Rails.application.configure do
.
.
.
# Force all access to the app over SSL, use Strict-Transport-Security,
# and use secure cookies.
config.force_ssl = true
.
.
.
end
コメントアウトされてるので外してあげればOK
pumaの設定は7章見ながら設定すればOK