Railsのgem 'active_hash'で都道府県データを作成してみた

こんにちは!kossyです!




本日気になったニュースはこちら
japan.cnet.com

オンライン診療、遠隔医療分野はこれからどんどん盛り上がりそうです。
病院の薬も市販の薬もそこまで効能に大差はないと言う話も聞きますし、
規制でガチガチになっている医療業界が少しでも変わればいいなと思います。





さて、今回はRailsでモデルに都道府県データを持たせずに、
active_hashで持たせる方法について、ブログに残してみたいと思います。




環境
Rails 5.2.3
Ruby 2.6.3
MacOS Mojave
active_hash 2.2.0

事前準備

例によってプロジェクトを新規作成して試してみます。

$ rails new active_hash_test

$ cd active_hash_test

次にactive_hashを導入します。

./Gemfile

gem 'active_hash'

で、bundle install。

$ bundle install



次にモデルを作成します。

$ rails g model Address prefecture_id:integer city:string

Running via Spring preloader in process 21996
      invoke  active_record
      create    db/migrate/20190108110055_create_addresses.rb
      create    app/models/address.rb
      invoke    test_unit
      create      test/models/address_test.rb
      create      test/fixtures/addresses.yml

そのままrails db:migrateします。

$ rails db:create
$ rails db:migrate

次はモデル周りを記述します。





Addressモデルの編集とPrefectureモデルの作成

まず、Prefectureモデルを作成してみます。
モデルの作成、と言うと、rails g modelしたくなりますが、
今回はActiveHash::Baseを継承したPrefectureモデルを自作します。
ActiveHash::Baseを継承したモデルを作成すると、ActiveRecordのメソッド(allとか)が使えるようになります。
他にどんなメソッドが使えるようになるか気になる人は、下の手順でPrefectureモデルを定義して、コンソールで、

$ Prefecture.methods

と入力してみてください。




以下実装です。

app/models/prefecture.rb

class Prefecture < ActiveHash::Base
  self.data = [
      {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: '沖縄県'}
  ]
end


app/models/address.rb

class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture
end

belongs_to_active_hashメソッドでアソシエーションも組めるってわけです。
extend ActiveHash::Associations::ActiveRecordExtensionsしないとprefectureモデルを使えません。
詳しく知りたい方はGemの中を覗いてみてください。
参考: active_hash/associations.rb at master · zilkey/active_hash · GitHub




これで準備完了です。
では、コンソールで試してみます。

$ rails c

# 以下はログです

[1] pry(main)> @address_1 = Address.create(prefecture_id: 1, city: '函館市')                                                                                       
   (0.2ms)  SAVEPOINT active_record_1
  Address Create (41.0ms)  INSERT INTO `addresses` (`prefecture_id`, `city`, `created_at`, `updated_at`) VALUES (1, '函館市', '2019-01-08 11:41:41', '2019-01-08 11:41:41')
   (0.3ms)  RELEASE SAVEPOINT active_record_1
=> #<Address:0x00007fc9f8cbf420
 id: 1,
 prefecture_id: 1,
 city: "函館市",
 created_at: Tue, 08 Jan 2019 11:41:41 UTC +00:00,
 updated_at: Tue, 08 Jan 2019 11:41:41 UTC +00:00>
[2] pry(main)> @address_2 = Address.create(prefecture_id: 13, city: '新宿区新宿')                                                                                        
   (0.3ms)  SAVEPOINT active_record_1
  Address Create (0.3ms)  INSERT INTO `addresses` (`prefecture_id`, `city`, `created_at`, `updated_at`) VALUES (13, '新宿区新宿', '2019-01-08 11:42:05', '2019-01-08 11:42:05')
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> #<Address:0x00007fc9f8a7cb40
 id: 2,
 prefecture_id: 13,
 city: "新宿区新宿",
 created_at: Tue, 08 Jan 2019 11:42:05 UTC +00:00,
 updated_at: Tue, 08 Jan 2019 11:42:05 UTC +00:00>
[3] pry(main)> @address_1.prefecture.name                                                                                                                                
=> "北海道"
[4] pry(main)> @address_2.prefecture.name                                                                                                                                
=> "東京都"

って感じで、prefecuture_idにPrefectureモデルに定義したハッシュのidを渡して、@address.prefecture.nameで名前を取れちゃいます。
もちろんPrefecture.find でハッシュを取ってこれます。

都道府県を選択するセレクトボックスを生成したい時は、

# hamlで書いてます

= f.collection_select :prefecture_id, Prefecture.all, :id, :name

って感じで、
f:id:kossy-web-engineer:20190307221245p:plain
こんな感じで設定できます。

検索機能も、詳細はこちらの記事に譲るとして、
Rails初心者が「form_with」を使って検索機能を実装してみた。 - Qiita
[Ruby on Rails 5] form_with でシンプルなサーチフォームを作成する | deadwood


f:id:kossy-web-engineer:20190512134125p:plain
こんな感じのチェックボックス式の検索フォームがあったとして、

view

<%= f.collection_check_boxes :prefecture_id, Prefecture.all, :id, :name %>

controller

@results = Address.where('prefecture_id IN(?)', params[:prefecture_id])

IN検索機能を使って、受け取ったprefecture_idを持つAddressテーブルのレコードを取得しています。



蛇足ですが、delegateを使えば@address.nameでも県名が取れたりしちゃいます。
Active Support コア拡張機能 - Rails ガイド

app/models/address.rb

class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture
  delegate :name, to: :prefecture
end

ログ

@address = Address.create(prefecture_id: 1, city: '猿払村') 

 @address.name                                                                                                                                             
=> "北海道"


あとは、app/models/address.rbに、

def full_address
  "#{name}#{city}"      #delegateしているのでnameだけで都道府県名が取れる
end

メソッドを定義すると、
@address.full_addressで、
都道府県名と市町村を連名で表示するような実装もできます。




active_hashさん、Prefectureモデルに静的データを持たせるよりも楽でいいですね。




ちなみに、active_hashで静的データを持つことの是非についてはこちらが詳しかったです。
blog.sesere.net




参考にさせていただいたサイト
【rails 5.1】DBに依存しない静的なデータはわざわざテーブルを作らず、belongs_toやhas_manyも使えるactive_hashを使うと良い - 考えすぎてしまう人のブログ
GitHub - zilkey/active_hash: A readonly ActiveRecord-esque base class that lets you use a hash, a Yaml file or a custom file as the datasource
https://blog.otsukasatoshi.com/entry/2017/04/20/003756
Active Support コア拡張機能 - Rails ガイド