twitter-bootstrap-railsでバリデーション後のフォームの色を変える

長ったらしいタイトルですが、まあそういうことです。
前日うまいこと行かなくてうわーんとなってたわけですが。
まあ適当にやればなんとかなるということでメモ。

準備

適当にscaffoldを使ってコントローラとビューを作ってbootstrap仕様にします。
Gemfileにtherubyracer,twitter-bootstrap-rails, less-railsを追加し忘れないように。

$ rails new color
$ cd color
$ rails g scaffold Hoge title:string body:text
$ rake db:migrate
$ rails g bootstrap:install less
$ rails g bootstrap:layout application fluid
$ rails g bootstrap:themed hoges

最後のコマンドやるとconflict云々出てくるけどaとタイプすればおk.
config/routes.rbでルートを設定

root :to => 'hoges#index'

デフォルトのページを削除

$ rm public/index.html

scaffolds.css.scssを削除

$ rm app/assets/stylesheets/scaffolds.css.scss 

バリデーション

app/models/hoge.rbにバリデーションを設定。

class Hoge < ActiveRecord::Base
  attr_accessible :body, :title
  validates :title, presence: true
  validates :body, presence: true, length: {minimum: 20}
end

ビューもいじる。バリデーションエラーを表示させるようにします。
apps/views/hoges/_form.html.erb

<% if @hoge.errors.any? %>
<div class="alert alert-error">
  <h2><%= pluralize(@hoge.errors.count, "error") %></h2>
  <ul>
  <% @hoge.errors.full_messages.each do |msg| %>
    <li><%= msg %></li>
  <% end -%>
  </ul>
</div>
<% end -%>

っていうのを先頭に追加します。エラーがそれっぽい枠に囲まれてそれっぽく表示してくれます。残念ながら英語ですが。
で、フォームの色を変更するにはコントローラの方もいじります。
エラーがあった項目を配列に入れます。ここではフォームの入力項目ごとに用意されてるシンボルを使っています。
キーを項目、値をエラー文にしたハッシュを使ってフォームの横に表示させるようにしたいけど実装できなかったのでまた今度。
hoges_controller.rbにて

  def new
    @error = Array.new
・・・
  def edit
    @error = Array.new
・・・
  def create
    @error = Array.new
    @hoge = Hoge.new(params[:hoge])
    respond_to do |format|
      if @hoge.save
        format.html { redirect_to @hoge, notice: 'Hoge was successfully created.' }
        format.json { render json: @hoge, status: :created, location: @hoge }
      else
        @hoge.errors.messages.each_key do |key| #エラーがあった入力項目のシンボルがkeyになる。もう少しいい方法がありそう。
          @error << key
        end
        format.html { render action: "new" }
        format.json { render json: @hoge.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    @hoge = Hoge.find(params[:id])
    @error = Array.new

    respond_to do |format|
      if @hoge.update_attributes(params[:hoge])
        format.html { redirect_to @hoge, notice: 'Hoge was successfully updated.' }
        format.json { head :no_content }
      else
        @hoge.errors.messages.each_key do |key|
          @error << key
        end
        format.html { render action: "edit" }
        format.json { render json: @hoge.errors, status: :unprocessable_entity }
      end
    end
  end
・・・

ビューをいじる。
twitter-bootstrapは、control-group errorというdiv要素に対してうまいこと赤くしてくれるのでそうします。

・・・
  <div class="control-group <%= "error" if @error.include?(:title) %>">
・・・
  <div class="control-group <%= "error" if @error.include?(:body) %>">
・・・

確認。

やったぜ。

その他

出来たには出来たんだけど、なんか美しくない。
Railsはもともとバリデーションで弾かれたとこのdiv要素になんか勝手にclassを追加してくれるので、CSSだけいじればいいはずなので。
わざわざビューとコントローラにコードを書くのは美しくないし、変なエラーになってもよろしくないのでこの方法はちょっとうんこ。