Railsで開発する中で、データベースに保存するほど重要ではなく、またそれほど変更/増減する予定もないデータってよくありますよね。
そのようなデータをあたかもデータベースへ保存しているかのように扱わせてくれるのが「active_hash」になります。
今回はそんな「active_hash」の役割や導入方法、そして主な使い方について解説させていただきますね。
非常に便利で且つ使い方もとてもシンプルで簡単ですので、機会があれば積極的に使っていきましょう。
フリーランス案件を探すならこのエージェント!
転職を考えているならこのエージェント!
active_hashの役割/特徴
上述した通り、active_hashの主な役割は、データベースに保存するほどでもないデータをあたかもデータベースに保存しているかのように扱わせてくれることにあります。
ではどんな時にこのactive_hashを導入すればよいのでしょうか?
筆者は主に以下の3つの条件を満たした場合にはactive_hashを導入してよいのではないかと考えています。
- 1. データベースに保存しておくほど重要なデータではない
- 2. 性質上、それほど頻繁に変更/増減されるデータではない
- 3. ActiveRecordと同じ感覚でデータを扱いたい
このような条件を満たした場合にはactive_hashを導入してしまいましょう。
active_hashを使う際には、扱いたいデータをハッシュとしてモデルに定義することで、そのデータがあたかもデータベース内に保存されているものであるかのように扱うことができるようになります。
イメージとしては、一つのハッシュがテーブル内の一つのレコードとして扱われるものとして認識していただいて大丈夫です。
(※この時点ではまだあまりイメージが湧かないと思いますので、詳しくは後述させていただきますね。)
active_hashの導入方法
active_hashの導入方法は非常に簡単です。
active_hashもGemですので、Gemfileに以下を記載して「bundle install」を実行しましょう。
gem 'active_hash'
active_hashの使い方
active_hashを導入できたところで、続いては実例を見ながらその使い方を解説していきますね。
まずは扱いたいデータをモデルに定義していきましょう。今回は都道府県のデータ(idとname属性を有するものとします)を定義していこうと思います。
(※都道府県であれば、その名前や数は変更/増減するものではないため、active_hashを使用するのに適していますよね)
なお、ここで注意点があるのですが、通常、モデルを作成する際にはrails g model Prefecture name:string
のようなコマンドを実行すると思うのですが、active_hashを用いる場合には手動でファイルを作成するようにしてください。
今回であれば、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
これで何となく上述した「一つのハッシュがテーブル内の一つのレコードとして扱われる」のイメージが掴めたかと思います。
こちらの例で言えば、{id: 1, name: '北海道'}
が一つのレコード(id, nameがカラム名)として扱われるということですね。
こうすることで、Prefectureモデルに対してActiveRecordの各種メソッドが使用できるようになります。
試しに実行してみると以下のような実行結果が返ってくるかと思います。
[1] pry(main)> Prefecture.find(1)
=> Prefecture:0x007f464b2fad66 @attributes={:id=>1, :name=>"北海道"}>
[2] pry(main)> Prefecture.last
=> Prefecture:0x007f514b2fad37 @attributes={:id=>47, :name=>"沖縄県"}>
使い方も非常にシンプルで、active_hashを導入したということを意識することなくデータを扱うことが可能ですよね。
active_hashでアソシエーションを組んでみよう
通常のモデル同様、アソシエーションを組むのも非常に簡単です。
今回はCity(市区町村)モデルを定義し、Prefectureモデルと紐づいているものとします。
Prefectureモデルはhas_many
を使用するために以下のように記載しておきましょう。
class Prefecture < ActiveHash::Base
include ActiveHash::Associations
has_many :cities
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: '栃木県'} ...
]
end
Cityモデルは通常通り、rails g model City name:string prefecture_id:integer
を実行してください。
その後、作成されたapp/model/city.rb
を以下のように記載しましょう。
class City < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to_active_hash :prefecture
end
ここでは以下の2点を追加しているのですが、特にbelongs_to
は少し書き方が異なっていますのでご注意くださいね。
- extend ActiveHash::Associations::ActiveRecordExtensions
- belongs_to_active_hash :prefecture
その後ターミナルにて確認すると無事アソシエーションが確立されているのが確認できるかと思います。
[1] pry(main)> city_1 = City.create(prefecture_id: 1, name: '札幌市')
id: 1,
prefecture_id: 1,
name: '札幌市',
created_at: Tue, 28 Jan 2020 11:41:41 UTC +00:00,
updated_at: Tue, 28 Jan 2019 11:41:41 UTC +00:00
[2] pry(main)> city_2 = City.create(prefecture_id: 27, name: '大阪市')
id: 2,
prefecture_id: 27,
name: '大阪市',
created_at: Tue, 28 Jan 2020 11:42:41 UTC +00:00,
updated_at: Tue,28 Jan 2020 11:42:41 UTC +00:00
[3] pry(main)> city_1.prefecture.name
=> "北海道"
[4] pry(main)> city_2.prefecture.name
=> "大阪府"
他にも色々なメソッドが使えます
上記で扱ったもの以外にも使用できるメソッドは多くありますので、ここではその一部をご紹介させていただきますね。
メソッド | 戻り値 |
---|---|
City.all | 全てのCityオブジェクト |
City.count | Cityオブジェクトの数 |
City.first | 最初のCityオブジェクト |
City.last | 最後のCityオブジェクト |
City.find(1) | idが「1」である最初のCityオブジェクト |
City.find_by_id(1) | idが「1」である最初のCityオブジェクト |
City.find_by_name(“大阪市”) | nameが「大阪市」である最初のCityオブジェクト |
City.find_all_by_name(“大阪市”) | nameが「大阪市」である全てのCityオブジェクト |
さらに詳しく知りたい方は以下のGithubのREADMEを参照してみてくださいね。
https://github.com/zilkey/active_hash
active_hashを使用する際の注意点
このように、active_hashは非常に便利ではあるのですが、1点だけ注意事項があります。
それは、active_hashを使用したモデルがあると、1対多は問題ないのですが、多対多のアソシエーションは組むことができない、ということです。
そのため、active_hashを使用したモデルで多対多を実現しようと思うと、以下の例のようにメソッドを追加してあげる必要がありますので、この点はくれぐれもご注意くださいね。
前提条件
- 前提条件① Human(人材)モデルとSkill(スキル)モデルを定義
- 前提条件② 人材には複数のスキルが登録されており、スキルも複数の人材を有している(=HumanとSkillは多対多の関係)
- 前提条件③ 中間テーブルとしてHumanSkillモデルを定義
実装例
class Human < ApplicationRecord
has_many :human_skills
def skills
human_skills.map(&:skill)
end
end
class HumanSkill < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :human
belongs_to_active_hash :skill
end
class Skill < ActiveHash::Base
include ActiveHash::Associations
has_many :human_skills
def humans
human_skills.map(&:human)
end
end
上記では、直接humans
やskills
を呼び出す代わりに、一度中間テーブル(ここではhuman_skills
)のレコードを取得し、map
メソッドを使って各レコードごとのhuman
或いはskill
を配列に格納する形に実装しています。
これ以外にも実現方法はいくつかあるとは思いますが、まずは「active_hashで多対多を組みたい」場合の選択肢の一つとして知っておいていただければと思います。
最後に
さて、ここまでactive_hashの役割や導入方法、そしてその使い方についてご紹介してきましたがいかがでしたか?
便利なGemではありながらもその使い方は非常にシンプルで分かりやすかったと思います。
無駄なテーブルを増やしていかないためにも、導入に適したデータがある場合には積極的に使っていきましょうね!
ただし、多対多のアソシエーションを組む場合には少し工夫が必要になりますので、くれぐれもご注意くださいね。このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。
最後まで読んで頂きありがとうございました。