Ransackの導入時に躓いたり転んだりした。 

Ransackの導入って単純だと思っていましたが、躓きました。
タスク一覧画面で、タイトルのキーワードと進捗のステータスでタスクを絞り込み検索する機能を実装しようと考えています。

# 手順
公式ドキュメントの手順に沿って実装しました。
https://github.com/activerecord-hackery/ransack
### gem 'ransack' のインストール
```ruby

gem 'ransack', '~> 1.8', '>= 1.8.8'
```
```
$ bundle install
```

### `app/controllers/tasks_controller.rb`の設定
```ruby

def index
  @q = Task.ransack(params[:q])
  @tasks = @q.result(distinct: true)
end
```
### ビューファイルの編集
`app/views/tasks/index.slim`

```
.search__body
  = search_form_for(@q, url:root_path) do |f|
    = f.label :title_cont
    = f.search_field :title_cont, placeholder: "Input Title"
    = f.label :status_id_eq
    = f.select :status_id_eq, Task.status_ids.to_a.map{|s|[t("activerecord.enum.status_id.#{s[0]}"), "#{s[0]}"]}
    = f.submit
```

### その他
 `db/schema.rb` はこんな感じです。

```ruby
ActiveRecord::Schema.define(version: 20180516232642) do

  create_table "tasks", force: :cascade do |t|
    t.string "title", null: false
    t.text "detail", default: "detail", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "deadline"
    t.integer "status_id"
  end

end
```

`app/models/task.rb`はこんな感じです。

```ruby
class Task < ApplicationRecord
  validates :title, presence: true
  validates :detail, presence: true
  enum status_id: %i[undefined untouched in_progress done]
end
```
 `status_id`の翻訳は以下の通りになります。
`config/locale/ja.yml`

```
  activerecord:
    enum:
      status_id:
        undefined: "未設定"
        untouched: "未着手"
        in_progress: "作業中"
        done: "完了"
```

参考程度に`app/helpers/tasks_helper.rb`

```ruby
module TasksHelper
  def deadline_format(datetime)
    datetime.present? ? datetime.strftime('%Y.%m.%d') : t(:undefined)
  end

  def status_format(status)
    t(status.presence) || t(:undefined)
  end
end

```

# 何に躓いているのか。
上記の状態で絞り込み検索を行なっても正しい結果を返してくれません。
検索結果はいつもゼロ。


# 試行錯誤
### デバックで流れを確認してみます。
いつもお世話になっている`binding.pry`

```ruby
  def index
    @q = Task.ransack(params[:q])
    @tasks = @q.result(distinct: true)
    binding.pry
  end
```
最初のデバックは`exit`します。（意味ないんで。）
そして2回目。今回は`明日`というキーワードを含み、且つ進捗が`未着手`のタスクを探します。
注）未着手=>untouched=>1
未着手は`i18n`では`"untouched"`, そして `"untouched"` は`enum`で`1`と設定されています。

止まったところで色々と調べます。

```ruby

    4: def index
    5:   @q = Task.ransack(params[:q])
    6:   @tasks = @q.result(distinct: true)
    7:   binding.pry
 => 8: end

[1] pry(#<TasksController>)> @tasks
  Task Load (0.7ms)  SELECT DISTINCT "tasks".* FROM "tasks" WHERE ("tasks"."title" ILIKE '%明日%' AND "tasks"."status_id" = 0)
=> []
[2] pry(#<TasksController>)> 
```

### `@tasks`の値
`@tasks`は空の配列を返しています。何故？
検索が期待通りの結果を返してくれるならば該当するタスクを表示してくれるはずなのですが。

```ruby

[2] pry(#<TasksController>)> @tasks.class
=> Task::ActiveRecord_Relation
[3] pry(#<TasksController>)> @q
=> Ransack::Search<class: Task, base: Grouping <conditions: [Condition <attributes: ["title"], predicate: cont, values: ["明日"]>, Condition <attributes: ["status_id"], predicate: eq, values: ["untouched"]>], combinator: and>>
[4] pry(#<TasksController>)> params[:q]
=> <ActionController::Parameters {"title_cont"=>"明日", "status_id_eq"=>"untouched"} permitted: false>
```

`@tasks`のクラスは`Task::ActiveRecord_Relation`というのは大丈夫です。
`params[:q]`で検索条件をパラメーターとして取得し、それを`SQL文`に変換してDBから該当するインスタンスを取得したいのですが、よく見ると`AND "tasks"."status_id" = 0`となっていますね。
未着手は`enum`で`1`と設定しているのに`@tasks`には`AND "tasks"."status_id" = 0`と書かれていま。
ここ、怪しいです。
案の定ほかの進捗ステータスで検索しても`AND "tasks"."status_id" = 0`でした。
そりゃ、検索結果は０件だ。

### 何故？

今回タスクの進捗を管理するために`status_id`というカラムを用意しました。
`status_id`カラムの型は`integer`にして、`enum`を使い数値に対応する文字列（半角の英語）を設定しています。そしてその文字列を`i18n`で日本語にしてビューで表示するようにしています。

