Rails polymorphic associations
Working on a Rails project, I faced a problem similar to this: Given an ActiveRecordModel called Booking which can have multiple bills:
class Bill < ActiveRecord::Base
belongs_to :booking
end
class Booking < ActiveRecord::Base
has_many :bills
end
The amount of the Bill comes from the Booking’s amount:
class Bill < ActiveRecord::Base
belongs_to :booking
delegate :amount, to: :booking
end
Now we need a new model, Subscription, and a Subscription can also have a Bill so:
class Bill < ActiveRecord::Base
belongs_to :subscription
belongs_to :booking
delegate :amount, to: :booking
end
class Subscription < ActiveRecord::Base
has_many :bills
end
And here is the problem, it isn’t possible to delegate to both Booking and Subscription.
Ok, let’s stop thinking about Rails and try to solve this with OO and plain Ruby. In plain Ruby, the Bill class would probably look like this:
class Bill
def initializer(booking)
@booking = booking
end
def amount
@booking.amount
end
end
Then, when the Subscription entity enters the game, the only thing we need to take care of is the fact that Booking and Subscription have a common interface with one method amount. This can be written as follows:
class Bill
def initializer(billable)
@billable = billable
end
def amount
@billable.amount
end
end
Now, the Bill class doesn’t have to mind about Bookings or Subscriptions anymore.
Good, back to Rails. How do I model this in Rails? I would have to change the previous Rails code to something like this:
class Bill < ActiveRecord::Base
belongs_to :billable
end
The answer is polymorphic associations. This type of association allows Bill to belong_to an interface. The only thing we have to do is mark this relation as polymorphic, and the Booking and Subscription models as billable
class Bill < ActiveRecord::Base
belongs_to :billable, polymorphic: true
end
class Booking < ActiveRecord::Base
has_many :bills, as: :billable
end
class Subscription < ActiveRecord::Base
has_many :bills, as: :billable
end
Maybe in a future post I dig into how Rails maps these models to the database, but that’s it for today. Here I leave other posts about Rails polymorphic associations that I found interesting: