This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the object-oriented-programming category.
Last Updated: 2024-11-02
In this particular online store, what was known as a "product" could be either
be an instance of IndividualProduct
or of BundleProduct
- both inherited
from the same base Product
.
With that in mind, I had the following front-end code:
<td class="line-item-name">
<%= link_to product.name, admin_subject_path(product.subject) %>
</td>
The problem here was that only instances of IndividualProduct
had a subject
method. Instances of BundleProduct
did not, these being related to many products
and therefore only permitting of plural subjects
. This caused a fatal error
since these objects did not respond to the subject
message.
The fix was to branch the logic.
<% if product.bundle? %>
<%= link_to product.name, admin_notes_pack_path(product.notes_pack) %>
<% else %>
<%= link_to product.name, admin_subject_path(product.subject) %>
<% end%>
--
Another time: I added a JSON endpoint that translates the price on page for a given product to the discounted price, based on the logged in user's data. Something like:
def show
product = Product.find(params[:id])
render json: {
discounted_price: CalculateDiscount.for(product)
}
end
What I realized too late was that the product ID passed could trigger either an
IndividualProduct
or a BundleProduct
to instantiate (due to single table
inheritance behind the scenes), and a method called
within the CalculateDiscount
class was only available on BundleProduct
instances. Together this caused the endpoint to blow up sporadically.
When working with class hierarchies that diverge in method availabilities, ask yourself whenever you call any particular method whether it is available on all possible subclasses. If the methods aren't, then do some branching or create safe no-ops etc.
Relatedly, test all possible subclasses in your unit/manual tests to ensure there are no bugs with certain types.