active_hashまとめ




##active_hashってなに？
gemの一種。
ハッシュのデータを、ActiveRecordと同じ感覚で使えるようにしてくれる。

##どういうときにつかうの？
以下の条件2つにあてはまるとき。
◆これから扱うデータがDBにデータとして保存しておくほど重要ではない、かつ、基本的に変更されない。
◆ActiveRecordと同じ感覚でデータを操作したい。

##具体的に言うと？
例えば都道府県名一覧やカテゴリなど「基本的に変更されないデータ」があったとする。
そういったデータをDBに保存しておけば取り扱いは楽だけど、データの価値としてはDBに保持しておくほど重要ではない。
じゃあ ↓ の例のように定数でやるか？というとそれも微妙。
コード全体の見通しが悪くなるし、Activerecordを通して使えないので扱いづらくなってしまう。

```ruby
TODOHUKEN = [
 1: {name: :北海道, location: [12.323245, 102.3231231]},
 .
 .
 .
]
```


そういう悩みを解決するのに便利なのがactive_hash。
**ハッシュデータをモデルとして定義する**ことで、
**ハッシュをDBから引っ張ってきたデータと同じように扱うことができる**ようになる。
概要はここまで。次は実際の使い方を紹介。


##導入方法
Gemfileにactive_hashを記載して、`bundle install`
導入はこれでおしまい。カンタン。

```ruby:Gemfile

gem 'active_hash'
```

##モデル作成

次にモデルを作成する。
今回作成するモデルは2つ、AddressモデルとPrefectureモデル。
まずは`rails g model`でAddressモデルを作成。
続けて`db:create`と`db:migrate`を実行。

```ruby
$ 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
```
```ruby
$ rails db:create
$ rails db:migrate
```

次にPrefectureモデルを作成。
こちらは`rails g model`での作成ではなく
ActiveHash::Baseを継承したPrefectureモデルを **自作** する。
※ActiveHash::Baseを継承することで初めて、ActiveRecordのメソッド(allなど)が使えるようになる。


都道府県のデータはハッシュで配列に格納する。

```ruby: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
```


active_hashには**belongs_to_active_hashメソッド**が用意されているので、
address.rbに このメソッドを記述し、アソシエーションを定義する。

`extend ActiveHash::Associations::ActiveRecordExtensions`
`belongs_to_active_hash :prefecture`
この2行を追加。

なお、Prefectureモデルのファイルには、アソシエーションを明示する必要はない。

これで準備は完了。

```ruby:app/models/address.rb
class Address < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :prefecture
end
```

##コンソールで試してみる

```ruby:
$ rails c

[1] pry(main)> @address_1 = Address.create(prefecture_id: 1, city: '函館市')                                                                                       
 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: '新宿区新宿')                                                                                        
 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 でハッシュを取ることも可能。

##まとめ
テーブルの数を無駄に増やさないためにも、
扱うデータが最初の2つの条件にあてはまる場合は`active_hash`を活用していきましょう。


#※以下は補足情報

##使えるクラスメソッド

```ruby:
Country.all             # => returns all Country objects
Country.count           # => returns the length of the .data array
Country.first           # => returns the first country object
Country.last            # => returns the last country object
Country.find 1          # => returns the first country object with that id
Country.find [1,2]      # => returns all Country objects with ids in the array
Country.find :all       # => same as .all
Country.find :all, args # => the second argument is totally ignored, but allows it to play nicely with AR
Country.find_by_id 1    # => find the first object that matches the id

Country.find_by_name "foo"                    # => returns the first object matching that name
Country.find_all_by_name "foo"                # => returns an array of the objects with matching names
Country.find_by_id_and_name 1, "Germany"      # => returns the first object matching that id and name
Country.find_all_by_id_and_name 1, "Germany"  # => returns an array of objects matching that name and id

```


##使えるインスタンスメソッド
```ruby:
Country#id          # => returns the id or nil
Country#id=         # => sets the id attribute
Country#quoted_id   # => returns the numeric id
Country#to_param    # => returns the id as a string
Country#new_record? # => returns true if is not part of Country.all, false otherwise
Country#readonly?   # => true
Country#hash        # => the hash of the id (or the hash of nil)
Country#eql?        # => compares type and id, returns false if id is nil
```

##注意点
**検索**
ActiveRecordのincludesやjoinsを使ってテーブル間の検索をするのは(たぶん)無理なので、
おとなしくmigrationファイルでActiveRecordを使用したほうが良さそう。