Для данного класса посмотрите, есть ли у экземпляра метод (Ruby) - PullRequest
212 голосов
/ 22 июля 2010

Я знаю в Ruby, что могу использовать respond_to? для проверки, есть ли у объекта определенный метод.

Но, учитывая класс, как я могу проверить, есть ли у экземпляра определенный метод?*

то есть что-то вроде

Foo.new.respond_to?(:bar)

Но я чувствую, что должен быть лучший способ, чем создание нового экземпляра.

Ответы [ 11 ]

349 голосов
/ 22 июля 2010

Я не знаю, почему все предлагают, чтобы вы использовали instance_methods и include?, когда method_defined? выполняет свою работу.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

ПРИМЕЧАНИЕ

В случае, если вы переходите на Ruby с другого языка OO ИЛИ вы думаете, что method_defined означает ТОЛЬКО методы, которые вы определили явно с помощью:

def my_method
end

, тогда прочитайте это:

Ruby, свойство (атрибут) в вашей модели - это тоже метод.Так что method_defined? также вернет true для свойств, , а не только для методов.

Например:

Учитывая экземпляр класса, который имеетатрибут String first_name:

<instance>.first_name.class #=> String

<instance>.class.method_defined?(:first_name) #=> true

, поскольку first_name является одновременно атрибутом и методом (и строкой типа String).

44 голосов
/ 22 июля 2010

Вы можете использовать method_defined? следующим образом:

String.method_defined? :upcase # => true

Гораздо проще, портативнее и эффективнее, чем instance_methods.include?, о котором все остальные думают.

Имейте в виду, что вы не будете знать, будет ли класс динамически реагировать на некоторые вызовы с помощью method_missing, например, путем переопределения respond_to?, или начиная с Ruby 1.9.2 путем определения respond_to_missing? .

23 голосов
/ 11 октября 2013

На самом деле это не работает как для объектов, так и для классов.

Это делает:

class TestClass
  def methodName
  end
end

Итак, при данном ответе это работает:

TestClass.method_defined? :methodName # => TRUE

Но это НЕ работает:

t = TestClass.new
t.method_defined? : methodName  # => ERROR!

Так что я использую это для классов и объектов:

Классы:

TestClass.methods.include? 'methodName'  # => TRUE

Предметы:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE
9 голосов
/ 22 июля 2010

Ответ на " Для данного класса лучше узнать, есть ли у экземпляра метод (Ruby) ". Очевидно, в Ruby есть эта встроенная система, и я почему-то пропустил ее. Мой ответ оставлен для справки, вне зависимости.

Классы Ruby отвечают на методы instance_methods и public_instance_methods. В Ruby 1.8 первый перечисляет все имена методов экземпляра в массиве строк, а второй ограничивает его открытыми методами. Второе поведение - это то, что вам, скорее всего, нужно, так как respond_to? по умолчанию также ограничивается публичными методами.

Foo.public_instance_methods.include?('bar')

В Ruby 1.9 эти методы возвращают массивы символов.

Foo.public_instance_methods.include?(:bar)

Если вы планируете делать это часто, возможно, вы захотите расширить Module, чтобы включить метод быстрого доступа. (Может показаться странным присвоить это Module вместо Class, но, поскольку именно там живут методы instance_methods, лучше придерживаться этого шаблона.)

class Module
  def instance_respond_to?(method_name)
    public_instance_methods.include?(method_name)
  end
end

Если вы хотите поддерживать как Ruby 1.8, так и Ruby 1.9, это было бы удобным местом для добавления логики для поиска как строк, так и символов.

4 голосов
/ 22 июля 2010

Попробуйте Foo.instance_methods.include? :bar

3 голосов
/ 22 июля 2010

Не уверен, что это лучший способ, но вы всегда можете сделать это:

Foo.instance_methods.include? 'bar'
2 голосов
/ 05 июня 2014

Я думаю, что-то не так с method_defined? в Rails.Это может быть несовместимо или что-то в этом роде, поэтому, если вы используете Rails, лучше использовать что-то из attribute_method?(attribute).

" test для method_defined? В классах ActiveRecord не работаетдо тех пор, пока экземпляр"не станет вопросом о несостоятельности.

2 голосов
/ 27 января 2014

Если вы проверяете, может ли объект ответить на серию методов, вы можете сделать что-то вроде:

methods = [:valid?, :chase, :test]

def has_methods?(something, methods)
  methods & something.methods == methods
end

methods & something.methods объединит два массива в их общем / совпадающемэлементы.Что-то включает в себя все методы, которые вы проверяете, это будет равнозначно методам.Например:

[1,2] & [1,2,3,4,5]
==> [1,2]

так что

[1,2] & [1,2,3,4,5] == [1,2]
==> true

В этой ситуации вы захотите использовать символы, потому что когда вы вызываете .methods, он возвращает массив символов, и если выиспользовал ["my", "methods"], он вернул бы false.

1 голос
/ 21 января 2019

В моем случае, работающем с ruby ​​2.5.3, следующие предложения сработали отлично:

value = "hello world"

value.methods.include? :upcase

Будет возвращено логическое значение true или false.

1 голос
/ 05 июня 2014
class Foo
 def self.fclass_method
 end
 def finstance_method
 end
end

foo_obj = Foo.new
foo_obj.class.methods(false)
=> [:fclass_method] 

foo_obj.class.instance_methods(false)
=> [:fclass_method] 

Надеюсь, это поможет вам!

...