16

Consider the following class:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method
end

This is no issue and you can call Foo.new.a_new_inst_method with no issue.

I'd like the ability to have a class method like Foo.add_widget(*items) and alias it so I can do something like:

Foo.add_widget 'item1'
Foo.add_widgets 'item2', 'item3'

So essentially it has a "ruby style" like 1.minute and 2.minutes so I want to alias a Foo.add_widget so calling Foo.add_widgets calls the exact same method. I know I could wrap it but I feel like I should be able to do this in a cleaner fashion.

Consider my attempt at trying something like this:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method
  alias_method :a_new_class_method, :a_class_method
end

However, I get the following error:

NameError (undefined method `a_class_method' for class `Foo')

And so it looks like this doesn't work for class methods. How do I go about doing this?

aarona
  • 35,986
  • 41
  • 138
  • 186
  • 1
    The question is similar but its in a Rails context. The solutions would answer my question, but I'm almost certain that he could probably leverage Arel to solve his problem. The question I posted was designed purely as a Ruby question so that it was easier to search for. You might want to suggest an edit of the title of his question because it didn't show up when I searched for a solution to my problem. – aarona May 05 '20 at 17:37
  • 1
    Yes, unfortunately the other question has a misleading and completely irrelevant Rails context and might be harder to find for some people. IMHO, it's still a perfect *content* duplicate, although not a *topic* one. Answers are also equal. Anyways, still a good question of yours. *Side note: When I flagged yours as a dupe during review, the review system slapped me in the face and seriously accused me of not paying attention, a ridiculous claim by itself since locating a (potential) dupe requires quite a lot of attention. Just couldn't believe this had not come up before.* – amain May 05 '20 at 18:01
  • Didn't come up before for my particular question or in other reviews that you made? What message did you get? I'm curious. – aarona May 06 '20 at 01:56
  • I was referring to me when I read your question during review, when I immediately thought "I'm sure this has already been answered". – amain May 07 '20 at 21:17

5 Answers5

13

alias_method aliases an instances method of the receiver. Class methods are actually instance methods defined on the singleton class of a class.

class MyClass
  def self.a
    "Hello World!"
  end
end

method_1 = MyClass.method(:a).unbind
method_2 = MyClass.singleton_class.instance_method(:a)

method_1 == method_2
#=> true

To alias an instance method defined on the singleton class you can either open it up using the class << object syntax.

class << MyClass
  alias_method :b, :a
end

MyClass.b
#=> "Hello World!"

Or you can refer to it directly using the singleton_class method.

MyClass.singleton_class.alias_method :c, :a

MyClass.c
#=> "Hello World!"

If you are still within the class context self will refer to the class. So the above could also be written as:

class MyClass
  class << self
    def a
      "Hello World!"
    end
    alias_method :b, :a
  end
end

Or

class MyClass
  def self.a
    "Hello World!"
  end
  singleton_class.alias_method :c, :a
end

Or a combination of the two.

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
8

The important thing to understand is that there is no such thing as a class method in Ruby.

A class method is really just a singleton method. There is nothing special about class methods. Every object can have singleton methods. We just call them "class methods" when the object is a Class because "singleton method of an instance of Class" is too long and unwieldy.

Wait! Did I say "singleton method"?

Another important thing to understand is that there is no such thing as a singleton method in Ruby.

A singleton method is really just a regular boring old instance method of the singleton class. There is nothing special about singleton methods. They are just instance methods like any other instance method.

In fact, Ruby has only instance methods. No functions, no constructors, no static methods, no class methods, no module functions, no singleton methods.

The question is not "is this a class method, is this a singleton method", but rather "which module is this method defined in?"

"Singleton methods" are really instance methods defined in the singleton class. The syntax for accessing the singleton class of foo is

class << foo
end

There is also a method Object#singleton_class which returns the singleton class of an object.

Why am I so aggressively hammering on about the fact that every method is an instance method and that class methods don't exist? Because it means that Ruby's object model is much simpler than people think it is! After all, in your question, you already show that you know how to alias instance methods, but you say that you don't know how to alias class methods. But, that is wrong! You do know how to alias class methods, because they are just instance methods. If you had been taught this fact properly, you would never have needed to ask this question!

Once you understand that every method is an instance method, and that what we call "singleton methods" are just instance methods of the singleton class, the solution becomes clear:

singleton_class.alias_method :a_new_class_method, :a_class_method

Note: when I wrote above that "there is no such thing as X", what I meant was that "there is no such thing as X in the Ruby language". That does not mean that those concepts don't exist in the Ruby community.

We regularly talk about "singleton methods" and "class methods", simply because it is easier than talking about "instance methods of the singleton class" or "instance methods of the singleton class of an object which happens to be an instance of the Class class". There are even methods like Object#define_singleton_method, Object#singleton_method, Object#singleton_methods, Module#private_class_method, Module#public_class_method, and Module#module_function in the Ruby core library. But it is always important to remember that those are not language concepts. Those are community concepts that only exist in our heads and in the names of some library methods.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
5

You can wrap class methods and their alias_method calls into a separate module and incorporate that module into your class via extend:

class Foo
  def an_inst_method
    'instance method'
  end
  alias_method :a_new_inst_method, :an_inst_method

  module ClassMethods
    def a_class_method
      'class method'
    end
    alias_method :a_new_class_method, :a_class_method
  end

  extend ClassMethods
end
Stefan
  • 109,145
  • 14
  • 143
  • 218
3

OK, it looks like I can do something like this and it will work:

class Foo
  def an_inst_method
    'instance method'
  end

  def self.a_class_method
    'class method'
  end

  alias_method :a_new_inst_method, :an_inst_method

  class << self
    alias_method :a_new_class_method, :a_class_method
  end
end

Foo.a_class_method # => "class method" 
Foo.a_new_class_method # => "class method"

If anyone has any useful information about the Ruby language here and would like to submit a comprehensive answer, I'll give you a +1.

aarona
  • 35,986
  • 41
  • 138
  • 186
  • 2
    When you do `class << self` followed by `alias_method :a_new_class_method, :a_class_method` you actually call `alias_method` upon the singleton class of `Foo`. Another way of writing this is: `singleton_class.alias_method :a_new_class_method, :a_class_method` in class context. – 3limin4t0r Apr 20 '20 at 19:51
  • What more do you want from an answer? I mean, this is likely the cleanest solution. – Dave Newton Apr 20 '20 at 19:56
  • @DaveNewton you're right but because I also have a philosophy that if someone can provide any more meaningful information to a problem even if it indirectly solves the problem, I think that should also be rewarded. – aarona Apr 20 '20 at 20:02
2

alias_method operates on the instance, so you need to go one level deeper and operate on the Class instance, or the class' metaclass. Ruby has a wild object model that can be kind of brain breaking, but it also gives you a ton of power:

class Foo
  def an_inst_method
    'instance method'
  end

  alias_method :a_new_inst_method, :an_inst_method

  class << self
    def a_class_method
      'class method'
    end

    alias_method :a_new_class_method, :a_class_method
  end
end
lobati
  • 9,284
  • 5
  • 40
  • 61