静的データやマスターデータの為だけにtableを持つのを止めよう。 active_hash が便利そうだ

# active_hash
[zilkey/active_hash](https://github.com/zilkey/active_hash)

HashデータをActiveRecordのように扱う事の出来るgem。
少し使った感じはかなり便利そう。

Qiitaにactive_hashだけの記事が無かったので書いてみる。


## 用途
例えば、カテゴリデータや都道府県など、プルダウンに使うような静的データは、色々な持ち方ができます。

#### 単純に定数で扱ってもいいですし、

```rb
HOGES = {
  1 => 'aaa',
  2 => 'bbb',
  3 => 'ccc',
  4 => 'ddd'
}.with_indifferent_access
```

#### enumerize も便利です。

```
enumerize :hoge, in: {aaa: 1, bbb: 2, ccc: 3, ddd: 4}
```

[brainspec/enumerize](https://github.com/brainspec/enumerize)
[Rails - Enumerizeを数値型カラムで使う - Qiita](http://qiita.com/umanoda/items/daced48bd5f69caa4ecf)


#### DB
ただ、DBに`hoges table`としてデータでもつ場合も多いかと。
Relationを張れるので、他tableとの連動なども扱いやすいですしね。

#### active_hash
active_hashの場合、hashデータとして定義したデータを、擬似的にActiveRecordのように扱う事ができます。

また個人的に使いやすいと思ったのが、

- ActiveRecord <=> ActiveHash へのrelationを張る事が出来る点
- Decorator層に受け渡して加工もしやすい

点です。

## サンプル
#### install
```
gem 'active_hash'
# or
gem install active_hash
```

#### app/model
```rb:app/models/country.rb
class Country < ActiveHash::Base
  self.data = [
    {:id => 1, :name => "US"},
    {:id => 2, :name => "Canada"}
  ]
end
```

app/model内にhashで定義を書きます。
これは下記のようにも書けます

```rb:app/model/country.rb
class Country < ActiveHash::Base
  field :name
  add :id => 1, :name => "US"
  add :id => 2, :name => "Canada"
end
```
また、yamlにも定義できます

```rb:app/model/country.rb
class Country < ActiveYaml::Base
  set_root_path "/u/data"
  set_filename "sample"
end

# array style
- id: 1
  name: US
- id: 2
  name: Canada
- id: 3
  name: Mexico

# hash style
us:
  id: 1
  name: US
canada:
  id: 2
  name: Canada
mexico:
  id: 3
  name: Mexico
```

#### Class method
ActiveRecordでおなじみのmethodが色々使えるようになっています。

```rb
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
```

#### Instance method
```rb
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からのrelation
参考: [zilkey/active_hash#referencing-activehash-objects-from-activerecord-associations](https://github.com/zilkey/active_hash#referencing-activehash-objects-from-activerecord-associations)

```rb:app/model/country.rb
class Country < ActiveHash::Base
  include ActiveHash::Associations
  field :name
  add :id => 1, :name => "US"
  add :id => 2, :name => "Canada"

  has_many :persons
end

class Person < ActiveRecord::Base
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to_active_hash :country
end
```

`belongs_to_active_hash` が用意されているのでこれを使います。
（ActiveRecordのversionによって、記述方法が若干異なるようです。

```rb
person = Person.new
person.location = Country.first
person.save
person.location # => Country.first
```


## decorator層で扱う
元々このgemを使おうと思った動機が、プルダウンやcheckbox、radio butonなど、静的データをformで扱う場合に、少しずつデータの形を整えたいからでした。
（form_forとsimple_formとかでも

見た目の為にデータを整えるには、decorator層で行いたいですが、定数とかだとやり難いし、他のmodelが[drapergem/draper](https://github.com/drapergem/draper)を使っているので、合わせたいな〜と。

```rb
CountryDecorator.decorate_collection(Country.all)
```

これで、Decorator層で扱えます。
が、（active_hashだからなのか、わからないですが）少しはまったところもありました。

#### .allの戻り値はArray
通常のmodelは.allすると戻り値は、Relationですが、active_hashの場合はArrayみたいです。

```rb
[1] pry(main)> Hoge.all.class
=> Hoge::ActiveRecord_Relation
```

```rb
[3] pry(main)> Country.all.class
=> Array
```

Draperのgithubを見ると、
>Note: In Rails 3, the .all method returns an array and not a query. Thus you cannot use the technique of Article.all.decorate in Rails 3. In Rails 4, .all returns a query so this techique would work fine.

と、Rails 3とか、Arrayが帰ってきた場合は、`.all.decorate`使えないよ、って言われるので、素直に別のやり方をしましょう。

#### Collectionデータを扱う
Collectionデータを扱う場合、

```rb
@articles = ArticleDecorator.decorate_collection(Article.all)
```
と

```rb
# app/decorators/articles_decorator.rb
class ArticlesDecorator < Draper::CollectionDecorator
  def page_number
    42
  end
end

# elsewhere...
@articles = ArticlesDecorator.new(Article.all)
# or, equivalently
@articles = ArticlesDecorator.decorate(Article.all)
```

の二つのやり方があるようなのですが、自分の環境では、前者しかうまく動きませんでした。
しかも、前者のやり方をすると、ArticlesDecoratorのinstanceができます。（sがついて複数形のほう）

```
[11] pry(main)> 
ArticleDecorator.decorate_collection(Article.all).class.name
=> "ArticlesDecorator
```

ArticleDecoratorでdecorate_collectionすると、ArticlesDecoratorのmethodが呼ばれる。
こういうものなのかな。。。？
あまり調べきれてません。。。orz


### 参考
[zilkey/active_hash](https://github.com/zilkey/active_hash)
[【Rails】静的データのモデル化にactive_hash gemが便利・・！ - kotatu.org](http://kotatu.org/blog/2014/10/09/active-hash-gem/)
[ActiveHashを使ってRailsで区分値を扱う方法 - TIM Labs](http://labs.timedia.co.jp/2014/01/activehashrails.html)