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
endclass Booking < ActiveRecord::Base
has_many :bills
endThe amount of the Bill comes from the Booking’s amount:
class Bill < ActiveRecord::Base
belongs_to :booking
delegate :amount, to: :booking
endNow 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
endclass Subscription < ActiveRecord::Base
has_many :bills
endAnd 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
endThen, 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
endNow, 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
endThe 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
endclass Booking < ActiveRecord::Base
has_many :bills, as: :billable
endclass Subscription < ActiveRecord::Base
has_many :bills, as: :billable
endMaybe 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: