ActiveHashの導入目的とアプリ実装までの簡単な流れ

※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

## ActiveHashとは
都道府県名などの変更されないデータを**モデルファイル内に直接記述する**ことで、データベースへ保存せずにデータを取り扱うことができるgemです。

ActiveHashを導入すれば、以下のようなプルダウンメニューを作成するときに大いに役立ちます。

![スクリーンショット 2021-01-16 23.55.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/849306/87a1f948-e7a1-ea7a-dd40-c03d4c564004.png)

## ActiveHashを導入する目的
ユーザー情報のような編集されることがあるデータと違って、都道府県名などは変更されないデータなので、データベースにわざわざ保存する必要がありません。

かといってビューファイルに直接都道府県名のプルダウンメニューを作ってしまうとコードが肥大化して可読性が下がってしまいます。

そこでActiveHashを使ってモデルからデータを呼び出すという方法を採ることでコードの管理がしやすくなります。


## プルダウンメニューの実装
住んでいる都道府県を投稿するアプリを使って説明します。

以下のコマンドを入力してRailsアプリを作成します。

```:ターミナル
rails _6.0.0_ new activehash_app -d mysql
rails db:create
```

続いて、モデル、コントローラー、ビューを用意します。

### モデルを作成

```:ターミナル
rails g model address      
```

上記コマンドで生成されたマイグレーションファイルに`name`カラムと`prefecture_id`カラムを以下のとおり記述してマイグレートします。

`prefecture_id`カラムには、後でActiveHash gemで導入するプルダウンメニューの情報が入ります。

データ型がinteger型なのは、のちに作成するprefectureのデータをアソシエーションのようにidから引用するためです。

```rb:2021XXXXXXXXXX_create_addresses.rb
class CreateAddresses < ActiveRecord::Migration[6.0]
  def change
    create_table :addresses do |t|
      t.string :name, null: false
      t.integer :prefecture_id, null: false
      t.timestamps
    end
  end
end
```

```:ターミナル
rails db:migrate
```

### コントローラーとビューを作成
下記のコマンドでコントローラーに加えて、一覧表示と新規投稿のビューをまとめて作成します。

```:ターミナル
rails g controller addresses index new
```
コントローラーとビュー（`index`と`new`）を以下のように記述します。

```app/controllers/addresses_controller.rb
class AddressesController < ApplicationController
  def index
    @addresses = Address.order("created_at DESC")
  end

  def new
    @address = Address.new
  end

  def create
    @address = Address.new(address_params)
    if @address.valid?
      @address.save
      return redirect_to root_path
    else
      render "new"
    end
  end

  private
  def address_params
    params.require(:address).permit(:name, :prefecture_id)
  end
end
```

```html:app/views/addresses/index.html.erb
<h1>あなたの住んでいる都道府県は？</h1>
<%= link_to "投稿する", new_address_path %>
<div>
  <ul>
    <% if @addresses %>
      <% @addresses.each do |address| %>
      <li>
        <%= address.name %>
        <%= address.prefecture.name %>
      </li>
      <% end %>
    <% end %>
  </ul>
</div>

```

```erb:app/views/addresses/new.html.erb
<%= form_with model: @address, url:addresses_path, local: true do |f| %>
  <div class="article-box">
    あなたの名前と住んでいる都道府県を投稿する
    <div>
      <p><%= f.text_area :name, placeholder:"あなたの名前" %></p>
      <p><%= f.collection_select(:prefecture_id, Prefecture.all, :id, :name, {}, {class:"genre-select"}) %></p>
      <%= f.submit "投稿する" ,class:"btn" %>
    </div> 
  </div>
  <%= link_to "戻る", root_path %>
<% end %>
```

## ActiveHashの導入
いよいよActiveHashを導入します。
`Gemfile`の一番下に下記の記述をして、ターミナルで`bundle install`します。

```rb:Gemfile
gem 'active_hash'
```

```:ターミナル
bundle install
```

都道府県のリストを用意するため、prefectureモデルを作成します。

ここで、いつもの`rails g model :モデル名`コマンドに`--skip-migration`というオプションを付けます。

