ransackはrails用の検索機能を実装するためのgemです。
比較的シンプルなコードで複雑な検索を実装することができます。
ransackの概要と使い方についてはRansackのススメを参照してください。
ここでは実際に使用するまでのサンプルを作ってみます。
####プロジェクトを作成
rails new ransack_study -T --skip-bundle
cd ransack_study
localeとtimezoneを設定
require File.expand_path('../boot', __FILE__)
〜〜 ( 中略 ) 〜〜
module RansackStudy
class Application < Rails::Application
〜〜 ( 中略 ) 〜〜
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
config.time_zone = 'Tokyo'
config.active_record.default_timezone = :local
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
config.i18n.default_locale = :ja
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
〜〜 ( 中略 ) 〜〜
end
end
####ransackのインストール
Gemfileに追記
vi Gemfile
source 'https://rubygems.org'
gem 'rails', '3.2.11'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3'
gem 'rails-i18n' # この行を追加(ransackには関係ないけどdate_select用)
gem 'ransack' # この行を追加
〜〜( 以下 略 )〜〜
bundle installを実行
bundle install
####今回のサンプル用にscaffold
ユーザーモデルと、ユーザーが紐づく掲示板モデルを作成します。
rails g scaffold user name:string
rails g scaffold topic title:string content:text user:references
掲示板モデルでユーザーを登録するためにちょろっと修正
class Topic < ActiveRecord::Base
belongs_to :user
attr_accessible :content, :title, :user_id #この行を修正
end
ユーザーをselectタグで選択できるように
<%= form_for(@topic) do |f| %>
〜〜 ( 中略 ) 〜〜
<div class="field">
<%= f.label :user %><br />
<%= f.select :user_id, User.all.map { |u| [u.name, u.id] } %><!--この行を修正-->
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
一覧からcontentを削除し、created_atを追加、ユーザー名を表示するように変更
<h1>Listing topics</h1>
<table>
<tr>
<th>Title</th>
<th>User</th>
<th>Created At</th><!--この行を修正-->
<th></th>
<th></th>
<th></th>
</tr>
<% @topics.each do |topic| %>
<tr>
<td><%= topic.title %></td>
<td><%= topic.user.name %></td><!--この行を修正-->
<td><%= topic.created_at.strftime('%Y/%m/%d %H:%M') %></td><!--この行を修正-->
<td><%= link_to 'Show', topic %></td>
<td><%= link_to 'Edit', edit_topic_path(topic) %></td>
<td><%= link_to 'Destroy', topic, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Topic', new_topic_path %>
詳細でユーザー名を表示するように変更
〜〜 ( 中略 ) 〜〜
<p>
<b>User:</b>
<%= @topic.user.name %><!--この行を修正-->
</p>
〜〜 ( 中略 ) 〜〜
とりあえず実行してみる
rake db:migrate
rails s
http://localhost:3000/usersでユーザーを適当に登録
http://localhost:3000/topicsで掲示板を何件か登録
ここまでで準備は完了です。
ここからは実際にransackの機能を使って検索フォームを作成してきます。
####ransackの基本的な使い方
ransackは基本的にはsearchメソッドで条件を指定し、resultで結果を返します。
User.search(name_cont: 'ほげ').result
上の処理を実行すると次のようなSQL文を発行し、ActiveRecord::Relationオブジェクトを返します。
SELECT "users".* FROM "users" WHERE ("users"."name" LIKE '%ほげ%')
また、ransackには検索フォーム用のフォームビルダーも用意されています、
今回はそれを利用して掲示板の検索フォームを作成していきます。
####search_form_forの利用
TopicsControllerでsearchオブジェクトを生成
class TopicsController < ApplicationController
# GET /topics
# GET /topics.json
def index
@search = Topic.search(params[:q]) # この行を追加
@topics = @search.result #この行を修正
respond_to do |format|
format.html # index.html.erb
format.json { render json: @topics }
end
end
〜〜 ( 中略 ) 〜〜
end
search_form_forを利用して検索フォームを作成
まずはtopicのtitleを前後あいまい検索する場合index.html.erbを以下のように修正
<h1>Listing topics</h1>
<%= search_form_for @search do |f| %>
<%= f.label :title_cont, 'タイトル' %>
<%= f.text_field :title_cont %>
<div>
<%= f.submit '検索する' %>
</div>
<% end %>
〜〜 ( 以下 略 ) 〜〜
###以上
これだけtitleの曖昧検索が実装されます。
動かしてみるとこんな感じになってると思います。
ここからはindex.htmlを追加するだけで検索処理を実装することが可能です。
試しに作成日の範囲指定を追加してみます。
index.html.erbを以下のように修正します。
<h1>Listing topics</h1>
<%= search_form_for @search do |f| %>
<%= f.label :title_cont, 'タイトル' %>
<%= f.text_field :title_cont %>
<!--ここから追加-->
<br />
<%= f.label :created_at, '作成日' %>
<%= f.date_select :created_at_gteq, include_blank: true %>〜
<%= f.date_select :created_at_lteq, include_blank: true %>
<!--ここまで追加-->
<div>
<%= f.submit '検索する' %>
</div>
<% end %>
〜〜 ( 以下 略 ) 〜〜
動かしてみると以下のようになり、作成日での絞込みが実装されました。
日付の指定をした場合に発行されるSQLを確認してみます。
SELECT "topics".* FROM "topics" WHERE (("topics"."title" LIKE '%について%' AND "topics"."created_at" >= '2013-05-01 00:00:00.000000' AND "topics"."created_at" <= '2013-05-09 00:00:00.000000'))
このままではtoのほうの日付の条件が指定した日付の 00:00:00までしか対象になりません。
日付で絞り込むなら 23:59:59までを検索対象にしたいですよね。
なので、以下のようなファイル(config/initializers/ransack.rb)を作成します。
Ransack.configure do |config|
config.add_predicate 'lteq_end_of_day',
:arel_predicate => 'lteq',
:formatter => proc { |v| v.end_of_day },
:compounds => false
end
formatterを指定し、index.html.erbも以下のように修正します。
<h1>Listing topics</h1>
<%= search_form_for @search do |f| %>
<%= f.label :title_cont, 'タイトル' %>
<%= f.text_field :title_cont %>
<br />
<%= f.label :created_at, '作成日' %>
<%= f.date_select :created_at_gteq, include_blank: true %>〜
<%= f.date_select :created_at_lteq_end_of_day, include_blank: true %><!--この行を修正-->
<div>
<%= f.submit '検索する' %>
</div>
<% end %>
〜〜 ( 以下 略 ) 〜〜
ここでサーバーを一度再起動し、再度検索をし発行されるSQLを確認します。
SELECT "topics".* FROM "topics" WHERE (("topics"."title" LIKE '%について%' AND "topics"."created_at" >= '2013-05-01 00:00:00.000000' AND "topics"."created_at" <= '2013-05-08 23:59:59.999999'))
いい感じですね♪
最後に、関連するテーブルの項目での絞込みも試してみます。
topicsにひもづくユーザの名前で前後あいまい検索を行います。
index.html.erbを以下のように修正します。
<h1>Listing topics</h1>
```app/views/topics/index.html.erb
<h1>Listing topics</h1>
<%= search_form_for @search do |f| %>
<%= f.label :title_cont, 'タイトル' %>
<%= f.text_field :title_cont %>
<br />
<%= f.label :created_at, '作成日' %>
<%= f.date_select :created_at_gteq, include_blank: true %>〜
<%= f.date_select :created_at_lteq_end_of_day, include_blank: true %>
<!--ここから追加-->
<br />
<%= f.label :user, 'ユーザー' %>
<%= f.text_field :user_name_cont %>
<!--ここまで追加-->
<div>
<%= f.submit '検索する' %>
</div>
<% end %>
〜〜 ( 以下 略 ) 〜〜
実行すると以下のようになり、ユーザー名での絞込みが実装できましたね。
####ソートの実装
ransackにはソート用のhelperも用意されています。
それらを利用して掲示板一覧にソートを実装してみます。これもviewの修正だけで実装することが可能です。
index.html.erbのtableのヘッダーを以下のように修正します。
<h1>Listing topics</h1>
〜〜 ( 中略 ) 〜〜
<table>
<tr>
<th><%= sort_link(@search, :title, 'タイトル') %></th><!--この行を修正-->
<th><%= sort_link(@search, :user_name, 'ユーザー') %></th><!--この行を修正-->
<th><%= sort_link(@search, :created_at, '作成日時') %></th><!--この行を修正-->
<th></th>
<th></th>
<th></th>
</tr>
〜〜 ( 以下 略 ) 〜〜
これで実行すると、ヘッダーがリンクになりクリックするとソートが実装されていることがわかります。
####まとめ
ransackとransackのフォームビルダーを利用することで、比較的複雑な検索条件もほぼviewを修正するだけで実装することができました。動的に検索条件を作成することもview側を動的に作成するだけで可能ですので比較的簡単に実装できるかと思います。それらのチュートリアルがRailsCasts(#370 Ransack)にありますので、そちらを参照してみてください。
####今回のソース
今回のソースは以下で公開しています。
LuckOfWise/ransack_study https://github.com/LuckOfWise/ransack_study