简体   繁体   中英

Can you override an aliased method in Ruby?

In Ruby, when a method is aliased, the alias points to the body of the original method. So even if you redefine the original method, the alias will continue to use the original definition.

class Foo
  def bar
    "bar"
  end  
  alias :saloon :bar
end

class Foo
  def bar
    "BAR"
  end
end

puts Foo.new.saloon

will return 'bar' and not 'BAR'. Is there any way to get saloon to use the new definition of bar?

EDIT: I should have been more clear. The example was just an illustration of the issue - it's not the actual problem I need to solve. The issue is more complex when you have chained aliases, for example, in rails' core. Eg perform_action is aliased by benchmarking module, and then also by flash module. So now a call to perform_action is actually calling perform_action_with_flash which does it's thing, then effectively calls perform_action_with_benchmarking which then calls the original perform_action. If I want to override perform_action_with_benchmarking (even though I agree it's a bad idea - please let's not get into a discussion of that as it's besides the point), I can't because it has been aliased, and as far as I can tell the alias is pointing to what is essentially a copy of the original perform_action_with_benchmarking, so even if I redefine it, there's no effect.

Just re-establish the alias:

class Foo
  def bar
    "bar"
  end  
  alias :saloon :bar
end

class Foo
  def bar
    "BAR"
  end
  alias :saloon :bar
end

puts Foo.new.saloon # => "BAR"
class Foo
  def bar
    "bar"
  end
  def saloon
    bar
  end
end

This is not an alias at all, but it works as you want.

Here is another answer, but you have to do some additional steps: collect the aliases before overriding, and realias after:

class Class
  def get_aliases method_name
    original_proc = instance_method method_name
    aliases = []
    instance_methods.each do |meth|
      # if the methods have different names but they're the same, they're aliased
      if meth != method_name.to_s && original_proc == instance_method(meth)
        aliases << meth
      end
    end
    aliases
  end
end

class Foo
  def bar
    "bar"
  end  
  alias :saloon :bar
end

class Foo
  aliases = get_aliases :bar
  def bar
    "BAR"
  end
  aliases.each { |a| alias_method a, :bar }
end

puts Foo.new.saloon  #=> BAR

BTW, if anyone can strip off one of that steps, may I know it! :)

Yes and no. Either coreyward or Sony Santos's solutions work fine. What you need to know is why your coded didn't work the way you though.

alias makes a new name for the function as is appears when the method is invoked. This is not a pointer, but a new way of referring to something. It allows us to do something like this:

class Foo
  def bar
    "bar"
  end  
  alias :speakeasy :bar
end

class Foo
  def bar(secret_code = false)
    return speakeasy if secret_code == "A friend of Al"
    "Closed because of prohibition!"
  end
end

puts Foo.new.bar #=> "Closed because of prohibition!"
puts Foo.new.bar "A friend of Al" #=> "bar"

The old bar still exists, it just a little harder to access now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM