《No Ransack::Search object was provided to search_form_for!》の対処方法

# はじめに
皆さん、お疲れ様です。
今回、"Ransack" Gem を用いた検索機能の実装を行なっている際にとあるエラーが発生し、数時間溶かしてしまったので、備忘録として技術記事を作成しました。
もし同じエラー内容が発生している方は、是非今回の記事内容を解決の糸口として活用していただければと思います...！

# 概要
今回、《Ruby on Rails》を用いた投稿掲示板アプリにて、検索機能を"Ransack" Gem を用いて実装していました。
検索機能の実装内容としては、 **『ヘッダー部分に投稿に関する検索フォームを実装、検索を行うと投稿一覧画面に検索フォームに入力したワードに関する投稿のみを表示する。』** と言うシンプルな検索機能です。

しかし、この検索機能を実装後に実際に検索を掛けて投稿を絞った後、各投稿の詳細画面にアクセスしようとしたところ...
**《ArgumentError in Posts#index　No Ransack::Search object was provided to search_form_for!》**
と言うエラーが発生してしまいました...！

今回は、発生した上記エラーがどういった内容なのかと、その対処方法について纏めさせていただきました。
プログラミング初学者なりに纏めた記事になっておりますので、内容等に誤り等がある場合がございますのでご了承下さい。
もし誤りに気づかれた際は、コメント等でご教授いただけると助かります🙇‍♂️

# Ransack Gem とは？
まず、エラー内容の説明を行う前に "Ransack" Gem について軽く説明をしたいと思います。
"Ransack" は 《Ruby on Rails》 のアプリケーションで、**高度な検索フォームやソート機能を簡単に実装**するためのGemです。
Ransackの主な特徴としては、以下の4つになっています。
### 1. シンプルな検索フォームの生成
Ransackを使用する事で、検索フォームを簡単に生成できる。特に、複雑な検索条件やソート条件を持つフォームを簡単に作成することができるようになる。
### 2. 高度な検索クエリのサポート
Ransackは、「AND/OR条件」「範囲検索」「関連テーブルの検索」など、多くの高度な検索クエリをサポートしている。
### 3. ソート機能
Ransackを使用する事で、結果のソート順を簡単に指定することができる。
### 4. カスタム検索条件
必要に応じて、カスタムの検索条件やソート条件を定義することができる。

上記の特徴の通り、"Ransack"はその名前の由来となる"ransack"（徹底的に探す）のように、**高度な検索やソートの機能を提供**してくれる強力なGemです。そのため、多くのRailsアプリケーションで検索機能の実装に利用されています。

💡Gemの導入方法についてはこちらから⇩

https://github.com/activerecord-hackery/ransack

# 今回発生したエラーについて
では、"Ransack"の説明が終わったところで、今回発生したエラーについて説明をしていきたいと思います。
今回発生したエラーは...**《ArgumentError in Posts#index　No Ransack::Search object was provided to search_form_for!》**
このエラーは、`search_form_for`ヘルパーを使用して検索フォームを作成しようとしたときに、適切な`Ransack::Search`オブジェクトが提供されなかった場合に発生します。

# なぜこのようなエラーが発生したのか？
では、なぜこのようなエラーが発生したのか？

そもそも今回の検索機能については、検索フォームをヘッダー部分に実装するようにしていました。
つまり、`search_form_for`ヘルパーをviewのヘッダーファイルで使用していた、という事になります。
```Ruby:views/shared/_header.html.erb
<%= search_form_for @q, url: posts_path, class: "d-flex" do |f| %>
    <%= f.search_field :title_or_body_cont, class: "form-control me-2", placeholder: "投稿" %>
<% end %>
```
`search_form_for`ヘルパーをヘッダー部分に使用しているという事は、**「その検索フォームはアプリケーションのどのページにアクセスしても表示される」** という事になります。
つまり、ヘッダーが表示されるすべてのページで `Ransack::Search`オブジェクトが利用可能である必要があるんです。

しかし、今回`Ransack::Search`オブジェクトを生成するコードを、`Posts#index`でしか記述していませんでした...。
つまり、Postsコントローラーのindexアクション内でのみ`Ransack::Search`オブジェクトを生成しているため、他のページでは`Ransack::Search`オブジェクトが存在しない状態になっていました...。
```Ruby:controllers/posts_controller.rb
def index
	@q = Post.ransack(params[:q])
	@posts = @q.result(distinct: true).order(created_at: :desc).page(params[:page]).per(10)
end
```
そのため、投稿詳細画面にアクセスしようとしても、showアクション内に`Ransack::Search`オブジェクトが存在しない為にエラーが発生していました。

# 対処方法
では、このような場合どのように対処すれば良いのか？

簡単です。
全てのアクションで`Ransack::Search`オブジェクトを生成すれば良いのです！

今回発生したエラーの原因は...
**『ヘッダーが表示されるすべてのページで`Ransack::Search`オブジェクトが利用可能である必要があるが、実際はindexアクション内にしか`Ransack::Search`オブジェクトが存在していなかった。』**
なので、全てのアクションで`Ransack::Search`オブジェクトを生成すれば問題ありません。

しかし、全てのアクションにオブジェクトを生成するコードを記述するのはかなり手間ですし、コードの見た目も綺麗ではありません...。
なので、コントローラー全体に反映されるよう`application_controller.rb`に記述していくようにします。

```Ruby:controllers/application_controller.rb
class ApplicationController < ActionController::Base
	before_action :set_search

  def set_search
    @q = Post.ransack(params[:q])
    @posts = @q.result(distinct: true).order(created_at: :desc).page(params[:page]).per(10)
  end
end
```
:::note warn
**注意点**
この際、postsコントローラーの各アクションが実行される前に`Ransack::Search`オブジェクトを生成する必要があるので、必ずオブジェクトを生成するメソッド`set_search`を`before_action`で呼び出すようにしましょう。
:::

**上記のように記述することで、postsコントローラーの各アクションに`Ransack::Search`オブジェクトが生成されるようになるので、エラーは発生しなくなります。**

# まとめ
ある特定の画面でのみ検索フォームを実装する場合は、**その画面に対応するコントローラーのアクション内に`Ransack::Search`オブジェクトを生成**しておけば問題ありません。
しかし、複数画面内に検索フォームを実装する場合は、**各画面ごとに対応するアクション内全てに`Ransack::Search`オブジェクトを生成**しておく必要があります…！

私は、ヘッダーに検索機能を実装するのが今回初だったので、いつもの要領でコードを記述してしまいこのようなエラーが発生してしまいました…😭

皆さんも、検索フォームを複数画面に実装する場合はお気をつけ下さい...！🙇‍♂️