web-dev-qa-db-ja.com

クラスを指定して、インスタンスにメソッドがあるかどうかを確認します(Ruby)

Rubyでは、respond_to?を使用してオブジェクトに特定のメソッドがあるかどうかを確認できることを知っています。

しかし、クラスが与えられた場合、インスタンスに特定のメソッドがあるかどうかを確認するにはどうすればよいですか?

すなわち、何かのような

Foo.new.respond_to?(:bar)

しかし、新しいインスタンスをインスタンス化するよりも良い方法があるはずです。

214
user94154

instance_methodsが仕事をするときにinclude?method_defined?を使用すべきだとみんなが提案している理由がわかりません。

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

NOTE

別のRuby言語OOからORにアクセスする場合、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型の文字列)の両方であるためです。

353
horseyguy

次のようにmethod_defined?を使用できます。

String.method_defined? :upcase # => true

instance_methods.include?よりもはるかに簡単で、移植性があり、効率的です。

クラスがmethod_missingを使用していくつかの呼び出しに動的に応答するかどうかは、たとえばrespond_to?を再定義することによって、または respond_to_missing?を定義することによってRuby 1.9.2以降

44

実際、これはオブジェクトとクラスの両方で機能しません。

これは:

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
23
Alex V

クラスを指定して、インスタンスにメソッド(Ruby)があるかどうかを確認する 」の答えの方が優れています。どうやらRubyにこのビルトインがあり、どういうわけか見逃しました。とにかく、私の答えは参照用に残されています。

Rubyクラスは、メソッド instance_methods および public_instance_methods に応答します。 Ruby 1.8では、最初のメソッドは文字列の配列内のすべてのインスタンスメソッド名をリストし、2番目のメソッドはそれをパブリックメソッドに制限します。 respond_to?もデフォルトでパブリックメソッドに制限しているため、2番目の動作が最も望ましいと思われます。

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の両方をサポートする場合は、文字列と記号の両方を検索するロジックを追加するのに便利な場所です。

9
Matchu

Foo.instance_methods.include? :barを試してください

これが最善の方法であるかどうかはわかりませんが、常にこれを行うことができます。

Foo.instance_methods.include? 'bar'
3
dbyrne

オブジェクトが一連のメソッドに応答できるかどうかを確認している場合、次のようなことができます。

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

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

methods & something.methodsは、共通/一致する要素で2つの配列を結合します。 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が返されるため、シンボルを使用する必要があります。

2
TalkativeTree

Railsのmethod_defined?に何か問題があると思います。一貫性がないか何かである可能性があるため、Railsを使用する場合は、 attribute_method?(attribute) の何かを使用することをお勧めします。

" ActiveRecordクラスのmethod_definedのテストは、インスタンス化まで動作しません "は不整合に関する質問です。

2
NoDisplayName

Ruby 2.5.3を扱う私の場合、次の文は完全に機能しました。

value = "hello world"

value.methods.include? :upcase

ブール値trueまたはfalseを返します。

1
Manuel Lazo
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] 

これがあなたを助けることを願っています!

Rubyバージョンに応じて、klass.instance_methods.include :method_nameまたは"method_name"

1
yxhuvud