カテゴリー選択機能の実装（ActiveHash）

# はじめに
カテゴリー選択機能の実装の流れを残す

## カテゴリー選択を実装するためには
例えば、都道府県名一覧やカテゴリーなど「基本的に変更されないデータ」があったする。
「基本的に変更されないデータ」は、データベースに保存する必要性がない。
一方、ビューファイルなどにそれらのデータを直接書いてしまうと、可読性に欠ける。
そのようなケースでは、<strong>ActiveHash</strong>が有用。

## ActiveHashとは
都道府県名などの変更されないデータをモデルファイル内に直接記述することで、
データベースへ保存せずにデータを取り扱うことができるGem。
すなわち、Active_Hashを用いることで、モデルファイルに直接記述した変更されないデータに対して、ActiveRecordのようなメソッドを用いることができる。（例えば、allやcreateメソッドのことを指している。）

## ActiveHash導入の流れ
### 1. Gemの導入
    
    #　Gemfile内
    #中略
    gem 'active_hash'

    #　ターミナルにて
    % bundle install

### 2. モデルの作成
モデルには、
①選択項目を保存するモデル(例えば商品情報や記事モデルなど)
→rails g model コマンド (今回はarticleモデル)
②カテゴリーの選択肢を作成するためのモデル(ActiveHash用)
→modelフォルダ内で「カテゴリー名.rb」を自作する　（今回はgenreモデル）
を作成する。

### 3. カテゴリー名モデルにデータを定義する
まずは、モデルの「カテゴリー名クラス」を定義し、ActiveHash::Baseクラスを継承する。

ActiveHash::Baseは、あるモデル内（クラス内）でActiveHashを用いる際に必要となるクラス。
ActiveHashのGemに定義されている。
また、ActiveHash::Baseを継承することで、ActiveRecordと同じようなメソッドを使用できるようになる。

    #　記載例
    class Genre < ActiveHash::Base
     self.data = [
       { id: 1, name: '---' },
       { id: 2, name: '経済' },
       { id: 3, name: '政治' },
       { id: 4, name: '地域' },
       { id: 5, name: '国際' },
       { id: 6, name: 'IT' },
       { id: 7, name: 'エンタメ' },
       { id: 8, name: 'スポーツ' },
       { id: 9, name: 'グルメ' },
       { id: 10, name: 'その他' }
     ]
     end

    ※ジャンルのデータは、配列にハッシュ形式で格納している。

### 4. マイグレーションファイルの編集
    # 選択項目を保存するモデル
    t.integer    :カテゴリー名_id     , null: false

選択項目を保存するテーブルの中にカテゴリー名_idという名前のカラムを作成するのは、
保存先のテーブルに紐付いたカテゴリーを取得するため。
例えば、Articleテーブルの中で、Genreモデル（ActiveHash）のidを外部キーとして管理することで、その記事に紐付いたジャンルの取得が実現できる。

    % rails db:migrate
    を忘れないよう注意。

### 5. アソシエーションの設定
ActiveHashを用いてアソシエーションを設定する場合は、
ActiveHashで定義されている<strong>module</strong>をモデルに取り込む必要がある。

- moduleとは
特定の役割を持つメソッドや定数に名前を付けてまとめたもの。
moduleはどんなオブジェクトにも取り込んで使うことが可能。
複数のクラスにまたがるメソッドや定数をmoduleとしてまとめることで、
コードの肥大化を防いだり、複数クラスの特徴を継承させたいクラスを作成するときに利用する。
「メソッドをまとめているもの、取り込んで使用する」

今回、投稿する記事は、1つのジャンルに紐付いています。
そのため、Articleモデルにはbelongs_toを設定する。

ActiveHashを用いて、belongs_toを設定するには、
<strong>extend ActiveHash::Associations::ActiveRecordExtensions</strong>と記述してmoduleを取り込む。

    # article.rb内
    class Article < ApplicationRecord
      extend ActiveHash::Associations::ActiveRecordExtensions
      belongs_to :genre
    end


1つのジャンルは、たくさんの投稿物に紐付いている。そのため、Genreモデルにはhas_manyを設定する。

ActiveHashを用いて、has_manyを設定するには、
<strong>include ActiveHash::Associations</strong>と記述してmoduleを取り込む。

    # 記載例
    class Genre < ActiveHash::Base
     self.data = [
       { id: 1, name: '---' },
       { id: 2, name: '経済' },
       { id: 3, name: '政治' },
       { id: 4, name: '地域' },
       { id: 5, name: '国際' },
       { id: 6, name: 'IT' },
       { id: 7, name: 'エンタメ' },
       { id: 8, name: 'スポーツ' },
       { id: 9, name: 'グルメ' },
       { id: 10, name: 'その他' }
     ]
    
      include ActiveHash::Associations　　⇦追記箇所
      has_many :articles　　⇦追記箇所
    
     end
 
アソシエーション設定方法は色々とあるが、それは公式ドキュメントを見ましょう。

### 6. バリデーション設定
データベースに空の投稿が保存されないようにバリデーションを設定する。

今回のバリデーションには、「numericality」というバリデーションのヘルパーを使用する。

- numericalityとは
数値かどうかを検証するバリデーションの一種。
数値であればデータベースに保存を許可して、それ以外では保存が許可されないようにできる。
今回のアプリケーションでは、---を保存されないようにしたいので、id: 1以外であれば保存できるように設定します。

      # article内
      # ジャンルの選択が「---」の時は保存できないようにする
      validates :genre_id, numericality: { other_than: 1 } 

### 7.エラーメッセージの設定
「---」を選択して保存しようとすると、デフォルトのエラーメッセージでは「must be other than 0」と表示されます。
しかし、このエラーメッセージではユーザーに伝わりづらいため、「can't be blank」というエラーメッセージがユーザーに表示されるよう設定します。

    　# 記載例
    　validates :genre_id, numericality: { other_than: 1 , message: "can't be blank"}

### 8.ビューファイルの設定
form_withでは、collection_selectオプションを用いて、プルダウン状に表示させる。

    # 記載例
    <%= f.collection_select(:genre_id, Genre.all, :id, :name, {}, {class:"genre-select"}) %>

    # 各引数の項目について
    <%= form.collection_select(保存されるカラム名, オブジェクトの配列, カラムに保存される項目, 選択肢に表示されるカラム名, オプション, htmlオプション) %>

    ・第一引数（保存されるカラム名）	
    :genre_id	保存先のカラム名
    
    ・第二引数（オブジェクトの配列）	
    Genre.all	配列データを指定する
    （今回はジャンル情報の配列）
    
    ・第三引数（カラムに保存される項目）	
    id	表示する際に参照するDBのカラム名
    
    ・第四引数（選択肢に表示されるカラム名）	
    name	実際に表示されるカラム名
    
    ・第五引数（オプション）	
    {}	今回はオプションの指定なし
    （include_blankという値のない選択肢を先頭にするオプションなどがあります。）
    
    ・htmlオプション	
    {class:"genre-select"}	今回はクラス名を付与
    （cssを当てるため）

    ※第一〜第四引数は必ず設定する必要があります。

### 9.ストロングパラメーターの編集
ストロングパラメーター内に、:genre_idを追記する。

### 10.ブラウザ上に情報を記載するとき
今回は、ArticleモデルとActiveHashを用いたGenreモデルに、アソシエーションが組まれている。
アソシエーションが組まれている場合、
<strong>単一レコード情報.ActiveHashを用いたモデル名.カラム名</strong>と記述することで、
情報を取得できる。
したがって、ジャンル名を表示する場合は、<strong>article.genre.name</strong>と記述する。