Aliasing Methods

Problem

You (or your users) frequently misremember the name of a method. To reduce the confusion, you want to make the same method accessible under multiple names.

Alternatively, you're about to redefine a method and you'd like to keep the old version available.

Solution

You can create alias methods manually, but in most cases, you should let the alias command do it for you. In this example, I define an InventoryItem class that includes a price method to calculate the price of an item in quantity. Since it's likely that someone might misremember the name of the price method as cost, I'll create an alias:

	class InventoryItem
	 attr_accessor :name, :unit_price

	 def initialize(name, unit_price)
	 @name, @unit_price = name, unit_price
	 end

	 def price(quantity=1)
	 @unit_price * quantity
	 end

	 #Make InventoryItem#cost an alias for InventoryItem#price
	 alias :cost :price
	
	 #The attr_accessor decorator created two methods called "unit_price" and
	 #"unit_price=". I'll create 
aliases for those methods as well.
	 alias :unit_cost :unit_price
	 alias :unit_cost= :unit_price=
	end

	bacon = InventoryItem.new("Chunky Bacon", 3.95)
	bacon.price(100) # => 395.0
	bacon.cost(100) # => 395.0

	bacon.unit_price # => 3.95
	bacon.unit_cost # => 3.95
	bacon.unit_cost = 3.99
	bacon.cost(100) # => 399.0

 

Discussion

It's difficult to pick the perfect name for a method: you must find the word or short phrase that best conveys an operation on a data structure, possibly an abstract operation that has different "meanings" depending on context.

Sometimes there will be no good name for a method and you'll just have to pick one; sometimes there will be too many good names for a method and you'll just have to pick one. In either case, your users may have difficulty remembering the "right" name of the method. You can help them out by creating aliases.

Ruby itself uses aliases in its standard library: for instance, for the method of Array that returns the number of items in the array. The terminology used in area varies widely. Some languages use length or len to find the length of a list, and some use size.[3]

[3] Java uses both: length is a member of a Java array, and size is a method that returns the size of a collection.

Ruby compromises by calling its method Array#length, but also creating an alias called Array#size.[4] You can use either Array#length or Array#size because they do the same thing based on the same code. If you come to Ruby from Python, you can make yourself a little more comfortable by creating yet another alias for length:

[4] Throughout this book, we use Array#size instead of Array#length. We do this mainly because it makes the lines of code a little shorter and easier to fit on the page. This is probably not a concern for you, so use whichever one you're comfortable with.

	class Array
	 alias :len :length
	end

	[1, 2, 3, 4].len # => 4 

The alias command doesn't make a single method respond to two names, or create a shell method that delegates to the "real" method. It makes an entirely separate copy of the old method under the new name. If you then modify the original method, the alias will not be affected.

This may seem wasteful, but it's frequently useful to Ruby programmers, who love to redefine methods that aren't working the way they'd like. When you redefine a method, it's good practice to first alias the old method to a different name, usually the original name with an _old suffix. This way, the old functionality isn't lost.

This code (very unwisely) redefines Array#length, creating a copy of the original method with an alias:

	class Array
	 alias :length_old :length
	 def length
	 return length_old / 2
	 end
	end

Note that the alias Array#size still works as it did before:

	array = [1, 2, 3, 4]
	array.length # => 2
	array.size # => 4
	array.length_old # => 4

Since the old implementation is still available, it can be aliased back to its original name once the overridden implementation is no longer needed.

	class Array
	 alias :length :length_old
	end

	array.length # => 4

If you find this behavior confusing, your best alternative is to avoid alias altogether. Instead, define a method with the new name that simply delegates to the "real" method. Here I'll modify the InventoryItem class so that cost delegates to price, rather than having alias create a copy of price and calling the copy cost.

	class InventoryItem
	 def cost(*args)
	 price(*args)
	 end
	end

If I then decide to modify price to tack on sales tax, cost will not have to be modified or realiased.

	bacon.cost(100) # => 399.0

	require 'bigdecimal'
	require 'bigdecimal/util'
	class InventoryItem
	 def price(quantity=1, sales_tax=BigDecimal.new("0.0725"))
	 base_price = (unit_price * quantity).to_d
	 price = (base_price + (base_price * sales_tax).round(2)).to_f
	 end
	end

	bacon.price(100) # => 427.93
	bacon.cost(100) # => 427.93

We don't even need to change the signature of the cost method to match that of price, since we used the *args construction to accept and delegate any arguments at all:

	bacon.cost(100, BigDecimal.new("0.05")) # => 418.95

 

See Also

  • Recipe 2.9, "Converting Between Degrees and Radians"
  • Recipe 4.7, "Making Sure a Sorted Array Stays Sorted"
  • Recipe 17.14, "Running Multiple Analysis Tools at Once"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net