Есть ли alias_method для метода класса?

10

Рассмотрим следующий класс:

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

Это не проблема, и вы можете позвонить Foo.new.a_new_inst_methodбез проблем.

Я хотел бы иметь возможность иметь метод класса наподобие Foo.add_widget(*items)псевдонима, чтобы я мог сделать что-то вроде:

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

По сути, он похож на «стиль ruby», 1.minuteи 2.minutesпоэтому я хочу создать псевдоним Foo.add_widgetтак называемых Foo.add_widgetsвызовов точно такого же метода. Я знаю, что мог бы обернуть это, но я чувствую, что должен быть в состоянии сделать это более чистым способом.

Рассмотрим мою попытку попробовать что-то вроде этого:

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

Однако я получаю следующую ошибку:

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

И, похоже, это не работает для методов класса. Как мне это сделать?

aarona
источник
Отвечает ли это на ваш вопрос? Как псевдоним метода класса в модели рельсов?
сломя голову

Ответы:

9

alias_methodпсевдонимы метода экземпляров получателя. Методы класса на самом деле являются методами экземпляра, определенными в одноэлементном классе класса.

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

Чтобы создать псевдоним метода экземпляра, определенного в одноэлементном классе, вы можете открыть его, используя class << objectсинтаксис.

class << MyClass
  alias_method :b, :a
end

MyClass.b
#=> "Hello World!"

Или вы можете обратиться к нему напрямую, используя singleton_classметод.

MyClass.singleton_class.alias_method :c, :a

MyClass.c
#=> "Hello World!"

Если вы все еще находитесь в контексте класса, selfбудет ссылаться на класс. Таким образом, вышесказанное также может быть записано как:

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

Или

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

Или комбинация двух.

3limin4t0r
источник
5

Вы можете обернуть методы класса и их alias_methodвызовы в отдельный модуль и включить этот модуль в свой класс с помощью 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
Стефан
источник
Привет! Я забыл о extend ClassMethodsрешении. +1
Аарона
4

Важно понимать, что в Ruby не существует метода класса .

Метод класса на самом деле просто одноэлементный метод. В методах класса нет ничего особенного. Каждый объект может иметь одноэлементные методы. Мы просто называем их «методами класса», когда объект является « Classпотому что» метод синглтона экземпляра Class«слишком длинный и громоздкий.

Подождите! Я сказал "метод синглтона"?

Еще одна важная вещь, которую нужно понять, это то, что в Ruby не существует такого метода, как синглтон .

Одноэлементный метод - это на самом деле просто обычный скучный старый метод экземпляра одноэлементного класса. В одноэлементных методах нет ничего особенного. Они просто методы экземпляра, как и любой другой метод экземпляра.

На самом деле, в Ruby есть только методы экземпляра. Нет функций, нет конструкторов, нет статических методов, нет методов класса, нет функций модуля, нет одноэлементных методов.

Вопрос не в том, «это метод класса, метод одиночный», а в том, «в каком модуле определен этот метод?».

«Методы Singleton» - это действительно методы экземпляра, определенные в классе Singleton. Синтаксис для доступа к одноэлементному классу foo:

class << foo
end

Существует также метод, Object#singleton_classкоторый возвращает одноэлементный класс объекта.

Почему я так настойчиво говорю о том, что каждый метод является методом экземпляра, а методы класса не существуют? Потому что это означает, что объектная модель Ruby намного проще, чем думают люди! В конце концов, в своем вопросе вы уже показали, что знаете, как псевдонимы методов экземпляра, но вы говорите, что не знаете, как псевдонимы методов класса. Но это неправильно! Вы делаете знаете , как методы класса псевдоним, потому что они просто методы экземпляра . Если бы вас правильно учили этому факту, вам никогда бы не пришлось задавать этот вопрос!

Как только вы поймете, что каждый метод является методом экземпляра и что то, что мы называем «одноэлементными методами», является просто методами экземпляра одноэлементного класса, решение становится ясным:

singleton_class.alias_method :a_new_class_method, :a_class_method

Примечание: когда я писал выше, что «нет такой вещи, как X», я имел в виду, что « в языке Ruby нет такой вещи, как X ». Это не означает, что эти понятия не существуют в сообществе Ruby .

Мы регулярно говорим о «одноэлементных методах» и «методах класса» просто потому, что это проще, чем говорить о «методах экземпляра класса singleton» или «методах экземпляра класса singleton объекта, который является экземпляром Classкласса ». Есть даже такие методы , как Object#define_singleton_method, Object#singleton_method, Object#singleton_methods, Module#private_class_method, Module#public_class_method, и Module#module_functionв основной библиотеке Ruby. Но всегда важно помнить, что это не языковые понятия. Это концепции сообщества, которые существуют только в наших головах и в названиях некоторых библиотечных методов.

Йорг Миттаг
источник
2

ОК, похоже, я могу сделать что-то вроде этого, и это будет работать:

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"

Если у кого-нибудь есть какая-либо полезная информация о языке Ruby здесь и вы хотели бы представить исчерпывающий ответ, я дам вам +1.

aarona
источник
1
Когда вы следуете class << selfза ним, alias_method :a_new_class_method, :a_class_methodвы фактически призываете alias_methodсинглтон-класс Foo. Другой способ написать это: singleton_class.alias_method :a_new_class_method, :a_class_methodв контексте класса.
3limin4t0r
Что еще вы хотите от ответа? Я имею в виду, это, вероятно, самое чистое решение.
Дейв Ньютон
@ 3limin4t0r, если ты сделаешь такой ответ, я его +1 добавлю. Спасибо за эту дополнительную информацию.
Аарона
@DaveNewton вы правы, но у меня также есть философия, что если кто-то может предоставить какую-либо более значимую информацию о проблеме, даже если это косвенно решает проблему, я думаю, что это также должно быть вознаграждено.
Аарона
2

alias_methodработает с экземпляром, поэтому вам нужно пойти на один уровень глубже и оперировать Classэкземпляром или метаклассом класса . У Ruby есть дикая объектная модель, которая может привести к поломке мозга, но она также дает вам массу силы:

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
источник
Я работал с ruby ​​в контексте рельсов около десяти лет, но только недавно начал углубляться в объектную модель. Есть так много классных вещей, доступных!
Аарона