active_hash
HashデータをActiveRecordのように扱う事の出来るgem。
少し使った感じはかなり便利そう。
Qiitaにactive_hashだけの記事が無かったので書いてみる。
用途
例えば、カテゴリデータや都道府県など、プルダウンに使うような静的データは、色々な持ち方ができます。
単純に定数で扱ってもいいですし、
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
Rails - Enumerizeを数値型カラムで使う - Qiita
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
class Country < ActiveHash::Base
self.data = [
{:id => 1, :name => "US"},
{:id => 2, :name => "Canada"}
]
end
app/model内にhashで定義を書きます。
これは下記のようにも書けます
class Country < ActiveHash::Base
field :name
add :id => 1, :name => "US"
add :id => 2, :name => "Canada"
end
また、yamlにも定義できます
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が色々使えるようになっています。
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
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
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によって、記述方法が若干異なるようです。
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を使っているので、合わせたいな〜と。
CountryDecorator.decorate_collection(Country.all)
これで、Decorator層で扱えます。
が、(active_hashだからなのか、わからないですが)少しはまったところもありました。
.allの戻り値はArray
通常のmodelは.allすると戻り値は、Relationですが、active_hashの場合はArrayみたいです。
[1] pry(main)> Hoge.all.class
=> Hoge::ActiveRecord_Relation
[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データを扱う場合、
@articles = ArticleDecorator.decorate_collection(Article.all)
と
# 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
【Rails】静的データのモデル化にactive_hash gemが便利・・! - kotatu.org
ActiveHashを使ってRailsで区分値を扱う方法 - TIM Labs