Mixins Don't Replace Inheritance October 4th, 2007

Our application lets you search for businesses based on criteria including name and phone number. Each search could follow a different flow. If a name search returns nothing, check for typos in the name. On a phone search, give up.

This calls for controller inheritance. From a SearchController class, create NameSearchController, PhoneSearchController, etc.

However, Ruby is flexible you can modify an existing class at any point in your application. They can be "reopened." They are prone to abuse.

So instead, we have one giant Search controller. The extracted functionality was moved into modules, which were dynamically added to the main class, or "mixed-in."

Here's a simplified example.

# In name_search.rb
class SearchController < ApplicationController
  module NameSearch
    module Actions
       #name search Specific actions
    end
  end
end
# In search_controller.rb
require File.dirname(__FILE__) + '/search_controller/name_search'
class SearchController < ApplicationController
  include NameSearch::Actions, NameSearch::Reflection
  # common search actions
end

What could possibly go wrong?

You live a dangerous life when you're too clever for inheritance. Watch your method names. Every Ruby developer has accidentally defined a method twice, started editing the first definition, and pulled out their hair as their code changes mysteriously have no effect. Imagine the danger splitting things across files.

Now ask: "How do I know what search I'm performing?" After all, in your layout you'll need to know which navigation tab to highlight.

In the mixin setup:

module Reflection
  def name_search?
    NameSearch::Actions.action_methods.include? params[:action]
  end
end

With a subclass:

def name_search?
   self.kind_of? NameSearchController
end

helper_method :name_search?

Mixins are perfect for multiple inheritance. You just don't need that here.