【Rails】active_hashの使い方を丁寧に解説します!

Railsで開発する中で、データベースに保存するほど重要ではなく、またそれほど変更/増減する予定もないデータってよくありますよね。

そのようなデータをあたかもデータベースへ保存しているかのように扱わせてくれるのが「active_hash」になります。

今回はそんな「active_hash」の役割や導入方法、そして主な使い方について解説させていただきますね。

非常に便利で且つ使い方もとてもシンプルで簡単ですので、機会があれば積極的に使っていきましょう。


フリーランス案件を探すならこのエージェント!

運営会社 レバテック株式会社 株式会社Branding Engineer 株式会社Hajimari
対応エリア 東京・神奈川・埼玉・千葉
大阪・兵庫・京都・愛知・福岡
東京・神奈川・埼玉・千葉
大阪・兵庫・京都・奈良・和歌山・滋賀
東京・神奈川・埼玉・千葉
茨城・大阪・兵庫・福岡
案件数 約28,000件 約3,300件 約4,200件
平均単価 68.2万円 72.2万円
最高月収 145万円 200万円 176万円
支払いサイト 15日(月末締め翌月15日支払い) 20日
手数料(中間マージン) 非公開 非公開
※実質20%
非公開
特徴高単価な案件が多い
福利厚生サービスが受けられる
・首都圏の案件が中心
給与保証制度で安定した収入を得られる
・税務処理のサポートあり
・マージン率は実質20%
週2/週3の案件が多い
在宅/リモート案件が多い
・スタートアップ企業やベンチャー企業の案件が多い
紹介企業例 ・株式会社Gunosy
・株式会社バンダイナムコホールディングス
・株式会社ユーザベース
・Sansan株式会社
・株式会社一休
・株式会社FiNC Technologies など
・クックパッド株式会社
・株式会社カオナビ
・株式会社Kaizen Platform など
説明記事
公式サイト


転職を考えているならこのエージェント!

運営会社 レバテック株式会社 株式会社Branding Engineer 株式会社リブセンス
対応エリア 東京・神奈川・埼玉・千葉
大阪・兵庫・京都・愛知・福岡
東京・神奈川・埼玉・千葉
大阪・兵庫・京都・奈良・和歌山・滋賀
東京・神奈川・埼玉・千葉
茨城・大阪・兵庫・福岡
案件数 約13,000件 約800件
※非公開求人が多い

※ドラフト制のため、対象外
年齢層 20代:◎
30代:〇
40代:〇
20代:〇
30代:〇
40代:△
20代:〇
30代:〇
40代:〇
特徴エンジニア・デザイナーを専門としている
・カウンセリングに時間をかける
年収アップ率60%の実績
IT/Web/ゲーム業界専門の転職エージェント
・業界経験者がキャリアコンサルタントを務める
・丁寧なカウンセリングで入社後の離職率1%以下を実現
ドラフト制による採用を叶える特殊なサービス
スキル・経験重視で評価してもらえる
・他エンジニアの入札結果を見ることで
自分の市場価値が分かる
紹介企業例 ・サイボウズ株式会社
・株式会社サイバーエージェント
・Chatwork株式会社 など
・DMM.com
・Money Forward
・SmartHR など
・メルカリ
・ZOZOテクノロジーズ
・freee など
説明記事
公式サイト


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.countCityオブジェクトの数
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

上記では、直接humansskillsを呼び出す代わりに、一度中間テーブル(ここではhuman_skills)のレコードを取得し、mapメソッドを使って各レコードごとのhuman或いはskillを配列に格納する形に実装しています。

これ以外にも実現方法はいくつかあるとは思いますが、まずは「active_hashで多対多を組みたい」場合の選択肢の一つとして知っておいていただければと思います。

最後に

さて、ここまでactive_hashの役割や導入方法、そしてその使い方についてご紹介してきましたがいかがでしたか?

便利なGemではありながらもその使い方は非常にシンプルで分かりやすかったと思います。

無駄なテーブルを増やしていかないためにも、導入に適したデータがある場合には積極的に使っていきましょうね!

ただし、多対多のアソシエーションを組む場合には少し工夫が必要になりますので、くれぐれもご注意くださいね。

このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。

最後まで読んで頂きありがとうございました。