I've recently tried to start writing my methods in such a way that a user can pass in just about anything they want and get the expected results. I'll go through the normal iterations, and at the end we'll have a method that works how just about anyone could expect.

Consider a project where we need to add tags to an asset model. These models have a join table in between them. Here is our schema:

create_table "asset_join_tags", :force => true do |t|
  t.integer  "asset_id"
  t.integer  "tag_id"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

create_table "assets", :force => true do |t|
  t.string   "name"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

create_table "tags", :force => true do |t|
  t.string   "name"
  t.datetime "created_at", :null => false
  t.datetime "updated_at", :null => false
end

So we need an method on the asset model for people to add tags, and not have to know the underlying structure. Lets create that.

class Asset < ActiveRecord::Base
  has_many :asset_join_tags
  has_many :tags, :through => :asset_join_tags

  def add_tag tag
    asset_join_tags.create(:tag_id => tag.id)
  end
end

Excellent, so now from the console we can call @asset.add_tag(Tag.first) and it will tag our asset with the first tag. But wait a minute. What if we're in a controller and get a tag as a param to add? We'd have to load the model first and pass it to this method. Thats no good at all. Lets fix it.

def add_tag tag
  tag = tag.id if tag.is_a?(Tag)
  asset_join_tags.create(:tag_id => tag)
end

Excellent, now our method will accept an id or an actual tag and you get expected results either way. But what if the form submits an array of tag ids? Lets change our method to handle this behavior.

class Asset < ActiveRecord::Base
  has_many :asset_join_tags
  has_many :tags, :through => :asset_join_tags

  def add_tags tags
    tags = [tags] unless tags.is_a?(Array)
    tags.each do |tag|
      tag = tag.id if tag.is_a?(Tag)
      asset_join_tags.create(:tag_id => tag)
    end
  end
  alias :add_tag :add_tags
end

Perfect, now we have one method that can accept 4 different combinations of things and produce the same results. a tag id, a tag, an array of tags, or an array of tag ids.

Consider writing your methods this way so we can follow the principle of least surprise.

Adam has worked with Isotope 11 for 4 years and has been a professional software developer for over 12 years. He has been lead developer on multiple Fortune 500 projects. He is the author of "Beginning Rails 4" which was published by Apress in September 2013.