Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to alias a class method within a module?

I am using Ruby v1.9.2 and the Ruby on Rails v3.2.2 gem. I had the following module

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end
  end
end

and I wanted to alias the class method my_method. So, I stated the following (not working) code:

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end

    # Note: the following code doesn't work (it raises "NameError: undefined
    # local variable or method `new_name' for #<Class:0x00000101412b00>").
    def self.alias_class_method(new_name, old_name)
      class << self
        alias_method new_name, old_name
      end
    end

    alias_class_method :my_new_method, :my_method
  end
end

In other words, I thought to extend the Module class someway in order to add an alias_class_method method available throughout MyModule. However, I would like to make it to work and to be available in all my Ruby on Rails application.

  1. Where I should put the file related to the Ruby core extension of the Module class? Maybe in the Ruby on Rails lib directory?
  2. How should I properly "extend" the Module class in the core extension file?
  3. Is it the right way to proceed? That is, for example, should I "extend" another class (Object, BasicObject, Kernel, ...) rather than Module? or, should I avoid implementing the mentioned core extension at all?

But, more important, is there a Ruby feature that makes what I am trying to accomplish so that I don't have to extend its classes?

like image 384
Backo Avatar asked Oct 16 '12 15:10

Backo


People also ask

What is alias method in ruby?

To alias a method or variable name in Ruby is to create a second name for the method or variable. Aliasing can be used either to provide more expressive options to the programmer using the class or to help override methods and change the behavior of the class or object.

Is a module a class Ruby?

A Module is a collection of methods, constants, and class variables. Modules are defined as a class, but with the module keyword not with class keyword.

What is prepend in Ruby?

With prepend, the module is added before the class in the ancestor chain. This means ruby will look at the module to see if an instance method is defined before checking if it is defined in the class. This is useful if you want to wrap some logic around your methods.


2 Answers

You could use define_singleton_method to wrap your old method under a new name, like so:

module MyModule
  def alias_class_method(new_name, old_name)
    define_singleton_method(new_name) { old_name }
  end
end

class MyClass
  def my_method
    puts "my method"
  end
end

MyClass.extend(MyModule)
MyClass.alias_class_method(:my_new_method, :my_method)
MyClass.my_new_method     # => "my method"

Answering your comment, you wouldn't have to extend every single class by hand. The define_singleton_method is implemented in the Object class. So you could simply extend the Object class, so every class should have the method available...

Object.extend(MyModule)

Put this in an initializer in your Rails app and you should be good to go...

like image 125
Vapire Avatar answered Sep 28 '22 02:09

Vapire


I found an answer on this website: http://engineering.lonelyplanet.com/2012/12/09/monitoring-our-applications-ruby-methods/

The solution is to use class_eval with a block. That enables using variables from the enclosing scope.

module Alias

  def trigger
    @trigger = true
  end

  def method_added(name)
    if @trigger
      @trigger = false
      with_x = "#{name}_with_x"
      without_x = "#{name}_without_x"
      define_method(with_x) do
        "#{send(without_x)} with x"
      end
      alias_method without_x, name
      alias_method name, with_x
    end
  end

  def singleton_method_added(name)
    if @trigger
      @trigger = false
      with_x = "#{name}_with_x"
      without_x = "#{name}_without_x"
      define_singleton_method(with_x) do
        "singleton #{send(without_x)} with x"
      end
      singleton_class.class_eval do
        alias_method without_x, name
        alias_method name, with_x
      end
    end
  end

end

class TestAlias

  extend Alias

  trigger
  def self.foo
    'foo'
  end

  trigger
  def bar
    'bar'
  end

end

TestAlias.foo # => 'singleton foo with x'
TestAlias.new.bar # => 'bar with x'

If you don't have singleton_class then you should probably upgrade your version of Ruby. If that's not possible you can do this:

class Object
  def singleton_class
    class << self
      self
    end
  end
end
like image 44
Sami Samhuri Avatar answered Sep 28 '22 00:09

Sami Samhuri