Refactoring: Refactorings/Introduce Named Parameter
Parameters in a method call cannot be easily remembered or deduced from the name of the method called. Convert the parameter list into a hash of key => value pairs.
Mechanics
- Choose the parameters you want to name. If you aren't naming all of the parameters, move the ones you want to name to the end of the parameter list. (That way, calling code doesn't need to wrap named parameters in {}'s)
- Test
- Replace parameters in the calling code with name/value pairs
- Replace parameters with a Hash object in the receiving method. Modify the receiving method to use the new Hash
- Test
Example: All Parameters
Before
class SearchCriteria
attr_reader :author_id, :publisher_id, :isbn
def initialize(author_id, publisher_id, isbn)
@author_id = author_id
@publisher_id = publisher_id
@isbn = isbn
end
end
After
Not the best example; Introduce Class Annotation is preferred in this case.
class SearchCriteria
attr_reader :author_id, :publisher_id, :isbn
def initialize(params)
@author_id = params[:author_id]
@publisher_id = params[:publisher_id]
@isbn = params[:isbn]
end
end
Example: Some Optional Parameters
Before
class Books
def self.find(selector, conditions="", *joins)
sql = ["SELECT * FROM books"]
joins.each do |join_table|
sql << "LEFT OUTER JOIN #{join_table} ON"
sql << "books.#{join_table.to_s.chap}_id"
sql << " = #{join_table}.id"
end
sql << "WHERE #{conditions}" unless conditions.empty?
sql << "LIMIT 1" if selector == :first
connection.find(sql.join(" "))
end
end
- Usage
Books.find(:all)
Books.find(:all, "title like '%Voodoo Economics'")
Books.find(:first, "authors.name = 'Jenny James'", :authors)
After
Fluency can still be improved by Introduce Assertion to validate the keys.
class Books
def self.find(selector, hash={})
hash[:joins] ||= []
hash[:conditions] ||= ""
sql = ["SELECT * FROM books"]
hash[:joins].each do |join_table|
sql << "LEFT OUTER JOIN #{join_table} ON"
sql << "books.#{join_table.to_s.chap}_id"
sql << " = #{join_table}.id"
end
sql << "WHERE #{hash[:conditions]}" unless hash[:conditions].empty?
sql << "LIMIT 1" if selector == :first
connection.find(sql.join(" "))
end
end
- Usage
Books.find(:all)
Books.find(:all, :conditions => "title like '%Voodoo Economics'")
Books.find(:first, :conditions => "authors.name = 'Jenny James'", :joins => [:authors])
After (with assertions)
module AssertValidKeys
def assert_valid_keys(*valid_keys)
unknown_keys = keys - [valid_keys].flatten
if unknown_keys.any?
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}")
end
end
end
Hash.send(:include, AssertValidKeys)
class Books
def self.find(selector, hash={})
hash.assert_valid_keys :conditions, :joins
hash[:joins] ||= []
hash[:conditions] ||= ""
sql = ["SELECT * FROM books"]
hash[:joins].each do |join_table|
sql << "LEFT OUTER JOIN #{join_table} ON"
sql << "books.#{join_table.to_s.chap}_id"
sql << " = #{join_table}.id"
end
sql << "WHERE #{hash[:conditions]}" unless hash[:conditions].empty?
sql << "LIMIT 1" if selector == :first
connection.find(sql.join(" "))
end
end