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: