As your domain model grows in complexity, you may want to factor out some common traits and behaviour into a MixIn. For the Java crowd out there, mixins are just a much simpler way to do AOP.

While adding instance methods via a MixIn is straightforward, adding constraints to your model requires to execute code in the scope of the class in which you include the module. It took me a while to get this one right so I thought I'd share:


module Discountable
  
  def self.included(base)
    base.class_eval do
      validates_inclusion_of :discount_type, 
                      :in => %w{ None Percentage Absolute }
      validates_numericality_of :discount_value
    end
  end

  def compute_discount(price)
     # ...
  end

  # ...
   
end

Now in your actual ActiveRecord class:


class OrderItem < ActiveRecord::Base

  # Associations
  belongs_to :order
  belongs_to :product

  # Validations
  validates_presence_of :quantity, :unit_price
  validates_numericality_of :quantity, :unit_price

  # Add discountable properties and constraints
  include Discountable

end

The same method can be used to declare associations or activate any kind of "acts as" behaviors.

I love this game.