Diberikan kelas, lihat apakah instance memiliki metode (Ruby)

227

Saya tahu di Ruby yang bisa saya gunakan respond_to?untuk memeriksa apakah suatu objek memiliki metode tertentu.

Tetapi, mengingat kelas, bagaimana saya bisa memeriksa jika instance memiliki metode tertentu?

yaitu, sesuatu seperti

Foo.new.respond_to?(:bar)

Tapi saya merasa harus ada cara yang lebih baik daripada membuat contoh baru.

pengguna94154
sumber

Jawaban:

364

Saya tidak tahu mengapa semua orang menyarankan Anda harus menggunakan instance_methodsdan include?kapan method_defined?melakukan pekerjaan.

class Test
  def hello; end
end

Test.method_defined? :hello #=> true

CATATAN

Jika Anda datang ke Ruby dari bahasa OO lain ATAU Anda berpikir itu method_definedHANYA metode yang Anda tetapkan secara eksplisit dengan:

def my_method
end

kemudian baca ini:

Di Ruby, properti (atribut) pada model Anda pada dasarnya juga merupakan metode. Jadi method_defined?juga akan mengembalikan true untuk properti, bukan hanya metode.

Sebagai contoh:

Diberikan turunan dari kelas yang memiliki atribut String first_name:

<instance>.first_name.class #=> String

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

karena first_namemerupakan atribut dan metode (dan string tipe String).

horseyguy
sumber
9
method_defined? adalah solusi terbaik karena instance_methods berubah antara Ruby 1.8 dan 1.9. 1.8 mengembalikan array string dan 1.9 mengembalikan array simbol. method_defined? mengambil simbol di 1.8 dan 1.9.
Jason
2
Belum lagi bahwa: instance_methods akan membuat array baru setiap panggilan dan bahwa: termasuk? maka harus berjalan array untuk memberi tahu. Sebaliknya,: method_defined? akan secara internal menanyakan metode tabel pencarian (satu pencarian hash yang ada di C) dan memberi tahu Anda tanpa membuat objek baru.
Asher
4
Sementara ada setidaknya satu keuntungan ketika Anda menggunakannya seperti ini, String.instance_methods(false).include? :freeze, yang palsu argumen bisa tahu persis apakah freezemetode didefinisikan dalam kelas String atau tidak. Dalam hal ini ia akan mengembalikan false karena freezemetode diwarisi dari modul Kernel oleh String, tetapi String.method_defined? :freezeakan selalu kembali benar, yang tidak dapat banyak membantu dengan tujuan seperti itu.
ugoa
1
@Bane Anda membingungkan metode di kelas dengan metode yang tersedia di instance. method_defined?harus digunakan di kelas (mis. Array), tetapi Anda mencoba menggunakannya dalam instance (mis [])
horseyguy
@banister Benar sekali. Terima kasih atas klarifikasi, sangat masuk akal saat membacanya. :) Saya sudah menghapus komentar saya agar tidak bingung.
Bane
45

Anda dapat menggunakan method_defined?sebagai berikut:

String.method_defined? :upcase # => true

Jauh lebih mudah, portabel, dan efisien daripada yang instance_methods.include?disarankan orang lain.

Ingatlah bahwa Anda tidak akan tahu apakah kelas merespons secara dinamis beberapa panggilan dengan method_missing, misalnya dengan mendefinisikan ulang respond_to?, atau sejak Ruby 1.9.2 dengan mendefinisikan respond_to_missing?.

Marc-André Lafortune
sumber
2
Perhatikan bahwa jika Anda memiliki instance di tangan dan Anda tidak tahu itu kelas, Anda bisa melakukannyainstance.class.method_defined? :upcase
jaydel
25

Sebenarnya ini tidak berfungsi untuk Obyek dan Kelas.

Ini tidak:

class TestClass
  def methodName
  end
end

Jadi dengan jawaban yang diberikan, ini berfungsi:

TestClass.method_defined? :methodName # => TRUE