`--skip-migration`はマイグレーションファイルの作成をスキップしてくれるオプションです。都道府県に関する情報はデータベースに保存しないためです。

```:ターミナル
rails g model prefecture --skip-migration
```
作成した`prefecture.rb`に、以下のように記述します。

```app/models/prefecture.rb
class Prefecture < ActiveHash::Base
  self.data = [
    {id: 0, name: '--'}, {id: 1, name: '北海道'}, {id: 2, name: '青森県'}, 
    {id: 3, name: '岩手県'}, {id: 4, name: '宮城県'}, {id: 5, name: '秋田県'}, 
    {id: 6, name: '山形県'}, {id: 7, name: '福島県'}, {id: 8, name: '茨城県'}, 
    {id: 9, name: '栃木県'}, {id: 10, name: '群馬県'}, {id: 11, name: '埼玉県'}, 
    {id: 12, name: '千葉県'}, {id: 13, name: '東京都'}, {id: 14, name: '神奈川県'}, 
    {id: 15, name: '新潟県'}, {id: 16, name: '富山県'}, {id: 17, name: '石川県'}, 
    {id: 18, name: '福井県'}, {id: 19, name: '山梨県'}, {id: 20, name: '長野県'}, 
    {id: 21, name: '岐阜県'}, {id: 22, name: '静岡県'}, {id: 23, name: '愛知県'}, 
    {id: 24, name: '三重県'}, {id: 25, name: '滋賀県'}, {id: 26, name: '京都府'}, 
    {id: 27, name: '大阪府'}, {id: 28, name: '兵庫県'}, {id: 29, name: '奈良県'}, 
    {id: 30, name: '和歌山県'}, {id: 31, name: '鳥取県'}, {id: 32, name: '島根県'}, 
    {id: 33, name: '岡山県'}, {id: 34, name: '広島県'}, {id: 35, name: '山口県'}, 
    {id: 36, name: '徳島県'}, {id: 37, name: '香川県'}, {id: 38, name: '愛媛県'}, 
    {id: 39, name: '高知県'}, {id: 40, name: '福岡県'}, {id: 41, name: '佐賀県'}, 
    {id: 42, name: '長崎県'}, {id: 43, name: '熊本県'}, {id: 44, name: '大分県'}, 
    {id: 45, name: '宮崎県'}, {id: 46, name: '鹿児島県'}, {id: 47, name: '沖縄県'}
  ]
  include ActiveHash::Associations #ActiveHashに定義されているモジュールを読み込み
  has_many :address #addressモデルとのアソシエーションを定義
end
```

都道府県のデータを配列にハッシュ形式で入れます。
また、`include ActiveHash::Associations`でActiveHashに定義されているモジュールを読み込み、`has_many`を記述してAddressモデルとのアソシエーションを定義しています。

ActiveHashのモジュールについては公式GitHubの[README](https://github.com/zilkey/active_hash)と[ライブラリ](https://github.com/zilkey/active_hash/tree/master/lib)を参照してください。

`address.rb`にも同様に、モジュールを読み込み、`belongs_to`でPrefectureモデルとのアソシエーションを定義します。

```app/models/address.rb
class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions #Activehashに定義されているモジュールを読み込み
  belongs_to :prefecture #prefectureモデルとのアソシエーションを定義
end
```
上記のアソシエーションを定義することで、先ほど作成した`index.html.erb`でPrefectureモデルに格納した都道府県名にアクセスできるようになりました。

```erb:app/views/addresses/index.html.erb（再掲）
<h1>あなたの住んでいる都道府県は？</h1>
<%= link_to "投稿する", new_address_path %>
<div>
  <ul>
    <% if @addresses %>
      <% @addresses.each do |address| %>
      <li>
        <%= address.name %>
        <%= address.prefecture.name %><%# アソシエーション定義によってprefecure.rbに格納している都道府県名にアクセスできる %>
      </li>
      <% end %>
    <% end %>
  </ul>
</div>

```
## 参考資料
[ActiveHashの公式GitHub](https://github.com/zilkey/active_hash)