Reputation: 1356
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
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
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