Tapi ini TIDAK berhasil:

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

Jadi saya menggunakan ini untuk kelas dan objek:

Kelas:

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

Benda:

t = TestClass.new
t.methods.include? 'methodName'  # => TRUE
Alex V
sumber
3
Sebagai contoh, Anda juga dapat menggunakan `instance.class.method_defined? '
Luksurious
Itu tergantung pada versi ruby ​​Anda. Metode di atas berfungsi untuk semua versi termasuk 1.8.7.
Alex V
10

Jawaban untuk " Diberikan kelas, lihat apakah instance memiliki metode (Ruby) " lebih baik. Rupanya Ruby memiliki built-in ini, dan entah bagaimana saya melewatkannya. Jawaban saya dibiarkan untuk referensi, terlepas.

Kelas Ruby merespons metode instance_methodsdan public_instance_methods. Di Ruby 1.8, yang pertama mencantumkan semua nama metode instance dalam array string, dan yang kedua membatasi ke metode publik. Perilaku kedua adalah apa yang kemungkinan besar Anda inginkan, karena respond_to?membatasi dirinya pada metode publik secara default, juga.

Foo.public_instance_methods.include?('bar')

Di Ruby 1.9, metode tersebut mengembalikan array simbol.

Foo.public_instance_methods.include?(:bar)

Jika Anda berencana sering melakukan ini, Anda mungkin ingin memperluas Moduleuntuk memasukkan metode pintas. (Ini mungkin aneh untuk menempatkan ini Moduledaripada Class, tetapi karena di situlah instance_methodsmetode tinggal, yang terbaik untuk tetap sejalan dengan pola itu.)

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

Jika Anda ingin mendukung Ruby 1.8 dan Ruby 1.9, itu akan menjadi tempat yang nyaman untuk menambahkan logika untuk mencari string dan simbol, juga.

Matchu
sumber
1
method_defined?lebih baik :)
horseyguy
@banister - oh, saya, dan saya entah bagaimana melewatkannya! xD Melemparkan +1 di belakang jawaban Marc, dan menambahkan tautan untuk memastikannya tidak terlewatkan :)
Matchu
5

Mencoba Foo.instance_methods.include? :bar

imightbeinatree di Cloudspace
sumber
3

Tidak yakin apakah ini cara terbaik, tetapi Anda selalu bisa melakukan ini:

Foo.instance_methods.include? 'bar'
dbyrne
sumber
3

Jika Anda memeriksa untuk melihat apakah suatu objek dapat merespons serangkaian metode, Anda dapat melakukan sesuatu seperti:

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

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

yang methods & something.methodsakan bergabung dengan dua array pada umum / pencocokan elemen mereka. something.methods mencakup semua metode yang Anda periksa, itu akan sama dengan metode. Sebagai contoh:

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

begitu

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

Dalam situasi ini, Anda ingin menggunakan simbol, karena ketika Anda memanggil .methods, ia mengembalikan array simbol dan jika Anda menggunakannya ["my", "methods"], itu akan mengembalikan false.

TalkativeTree
sumber
2

klass.instance_methods.include :method_nameatau "method_name", tergantung pada versi Ruby yang saya pikir.

yxhuvud
sumber
2
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] 

Semoga ini bisa membantu Anda!

Rajeev Kannav Sharma
sumber
1

Pada kasus saya bekerja dengan ruby ​​2.5.3 kalimat berikut ini telah bekerja dengan baik:

value = "hello world"

value.methods.include? :upcase

Ini akan mengembalikan nilai boolean benar atau salah.

Manuel Lazo
sumber
1

Meskipun respond_to?akan mengembalikan true hanya untuk metode publik, memeriksa "definisi metode" pada kelas juga mungkin berkaitan dengan metode pribadi.

Pada Ruby v2.0 + memeriksa set publik dan pribadi dapat dicapai dengan

Foo.private_instance_methods.include?(:bar) || Foo.instance_methods.include?(:bar)
Epigene
sumber