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.
