こんにちは。まっつんです。今回は 第 2 回 で実装しなかったバリデーションを実装します。

Ruby on Rails (以下、Rails) ではモデルにバリデーションを実装します。簡単なバリデーションであれば、定義済みのバリデーションヘルパーを使って容易に実装することができます。

容易に実装できるにも関わらず、第 2 回 では実装を見送りました。これには 2 つの理由があります。

MATSUFUJI Hideharu

最初の理由はバリデーションを実行するタイミングにあります。デフォルトではモデルがデータベースに保存される時にバリデーションが実行されます。よって、ポテトバーガー注文アプリケーションのメインメニューが選択された時、そしてサイドメニューが選択された時にそれぞれバリデーションを実行するには独自の実装が必要となります。

次により重要な理由として、普段から特に意識することなく行っているバリデーションという処理を考えるということがあります。

そこで、まずバリデーションを実装し、そのあとでバリデーションについて考えてみたいと思います。

バリデーションの実装

バリデーションの実装は以下のようになります。バリデーションに関するメソッドはすべてモデルに実装し、コントローラから呼び出すようにします。

  • プロパティごとのバリデータメソッドを用意する
  • コントローラから呼び出すことができるバリデーションメソッドをモデルに用意する
  • 実行のタイミングに関わらず、プロパティ毎のバリデータメソッドを呼び出す
app/models/order.rb
class Order < ActiveRecord::Base
  belongs_to :customer

  validate :validator_of_main, :validator_of_side

  def invalid_for_main_menu?
    errors.clear
    validator_of_main
    return errors.count > 0
  end

  def invalid_for_side_menu?
    errors.clear
    validator_of_side
    return errors.count > 0
  end

  def validator_of_main
    errors.add(:main, '正しい注文内容を選択してください') if
      main.blank? or ![1, 2, 3].include?(main)
  end

  def validator_of_side
    errors.add(:side, '正しいサイドメニューを選択してください') if
      side.blank? or ![1, 2, 3].include?(side)
  end
end

Rails のドキュメント「Active Record Validations and Callbacks」の「6 Creating Custom Validation Methods」を参考に、メインメニュー (main フィールド), サイドメニュー (side フィールド) のバリデータメソッドとしてそれぞれ validator_of_main, validator_of_side を実装しています。また、これらのメソッドが validate メソッドの引数になっていることに注意してください。これによってデフォルトのバリデーションの際にも validator_of_main, validator_of_side が呼び出されるようになります。

invalid_for_main_menu?, invalid_for_side_menu? はコントローラから呼び出されるメソッドです。エラーをクリアして、バリデータメソッドを呼び出し、結果を返しています。

バリデーションの実行

続いて、実装したバリデーションをコントローラから呼び出すようにします。

app/controllers/orders_controller.rb
class OrdersController < ApplicationController
  ...
  def side_menu
    @order = Order.new
    @order.main = params[:main]

    render :action => 'main_menu', :status => 400 and return if @order.invalid_for_main_menu?
  end

  def confirmation
    @order = Order.new
    @order.main = params[:main]
    @order.side = params[:side]

    render :action => 'main_menu', :status => 400 and return if @order.invalid_for_main_menu?
    render :action => 'side_menu', :status => 400 and return if @order.invalid_for_side_menu?

    main_index = @order.main - 1
    side_index = @order.side - 1

    @main = ['ジャーマンポテトバーガー', 'ポテトコロッケバーガー', '肉じゃがバーガー'][main_index]
    @side = ['フライドポテト', 'ポテトサラダ', 'スイートポテト'][side_index]
    @price = [650, 600, 700][main_index]
  end

  def create
    @order = Order.new(params[:order])

    if @order.save
      render :action => 'finish'
    else
      render :action => 'main_menu', :status => 400
    end
  end
  ...
end

side_menu メソッドではメインメニューのバリデーション、confirmation メソッドではメインメニューとサイドメニューのバリデーションを行っています。バリデーションに失敗した場合は HTTP ステータスコード 400 (Bad Request) を返し、該当するメニューのページを表示します。

また、データベースへの保存に失敗した場合は、バリデーションに失敗したものと見なし、HTTP ステータスコード 400 (Bad Request) を返し、メインメニューを表示します。

エラーの表示

メインメニューとサイドメニューの選択ページで発生したエラーが表示されるようにビューを変更します。

app/views/orders/main_menu.html.erb
<h3>ご注文は何になさいますか?</h3>
<%= error_messages_for :order %>
<ul>
...
app/views/orders/side_menu.html.erb
<h3>サイドメニューは何になさいますか?</h3>
<%= error_messages_for :order %>
<ul>
...

エラーの表示に関する詳細については、Rails のドキュメント「Active Record Validations and Callbacks」の「8 Displaying Validation Errors in the View」をご覧ください。

それではアプリケーションを実行してみましょう。URI のリクエストパラメータを変更することでエラーの動作確認を行うことができます。

rails4-main-menu-error.png rails4-side-menu-error.png

おわりに

今回はバリデーションの実装を中心に解説しました。

今回のアプリケーションは当初データベースへ保存する部分以外はセッションを使って実装を行いました。しかし、RESTful なアプリケーションを作成するという観点から実装を見直し、現在の形になりました。その甲斐あって、全体的にシンプルでわかりやすい実装になったのではないかと思います。

このチャレンジは今回で終了する予定だったのですが、予定を変更してもう 1 回書くことにしました。次回はポテトバーガー注文アプリケーションをそのまま利用して、JavaScript によるクライアントを実装します。

使用したソフトウェアのバージョン

Ruby on Rails2.3.2

参考文献

トラックバック(0)
  • このブログ記事のトラックバックURL:
コメント