Ben Downey
Ben Downey

Reputation: 2665

How to make a shared table flexible enough to service two models

I'm working on an app that will help me track me finances. There's a model that tracks my investment accounts (e.g. 401k, etc.) and the monthly balance associated with each account. The investment_accounts model has_many balances and the balances belong_to investment_accounts.

I'd like the app to now track debt, specifically my mortgage. But I'm struggling to figure out how to best model this? I'll need to have a mortgage model that has different information that than the investment_accounts (specifically interest rate). But the mortgage model should leverage the balances table, since tracking mortgage balances is so similar to tracking investment account balances. But I'm at a loss for how to revise the balances table to make it sufficiently flexible to service both the mortgage table and the investment_account table.

I considered adding a 'type' attribute on the balances model and creating named scopes like this:

scope :investment_accounts, where(type: 'investment')
scope :mortgage, where(type: 'mortgate')

But then I realized this should lead to some weird looking ActiveRecord queries. For instances:

investment_accounts.balances.investment_accounts

and

mortgage.balances.mortgage

I take this as a code smell. Any suggestions on how to do this right?

Upvotes: 0

Views: 69

Answers (2)

Billy Chan
Billy Chan

Reputation: 24815

This is the case for polymorphic association.

class InvestmentAccount < ActiveRecord::Base
  has_many :balances, as: :balanceable
end

class Mortgage < ActiveRecord::Base
  has_many :balances, as: :balanceable
end

class Balance < ActiveRecord::Base
  belongs_to :balanceable, polymorphic: true
end

By this your Balance will have a column balanceable which storing the type such as "investment_account", "mortgage", and possible more.

# Usage
account = InvestmentAccount.last
account.balances

mortgage = Mortgage.last
mortgage.balances

balance = Balance.last
balance.balanceable # Will return object according to its balanceable

For more about polymorphic and its migration, check the guide: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

Upvotes: 1

pdobb
pdobb

Reputation: 18037

Balance could just have some optional fields that are only used as needed. Or you could use STI to make Balance a super class and then subclass for each type. So you might have a MortgageBalance and an InvestmentAccountBalance class whose parent is Balance. They'd share the same table, have a type field, and allow for the model to distinguish based on its type. But I'm not sure that's necessary. You may just be able to keep it loose and use duck typing. mortgage.balances would be Balances tailored for Mortgages and may not even need to know the difference. The relationship to the Mortgage object could create the difference. Make sense?

Upvotes: 1

Related Questions