Search code examples
ruby-on-railsrubymodelssingle-table-inheritance

Should I should subclass this Rails model?


I have a model called Coupon, which can either be set to have a money_off or percent_off attributes (it can only have one set at a time).

Also depending on whether the Coupon is money_off or percent_off changes which methods are used.

Im wondering if i should be using Single table inheritance to eseentially sub class Coupon and have a sub class that deals with percent off coupons and another dealing with money off coupons?

I would then like to know how a user would be able to select this from the view.


Solution

  • The best way is to determine which functionality you require for each class. If you only need a small amount of changes, then stick to a single class with an enum:

    #app/models/coupon.rb
    class Coupon < ActiveRecord::Base
       enum type: [:percent, :money]
    
       def value
          if type.percent?
             # ...
          elsif type.money?
             # ...
          end
       end
    end
    

    This will allow you to use the type in your instance methods, which shouldn't cause such a problem if you didn't have a lot of changes to make within the class.

    This would allow you to call:

    @coupon = Coupon.find x
    @coupon.value #-> returns value based on the type
    

    --

    The alternative (STI) would be more of a structured change, and would only work if you were referencing each class explicitly:

    #app/models/coupon.rb
    class Coupon < ActiveRecord::Base
    end
    
    #app/models/percent.rb
    class Percent < Coupon
       def amount
          # ...
       end
    end
    
    #app/models/money.rb
    class Money < Coupon
       def takeout
          # ...
       end
    end
    

    An important factor here is how you call these.

    For the above classes, you have to reference the subclassed classes on their own:

    @percentage_coupon = Percent.find x
    @money_coupon      = Money.find y
    

    This will obviously be more cumbersome, and may even cause problems with your routes & controllers etc.

    .... so it may be best going with the single class :)