連載
» 2014年07月30日 18時00分 公開

開発現場でちゃんと使えるRails 4入門(7):Rails開発を面白くするアクションコントローラーの5大機能とルーティングの基本 (2/3)

[著:林慶、監修:山根剛司,株式会社アジャイルウェア]

Rails 4から導入された「Strong Parameter」

 Rails 4から導入されたStrong Parameterはリクエストに含まれていてもよいパラメーターをコントローラーで指定する機能です。悪意のあるユーザーがフォームに存在していない属性の入力欄を追加してPOSTリクエストしてきても、その属性を弾くことができます。

 Strong Parameterの挙動を理解するために前節のPOSTデータパラメーターを使って実験してみましょう。

 まず「new」メソッドに「params」が持つパラメーターをHashオブジェクトのように渡すRails 4より前のやり方を試してみます。

params[:book] => {
      "title" => "新しい本",
      "author" => "新しい作者"
}
 
Book.new(params[:book])
=> ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError

 このように「params」をHashオブジェクトと同じようにモデルの複数属性の初期化に使うとエラーが発生します。

 それでは、「params」とは何でしょうか。

params[:book].class
=> ActionController::Parameters < ActiveSupport::HashWithIndifferentAccess
params[:book].class.superclass
=> ActiveSupport::HashWithIndifferentAccess < Hash

 「params」はHashクラスを継承していますが、Hashではありません。これを用いて複数属性の初期化に用いるには次のようにメソッドチェインの返り値を使います。

Book.new(params.require(:book).permit(:title))
=> #<Book:0x007fc8d8781088> 
=> Unpermitted parameters: author

 「permit」メソッドの引数には初期化する複数の属性名を渡せます。そこに含まれていない属性がparamsに含まれていると上のようなログが出力されます。

Book.new(params.require(:book).permit(:title, :author))
=> #<Book:0x007fc8d8781088>

 上記のように、作成・更新に使いたいパラメーターだけをpermitし、paramsからBookクラスのインスタンスを生成します。

【2】リクエスト間でデータを保存する「クッキーとセッション」

 クッキーとセッションはリクエストの間でデータを保存する方法で、ログイン状態の管理などに使われます。

クッキー

 クッキーはユーザー側で一時的に保存されるデータです。セッションを使う上でもセッションIDをユーザー側で保存するために使われます。クッキーを保存するには次のように操作します。

# クッキーの値だけを保存(この値はブラウザーを閉じると消去されます)
cookies[:condition] = ‘good’
 
# オプションを付けて保存
cookies[:condition] = {
  value: ‘soso’, # クッキーの値
  expires: 1.day.from_now # クッキーの有効期限
}

 またクッキーを読み出すときはハッシュと同じようにします。

cookies[:condition] # => ‘soso’
 
# 設定されていなければ
cookies[:hoge] # => nil

セッション

 セッション、データをサーバーで保持し、そのデータを取り出すセッションIDをクッキーとしてユーザー側で保存します。セッションは次のように保存します。

session[:user_id] = @user.id

 取り出すときはクッキーと同様です。

session[:user_id] # => 1

【3】簡単なメッセージを表示する「フラッシュ」

 フラッシュはデータの保存成功などの簡単なメッセージを表示する仕組みです。フラッシュはセッションに保存され、リダイレクトした先でも設定したメッセージを表示できます。

def index
    flash[:notice] = “現在時刻:#{Time.now}”
end

 表示するにはビューで次のようにして設定したキーで呼び出します。

<div id=”flash”>
  <%= flash[:notice] %>
</div>

 フラッシュはアクションが終了する際に削除されるため、以降のリクエストでは表示されません。

【4】アクションを実行する前後に処理を挟む「フィルター」

 「ActionController」には、アクションを実行する前後に前処理や後処理を挟むフィルターが用意されています。アクションの前に実行される「before_action」フィルター、後に実行される「after_action」フィルター、アクションの前後にまたがって実行される「around_filter」フィルターがあります。

 「scaffold」で作成したコントローラーでは次のようなフィルターが実装されています。

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]
 
(略)
 
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_book
      @book = Book.find(params[:id])
    end

 このように「before_action」の宣言にフィルターの処理を行うメソッド名を渡して使用できます。またメソッド名ではなくフィルター処理のブロックや、フィルターオブジェクトを渡すこともできます。

 フィルターオブジェクトは「before_action」に渡す場合であれば「before」メソッドか「filter」メソッド、「after_action」に渡す場合であれば「after」メソッドか「filter」メソッドにフィルターの処理を実装しておく必要があります。

 「around_filter」に渡す場合は「filter」メソッドまたは「around」メソッドにアクションの処理を埋め込む「yield」を含めて実装します。

class TransactionWrap
  def filter(controller, &action)
    ActiveRecord::Base.transaction do
      yield
      raise ActiveRecord::Rollback if @book.errors.present?
    end
  end
end
 
class BooksController < ApplicationController
  around_action TransactionWrap.new, only: [:create, :update]
 
(以下略)

 また、フィルターの宣言時に「only」オプションでフィルターを実行するアクションを指定でき、「except」オプションで全てのアクションからフィルターを実行しないアクションを指定することもできます。

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。