ransackで年度検索を実装する

[ransack]()を用いて年度検索を実装する．
動作デモは[こちら](https://github.com/tanakaworld/ransack-demo)．

# 前提条件
- Ruby v2.4.0
- Rails v5.0.2

- nameとbirthayを持つUserを作成する．

```
rails g scaffold User name:string birthday:date
```

- gemを追加する．

```rb:Gemfile
gem 'ransack'
```

# Nameで検索

名前文字列が含まれるかどうかで検索する．
`:name_cont` の記述がそれに当たる．

```ruby:app/controllers/users_controller.rb
def index
  @q = User.ransack(params[:q])
  @users = @q.result
end
```
```erb:app/views/users/index.html.erb
<%= search_form_for @q, url: :users do |f| %>
    <%= f.label :name %>
    <%= f.text_field :name_cont, placeholder: 'Name' %>

    <br>
    <%= f.submit 'Search' %>
<% end %>
```

![image](https://qiita-image-store.s3.amazonaws.com/0/31862/7f57e6b8-9184-2363-c943-6cff3b77f068.png)

これで生成されるSQLは下記の通り．

```ruby
# @q.result.to_sql
SELECT "users".* FROM "users" WHERE ("users"."name" LIKE '%A%')
```

# Birthdayを期間で検索
期間の開始・終了を両方選択させるパターン．

```erb:app/views/users/index.html.erb
<%= search_form_for @q, url: :users do |f| %>
    <%= f.label :name %>
    <%= f.text_field :name_cont, placeholder: 'Name' %>

    <%= f.label :birthday %>
    <%= f.date_field :birthday_gteq, include_blank: true %> 〜　
    <%= f.date_field :birthday_lteq, include_blank: true %>

    <br>
    <%= f.submit 'Search' %>
<% end %>
```

![image](https://qiita-image-store.s3.amazonaws.com/0/31862/af527bd3-aaee-aafd-4773-93f955d96ded.png)

これで生成されるSQLは下記の通り．
`birthday_gteq` が `"birthday" >= '2017-03-01'` を指し， `birthday_lteq` が `"birthday" <= '2017-03-03'` を指す．

```ruby
# @q.result.to_sql
SELECT "users".* FROM "users" WHERE ("users"."birthday" >= '2017-03-01' AND "users"."birthday" <= '2017-03-03')
```

# Birthdayを年度で検索（年度を選択させるパターン）

期間の開始と終了をしていするのではなく，年度を一つだけ選択して検索したい．
[Custom Predicate](https://github.com/activerecord-hackery/ransack/wiki/custom-predicates) を作成して，年度（yyyy/04/01-yyyy+1/03/31）で絞込み出来るようにする．

```ruby:config/initializers/ransack.rb
Ransack.configure do |config|
  config.add_predicate :during_year,
                       arel_predicate: :between,
                       formatter: proc { |v|
                         Time.zone.parse("#{v.year}-4-1").to_date...Time.zone.parse("#{v.year + 1}-4-01").to_date
                         # または `..` で 4/1〜3/31 でもOK
                         # Time.zone.parse("#{v.year}-4-1").to_date..Time.zone.parse("#{v.year + 1}-3-31").to_date
                       },
                       type: :date
end
```

Custom Predicate は， `<field name>_<custom_predicate_name>` で使用できる．

```erb:app/views/users/index.html.erb
<%= search_form_for @q, url: :users do |f| %>
    <%= f.label :name %>
    <%= f.text_field :name_cont, placeholder: 'Name' %>

    <%= f.label :birthday %>
    <%= f.date_field :birthday_gteq, include_blank: true %> 〜　
    <%= f.date_field :birthday_lteq, include_blank: true %>

    <%= f.label :birthday_during_year %>
    <%= f.date_select :birthday_during_year,
                      discard_day: true, discard_month: true,
                      include_blank: true %>

    <br>
    <%= f.submit 'Search' %>
<% end %>
```

![image](https://qiita-image-store.s3.amazonaws.com/0/31862/0ec68add-adc0-9d89-c49a-1651defe9268.png)

これで生成されるSQLは下記の通り．

```ruby
# @q.result.to_sql
SELECT "users".* FROM "users" WHERE ("users"."birthday" >= '2016-04-01' AND "users"."birthday" < '2017-04-01')
```

参考

- https://github.com/activerecord-hackery/ransack
- http://qiita.com/nysalor/items/9a95d91f2b97a08b96b0
- https://github.com/activerecord-hackery/ransack/issues/101