mph85
mph85

Reputation: 1356

When should I use an Alias Method? - Ruby

I've looked through and haven't seen an answer to:

What would you use an alias method?

class Vampire
 attr_reader :name, :thirsty

 alias_method :thirsty?, :thirsty
end

Is the only reason I would use one is to be able to use a question mark with whatever method I define? I believe you can't use question marks with instance variables.

Upvotes: 1

Views: 1689

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369428

There are two reasons why one would use Module#alias_method, one is current and valid, and the other is obsolete and was never actually necessary anyway.

The first reason is that you simply want to have two methods with different names that do the exact same thing. One reason for that could be that there are two equally widely-used terms for the same action, and you want to make it easier for people write code that is understandable to their community. (Some examples in the Ruby core library are the collection methods, which have names that are familiar to people coming from functional programming languages, such as map, reduce, people coming the Smalltalk family of programming languages, such as collect, inject, select, and standard English names, such as find_all.) Another possibility would be that you build a Fluent Interface and want it to read more fluently, like this:

play this
and that
and something_else

In this case, and could be an alias for play.

Another, related reason (let's call it reason 1.5) is that you want to implement some protocol and you already have a method that implements that protocol's semantics correctly, but it has the wrong name. Let's assume a hypothetical collections framework which has two methods map_seq and map_nonseq. The first one performs a map and guarantees the order of side-effects, whereas the second one does not guarantee the order of side-effects and may even perform the mapping operation asynchronously, concurrently, or in parallel. These two methods have actually different semantics, but if your data structure does not lend itself to parallelization, then you can simply implement map_seq and make map_nonseq an alias.

In this case, the driver is not so much that you want to provide two names for the same operation, but rather that you want to provide the same implementation for two names (if that sentence makes sense :-D ).

The second major reason why alias_method was used in the past, has to do with an important detail of its semantics: when you override or monkey-patch either of the two methods, this will only affect that name, but not the other. This was used in the past for wrapping the behavior of a method, like this:

class SomeClass
  def some_method
    "Hello World"
  end
end

This is a little boring. We want our method to SHOUT! But, we don't want to just copy and re-implement the method, we want to re-use its internal implementation without having to know how it is implemented internally. And we want to monkey-patch it, so that all clients of this method have the shouting behavior. The popular way to do this was something like this:

class SomeClass
  alias_method :__some_method_without_shouting :some_method

  def __some_method_with_shouting
    __some_method_without_shouting.upcase
  end

  alias_method :some_method :__some_method_with_shouting
end

In this example, we use alias_method to create a "backup" of the method we are monkey-patching, so that we can call it from within our monkey-patched version of the method. (Otherwise, the method would be gone.) This is actually the use case given in the documentation of alias_method.

This idiom was so popular and widely-used that some libraries even provided an implementation for it, e.g. ActiveSupport's Module#alias_method_chain.

Note, however that this idiom has some not-so-nice properties. One is that we are polluting the namespace with all those _with_ and _without_ methods. E.g. when you look at an object's methods, you will see all of those. The other problem is that people can still call the old method directly, but presumably we had a reason to patch it, because we don't want the old behavior. (Otherwise, we could have just made a method with a new name that calls the old one, e.g. shout.)

There has always been a better alternative that was not so widely-used:

class SomeClass
  some_method_without_shouting = instance_method(:some_method)

  define_method(:some_method) do
    some_method_without_shouting.bind(self).().upcase
  end
end

Here, we store the old method in a local variable and we use a block to define the new method (via Module#define_method). The local variable goes out of scope at the end of the class body, so it can never be accessed anywhere ever again. But blocks are closures, they close over their surrounding lexical environment, and thus the block passed to define_method (and only this block) still has access to the variable binding. That way, the old implementation is completely hidden and there is no namespace pollution.

However, as of Ruby 2.0, there is a much better solution for this method wrapping: Module#prepend. The beauty of prepend is that it is "just inheritance", and we can simply use super:

module Shouter
  def some_method
    super.upcase
  end
end

class SomeClass
  prepend Shouter
end

Module#prepend is the reason why Module#alias_method_chain was deprecated in ActiveSupport 5.0 and removed in 5.1, for example. All these contortions are simply no longer necessary.

So, to summarize: there were two main reasons why alias_method was used: literally making an alias, i.e. two names for the same operation, and creating a backup copy for method wrapping. The second is no longer valid and maybe arguably never was. Today, only the first reason is a valid reason to use alias_method.

Upvotes: 5

Jay Dorsey
Jay Dorsey

Reputation: 3662

I think this is from an earlier question I responded to, where I proposed using alias_method, so I have a little bit of extra context into this to explain it's use in that context.

In your code snippet, you have a bit of code that reads attr_reader :thirsty that is basically a getter for an instance variable of the same name (@thirsty)

def thirsty
  @thirsty
end

In the original code snippet, you had an assertion that was:

refute vampire.thirsty?

You also had code that simply returned true for thirsty? method, which failed your assertion.

There are at least two ways you could have modified your code so that the call to thirsty? worked and your assertion passed:

Create a method that calls the thirsty reader, or access the @thirsty instance variable itself:

def thirsty?
  thirsty # or @thirsty
end

The other way is to use alias_method, which is functionally equivalent to the above. It aliases thirsty? to thirsty which is an attr_reader which reads from the @thirsty instance variable

Reference to the other answer I gave

You might be better off not using an attr_reader at all, instead just doing as Sergio noted in his comment:

class Vampire
  def initialize(name)
    @name = name
    @thirsty = true
  end

  def thirsty?
    @thirsty
  end

  def drink
    @thirsty = false
  end
end

Upvotes: 3

Related Questions