Railsでのブール型(Boolean)の扱い

今回、railsでブール型を使った(正確にはmigrationのtypeに:booleanを指定した)ので、Railsでブール型を使う際の注意などをメモしておきます。

ブール型の扱い

  • テーブルのカラムをマイグレーションで定義する際に以下の様にすると、Railsはそのカラムをブール型として扱うようになります。
  • ブール型カラムの定義
 def self.up
   create_table(:users) do |table|
    table.column :active, :boolean, :null => false, :default => true 
   end
  end

  def self.down
    drop_table :users
  end  


以降、このカラムは次のような挙動をします。

  • DB(MySQL上)では、T/Fをtinyintの1/0で扱われる
  • オブジェクトレベルでは、"1"/"0"で管理される。これは、script/consoleで確認できる

C:\Rails>ruby script/console test
Loading test environment.
>> u = User.find(1)
=> #"1", "id"=>"1"}>

  • アクセサメソッドを通したり、attributesメソッドを使うと、キャストされた値が返ってくる

>> u.active
=> true
>> u.attributes
=> {"avtive"=>true, "id"=>1}

  • 代入した値が、save時にキャストされる

>> u.active
=> true
>> u.active = false
=> false
>> u.attributes
=> {"active"=>false, "id"=>1}
>> u
=> #false, "id"=>"1"}> #ここでは代入した値trueが使われている
>> u.save
=> true
>> u.reload
=> #"0", "id"=>"1"}> #キャストされて保存される

ブール型を扱う際の注意

フォームで送る場合は文字列で送る

これは、通常は意識しなくて大丈夫。例えば以下のようにしても、パラメータが送られる際には文字列に変換されるので、意識する必要はないです。

  link_to :action => "index", :active => @user.active


しかし、テストの場合はこれを意識する必要があります。例えば、次のようなテストを書いたとします。

  @bob = users(:bob)          #フィクスチャよりデータを持ってくる
  p @bob.active               #= true
  get :index, :user => @bob.attributes


この時、コントローラ側で、params[:user][:active]を見ると、その値はnilになります。本当なら、文字列"true"が送られてきて欲しい所です。
これは、@bob.attributesの結果が、{"active" => "true"}でなく、{"avtive" => true}だからです。trueをそのままパラメータとして送っても、文字列に変換されずにnilになってしまいます。
よって、上記のテストを成立されるには次のようにする必要があります。

  @bob = users(:bob)          #フィクスチャよりデータを持ってくる
  get :index, :user => {"id" => @bob.id, "active" => @bob.active_before_type_cast}


これで、キャストされる前の値("1"/"0")が送られるようになります。尚、@bob.active.to_sとして、"true"/"false"としても正しく動作します。

ブール型カラムを扱う際には、アクセサメソッドと、?メソッドの違い

ブール型のカラムのアクセサメソッドは次のように動作します。

属性に格納されている値が「1,"1","t",true,"true"」の場合はtrue。他はfalseになる。


ブール型のカラムを扱う際には、直接アクセサメソッドを使うことを推奨していません。つまり、以下のようなメソッドは推奨されません。

def loginable?
  self.active && ・・・ 
end


代わりに、最後に?をつけた、次のような形式を推奨しています。

def loginable?
  self.active? && ・・・ 
end


?をつけたメソッドの挙動は次のようになります。

属性に格納されている値が「nil,"",0,"0",false,"false","f"」の場合はfalse、その他はtrueを返します。


つまり、?が付かないメソッドでは、trueの候補を決め、他をfalseとしています。逆に、?がついたメソッドでは、falseの候補を決め、他をtrueとしています。

>>u.active
=>false
>>u.active = "t"
=> "t"
>>u.active
=>true
>>u.active?
=>true
>>u.active = "z" #このとき2つのメソッドの挙動が変わる
=> "z"
>>u.active
=>false
>>u.active?
=>true


Ruby本来の挙動は、「nil,false」以外はtrueとみなすので、false候補を定義する?をつけたメソッドの方が、よりRubyの感覚に近いといえます。