前 基礎Ruby on Rails Chapter8 単数リソース
次 基礎Ruby on Rails Chapter9 ActiveRecoedの活用/コールバック
アクション・コールバックを利用したアクセス制限
アクション・コールバックとは
- コントローラ内で、いずれかのアクションが呼ばれたときに行う前処理。アクセス制限などに使う。2種類の書き方がある。
- 1つ目の書き方。ブロックを使用する。全てのアクション実行前に、コードが実行される。
class ExampleController < ApplicationController
before_action do
# アクション実行前に行う処理
end
end
- 1つ目の書き方の特定アクションバージョン。
only
で指定したもののみ。except
で除外。
class ExampleController < ApplicationController
before_action only: [:index, :show] do
# アクション実行前に行う処理
end
end
- 2つ目の書き方は、プライベートメソッドを定義して、そのメソッドを定義する方法。
only
、except
は上記と同じ
class ExampleController < ApplicationController
before_action :do_something, only: [:index, :show]
before_action :do_something, except: [:index, :search]
private def :do_something
# アクション実行前に行う処理
end
end
会員限定のコンテンツ
-
LoginRequired
、Forbidden
を一般的なエラーを表すStandardErrorから継承して作成する。 - loginRequiredメソッドは、ログインしていないとエラーが発生する。before_actionのために作成。
app/controllers/application_controller.rb(一部)
# 以下を追加
class LoginRequired < StandardError; end
class Forbidden < StandardError; end
private def loginRequired
raise LoginRequired unless current_member
end
end
- before_actionにlogin_requiredメソッドをアクション・コールバックに指定する。これによりログインしていない状態で、membersのアクションを呼ぶとエラーが発生する。
app/controllers/members_controller.rb(一部)
class MembersController < ApplicationController
before_action :login_required
- ヘッダのメニューに、
if current_member
を入れて、会員名簿と管理ページをログインしていない状態では見えなくする。
app/views/shared/_header.html.erb(一部)
<nav class="menubar">
<ul>
<%= menu_link_to "TOP", :root %>
<%= menu_link_to "ニュース", "#" %>
<%= menu_link_to "ブログ", "#" %>
<% if current_member %>
<%= menu_link_to "会員名簿", :members %>
<%= menu_link_to "管理ページ", "#" %>
<% end %>
</ul>
</nav>
- ログインしていない状態でmembersにアクセスすると、エラーが発生する。
マイアカウントページの作成
ルーティングの設定
- ルーティングに、AccountControllerにshow、edit、updateのアクションを追加する。
config/routes.rb(一部)
resource :account, only: [:show, :edit, :update]
end
アクション | パス | HTTPメソッド | パスを示すシンボル | パスを返すメソッド |
---|---|---|---|---|
show | /account | GET | :account | account_path |
edit | /account/edit | GET | :edit_account | edit_account_path |
update | /account | PATCH | :account | account_path |
-
bin/rails routes -c コントローラ名
で、routes.rbで設定されたパスとHTTPメソッドの一覧を表示できる。
$ bin/rails routes -c account
Prefix Verb URI Pattern Controller#Action
edit_account GET /account/edit(.:format) accounts#edit
account GET /account(.:format) accounts#show
PATCH /account(.:format) accounts#update
PUT /account(.:format) accounts#update
AccountsControllerの作成
アカウント情報の表示
- AccountControllerを作成する。show、editも加えて、テンプレートファイルも作成する。
$ bin/rails g controller accounts show edit
create app/controllers/accounts_controller.rb
invoke erb
create app/views/accounts
create app/views/accounts/show.html.erb
create app/views/accounts/edit.html.erb
- sessionコントローラはログインしたメンバーのみアクセスできるように、
before_action :login_required
を付ける。 - showメソッドでは、自分自身のオブジェクトをインスタンス変数@memberにセットする。
app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
before_action :login_required
def show
@member = current_member
end
(省略)
- 部分テンプレート_body.html.erbを新規作成する。ほぼ同じなので、app/views/members/show.html.erbから、tableタグの部分をコピペして作成する。
app/views/members/_body.html.erb
<table class="attr">
<tr>
<th width="150">背番号</th>
<td><%= @member.number %></td>
</tr>
<tr>
<th>ユーザー名</th>
<td><%= @member.name %></td>
</tr>
<tr>
<th>氏名</th>
<td><%= @member.full_name %></td>
</tr>
<tr>
<th>性別</th>
<td><%= @member.sex == 1 ? "男" : "女" %></td>
</tr>
<tr>
<th>誕生日</th>
<td><%= @member.birthday&.strftime("%Y年%m月%d日") %></td>
</tr>
<tr>
<th>メールアドレス</th>
<td><%= @member.email %></td>
</tr>
<tr>
<th>管理者</th>
<td><%= @member.administrator? ? "○" : "-" %></td>
</tr>
</table>
- app/views/members/show.html.erbは、上記で作成した_body.html.erbで置き換える。
app/views/members/show.html.erb(一部)
(省略)
<div class="toolbar"><%= link_to "編集", [:edit, @member] %></div>
<%= render "body" %>
- app/views/account/show.html.erbは、上記で作成した_body.html.erbを使用する。
app/views/account/show.html.erb
<% @page_title = "マイアカウント" %>
<h1><%= @page_title %></h1>
<ul class="toolbar">
<%= menu_link_to "アカウント情報の編集", :edit_account %>
</ul>
<%= render "members/body" %>
- サイトのヘッダーにある「〇〇さん」を、自分自身の情報を見るshowアクションのリンクにする。
app/views/shared/_header.html.erb(一部)
<%= menu_link_to current_member.name + "さん", :account %>
アカウント情報の編集
-
_form.html.erb
を_member_form.html.erb
と名称変更して、sharedディレクトリに移動する。
$ mv app/views/members/_form.html.erb app/views/shared/_member_form.html.erb
- new.html.erbも、
member_form
→shared/member_form
に変更する。
app/views/members/new.html.erb(一部)
<%= render "shared/member_formber_form", form: form %>
- edit.html.erbも、
member_form
→shared/member_form
に変更する。
app/views/members/edit.html.erb(一部)
<%= render "shared/member_form", form: form %>
- アカウント情報の変更画面を作成する。
app/views/accounts/edit.html.erb
<% @page_title = "アカウント情報の変更" %>
<h1><%= @page_title %></h1>
<div class="toolbar"><%= link_to "マイアカウントに戻る" %></div>
<%= form_for @member, as: "account", url: :account do |form| %>
<%= render "shared/member_form", form: form %>
<div><%= form.submit %></div>
<% end %>
- 管理者欄は、Memberコントローラの場合のみ表示させる。Accountコントローラでは表示しない。
- human_attribute_nameは、属性名をロケールテキスト(ja.yml)から取得する。
app/views/shared/_member_form.html.erb(一部)
(省略)
<% if controller.kind_of?(MembersController) %>
<tr>
<th><%= Member.human_attribute_name(:administrator) %></th>
<td>
<%= form.check_box :administrator, disabled: !current_member.administrator? %>
<%= form.label :administrator %>
</td>
</tr>
<% end %>
</table>
- editアクションと、updateアクションを実装する。
app/controllers/accounts_controller.rb(一部)
(省略)
def edit
@member = current_member
end
def update
@member = current_member
@member.assign_attributes(params[:account])
if @member.save
redirect_to :account, notice: "アカウント情報を更新しました。"
else
render "edit"
end
end
end
パスワード変更機能
独立したパスワード変更フォームを作る理由
- パスワード変更は上記で作成したアカウント情報変更フォームにパスワード変更欄を置いておくと、パスワードが空の場合はパスワードを変更しないのか、空にするのかよくわからない。
- パスワードを変更するのに、変更前と、変更後を2回入力されるなど、わかりやすいユーザーインタフェースを提供するため。
ルーティングの設定
- 自分のパスワードは、自分のアカウント情報と同様に単数リソースとして扱える。
- passwordコントローラに、
show
、edit
、update
のアクションを用意する。
config/routes.rb(一部)
(省略)
resource :password, only: [:show, :edit, :update]
end
アクション | パス | HTTPメソッド | パスを示すシンボル | パスを返すメソッド |
---|---|---|---|---|
show | /password | GET | :password | password_path |
edit | /password/edit | GET | :edit_password | edit_password_path |
update | /password | PATCH | :password | password_path |
Memberモデルの変更
- memberモデルにcurrent_passwordを読み書き可能な属性に追加する。
- 元々password属性には、空文字禁止のバリデーションが設定されているが、これは新規作成の時しか働かない。
- current_passwordに値がセットされている場合は、常に空文字でもnilでもないことを確認するようにする。
app/models/member.rb(一部)
(省略)
attr_accessor :current_password
validates :password, presence: { if: :current_password }
(省略)
パスワード変更フォームの作成
- PasswordsControllerを作成する。editも加えて、テンプレートファイルも作成する。
$ bin/rails g controller passwords edit
create app/controllers/passwords_controller.rb
invoke erb
create app/views/passwords
create app/views/passwords/edit.html.erb
- PasswordsControllerに、show、editアクションを追加する。
app/controllers/passwords_controller.rb
class PasswordsController < ApplicationController
before_action :login_required
def show
redirect_to :account
end
def edit
@member = current_member
end
end
- パスワードを変更フォームのテンプレートを追加する。
app/views/passwords/edit.html.erb
<% @page_title = "パスワードの変更" %>
<h1><%= @page_title %></h1>
<div class="toolbar"><% link_to "マイアカウントに戻る", :account %></div>
<%= form_for @member, as: "account", url: :password do |from| %>
<%= render "shared/errors", obj: @member %>
<table class="attr">
<tr>
<th><%= form.label :current_password %></th>
<td><%= form.password_field :current_password %></td>
</tr>
<tr>
<th><%= form.label :password %></th>
<td><%= form.password_field :password %></td>
</tr>
<tr>
<th><%= form.label :password_confirmation %></th>
<td><%= form.password_field :password_confirmation %></td>
</tr>
</table>
<div><%= form.submit "変更" %></div>
<% end %>
- パスワード変更フォームで使用するラベルテキストをロケールファイルに追加する。
config/locales/ja.yml(一部)
#(省略)
current_password: 現在のパスワード
password: 新しいパスワード
password_confirmation: 新しいパスワードの確認
#(省略)
- 入力フォームのテーブルの上に、
パスワードの変更
リンクを追加する。
app/views/accounts/show.html.erb(一部)
(省略)
<ul class="toolbar">
<%= menu_link_to "アカウント情報の編集", :edit_account %>
<%= menu_link_to "パスワードの変更", :edit_password %>
</ul>
<%= render "members/body" %>
- パスワード変更フォームが表示される。
新しいパスワードの保存
- パスワードを変更する、updateアクションを追加する。
- 現在のパスワードを画面から取得し、authenticateで認証する。
- 正しければ、assign_attributesで、画面の値(新パスワード)を@memberにセットする。
- 新しいパスワードが空だったり、確認と異なっていたりするとバリデーションエラーとなる。
- 保存に成功したら、マイアカウントに戻る。
app/controllers/passwords_controller.rb(一部)
(省略)
def update
@member = current_member
current_password = params[:account][:current_password]
if current_password.present?
if @member.authenticate(current_password)
@member.assign_attributes(params[:account])
if @member.save
redirect_to :account, notice: "パスワードを変更しました。"
else
render "edit"
end
else
@member.errors.add(:current_password, :wrong)
render "edit"
end
else
@member.errors.add(:current_password, :empty)
render "edit"
end
end
end
- エラーメッセージ
wrong
を追加する。「現在のパスワードが正しくありません。」に使う。
config/locales/ja.yml
errors:
messages:
invalid_member_name: は半角英数字で入力してください。
wrong: が正しくありません。
メンバー追加フォームの修正
- メンバーを追加するには初期パスワードを入力する必要があるので、新規作成の場合、emailの下にパスワード欄を表示する。
- new_record?はモデルオブジェクトがデータベースに保存されていないときにtrueを返す。
app/views/shared/_member_form.html.erb(一部)
(省略)
<% if @member.new_record? %>
<tr>
<th><%= form.label :password, "パスワード" %></th>
<td><%= form.text_field :password %></td>
</tr>
<% end %>
- 新規登録に、パスワード入力欄が表示された。