先程の例だと、`未着手`=>`untouched`=>`1`という感じ
（`未着手`は`i18n`では`"untouched"`, そして `"untouched"` は`enum`で`1`と設定されています。）

これで、何が期待した通りに出来ていないのか判明しましたが、
次はどこが原因でそうなったのか調べます。

今度はキーワード`明日`、ステータス`作業中`で実行し、`binding.pry`をしてみます。
(`作業中`=>`in_progress`=>`2`)

```ruby

    4: def index
    5:   @q = Task.ransack(params[:q])
    6:   @tasks = @q.result(distinct: true).order('created_at DESC')
    7:   binding.pry
 => 8: end

[1] pry(#<TasksController>)> params[:q]
=> <ActionController::Parameters {"title_cont"=>"明日", "status_id_eq"=>"in_progress"} permitted: false>
[2] pry(#<TasksController>)> Task.ransack(params[:q])
=> Ransack::Search<class: Task, base: Grouping <conditions: [Condition <attributes: ["title"], predicate: cont, values: ["明日"]>, Condition <attributes: ["status_id"], predicate: eq, values: ["in_progress"]>], combinator: and>>
[3] pry(#<TasksController>)> @q
=> Ransack::Search<class: Task, base: Grouping <conditions: [Condition <attributes: ["title"], predicate: cont, values: ["明日"]>, Condition <attributes: ["status_id"], predicate: eq, values: ["in_progress"]>], combinator: and>>
[4] pry(#<TasksController>)> @q.result(distinct: true)
  Task Load (0.6ms)  SELECT DISTINCT "tasks".* FROM "tasks" WHERE ("tasks"."title" ILIKE '%明日%' AND "tasks"."status_id" = 0)
=> []
```

`@q`までは期待した検索結果が反映されています。
しかし`@q`に対して`result`メソッドを実行すると
返してくれる`ActiveRecord::Relation`クラスのオブジェクトのSQL文の部分に書かれた
進捗ステータスのidは0になってしまいました。
期待した進捗ステータスのidは2だったのですが…

注）記事内で行なっている検索では期待する検索結果が必ず存在する条件で検索を行なっています。

# 原因判明

このことをメンターの方に質問をしたところ
以下の記事を教えて頂きました。
http://www.tom08.net/entry/2016/12/05/121746
上記リンクの記事によると、
<strong>`ransack`は`enum`に対応していないので、`f.select`で選択した`status_id`は`ActiveRecord::Relation`クラスのオブジェクトに変換される際、常に`0`になってしまうようです。</strong>
なんでもっと早く気づけなかったのか…
`enum`の`i18n`導入時は頑なに`enum_help`を使わなかったのですが、もうダメなようです。


# 解決
といことで`enum_help`を導入。

### gem 'enum_help' のインストール

```ruby

gem 'enum_help', '~> 0.0.15'
```
```
$ bundle install
```

### `config/locale/ja.yml`を編集

```
enums:
  task:
    status_id:
      undefined: "未設定"
      untouched: "未着手"
      in_progress: "作業中"
      done: "完了"
```
### ビューファイルの編集

http://319ring.net/blog/archives/3041/
上記リンクの記事を参考にして修正しました。

`app/views/tasks/index.slim`

```
.search__body
  = search_form_for(@q, url:root_path) do |f|
    = f.label :title_cont
    = f.search_field :title_cont, placeholder: "#{t(:keyword)}"
    = f.label :status_id_eq
    = f.select :status_id_eq, Task.status_ids.map{|k, v| [Task.status_ids_i18n[k], v]}
    = f.submit
```

`bin/rails c`コマンドでコンソールを立ち上げて値を確認しました。

```ruby

Running via Spring preloader in process 10882
Loading development environment (Rails 5.1.6)
[1] pry(main)> statuses = Task.status_ids
=> {"undefined"=>0, "untouched"=>1, "in_progress"=>2, "done"=>3}
[2] pry(main)> statuses = Task.status_ids.map{|k,v| [Task.status_ids_i18n[k], v]}
=> [["未設定", 0], ["未着手", 1], ["作業中", 2], ["完了", 3]]
[3] pry(main)> 
```

これで無事`ransack`の検索機能が機能するようになりました。


# 課題
今回の問題を解決するにあたって以下のことが大事だと感じました。
## ①エラー解決のためのヒントの検索力
今回の問題の解決にかなりの時間を割き原因を探りましたがましたが、結局メンターの方に教えていただいた記事がきっかけで解決することができました。その時、もっと可能性のある原因を推測しそれぞれに対応した適切なキーワードで検索をすれば良かったと感じました。
## ②ソースコードを読む能力
ソースコードはやはり問題解決の最大のヒントになると思います。今回はたまたま記事を見つけたことで解決できましたが、そうでない場合は実際に何が、どのように動いているのか調べる必要があります。正直まだソースコードを理解できるほどの能力はないですが、ソースコードを理解できようになることを`Ruby`を学習する一つの目標にしようと思いました。