Apa yang dilakukan send () di Ruby?

96

Bisakah seseorang memberi tahu saya apa

send("#{Model.find...}")

adalah dan apakah?

Christian Bankester
sumber
2
Apakah ini seluruh baris kode? Maksud saya, apakah tidak ada sebelum 'mengirim'?
jerapah

Jawaban:

107

send mengirim pesan ke instance objek dan leluhurnya dalam hierarki kelas sampai beberapa metode bereaksi (karena namanya cocok dengan argumen pertama).

Secara praktis, baris-baris itu setara:

1.send '+', 2
1.+(2)
1 + 2

Perhatikan bahwa sendmengabaikan pemeriksaan visibilitas, sehingga Anda juga dapat memanggil metode privat (berguna untuk pengujian unit).


Jika benar-benar tidak ada variabel sebelum mengirim, itu berarti Object global digunakan:

send :to_s    # "main"
send :class   # Object
jerapah
sumber
1
Oh begitu, jadi orang mungkin menggunakan kirim jika ingin menyimpan sesuatu seperti 1. bulan di database alih-alih secara statis mengatakan jumlah hari.
Christian Bankester
3
Benar, Anda bisa menggunakannya untuk memanggil metode dengan nama yang dihitung, bukan statis. (Anda tidak boleh mengizinkan input pengguna yang tidak dibatasi, untuk menghindari pemanggilan metode privat ... Namun, Anda dapat memberi mereka awalan unik: send 'user_method _' + methodname, * args)
giraff
2
Kasus penggunaan yang baik mungkin jika Anda ingin menguji metode kelas yang dilindungi, Anda dapat memanggilnya di luar kelas — dalam file pengujian ..
GN.
108

send adalah metode ruby ​​(tanpa rel) yang memungkinkan untuk memanggil metode lain dengan nama.

Dari dokumentasi

   class Klass
     def hello(*args)
       "Hello " + args.join(' ')
     end
   end
   k = Klass.new
   k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"

http://corelib.rubyonrails.org/classes/Object.html#M001077

Nikita Rybak
sumber
5
Jawaban yang bagus, lebih jelas daripada jawaban yang diterima secara verbose.
aaron-coding
63

Salah satu fitur paling berguna yang menurut saya dengan metode .send adalah ia dapat memanggil metode secara dinamis. Ini dapat menghemat banyak pengetikan. Salah satu penggunaan metode .send yang paling populer adalah menetapkan atribut secara dinamis. Sebagai contoh:

class Car
  attr_accessor :make, :model, :year
end  

Untuk menetapkan atribut secara teratur, seseorang perlu

c = Car.new
c.make="Honda"
c.model="CRV"
c.year="2014"

Atau menggunakan metode .send:

c.send("make=", "Honda")
c.send("model=", "CRV")
c.send("year=","2014")

Tapi itu semua bisa diganti dengan yang berikut:

Dengan asumsi aplikasi Rails Anda perlu menetapkan atribut ke kelas mobil Anda dari input pengguna, Anda bisa melakukannya

c = Car.new()
params.each do |key, value|
  c.send("#{key}=", value)
end
Antonio Jha
sumber
Terima kasih untuk tautannya yang bagus
sid_09
7
Menggunakan .send dengan cara ini menambah kerumitan yang tidak perlu dan membuatnya lebih mudah untuk memasukkan bug ke dalam kode secara tidak sengaja. Misalnya, dalam kode Anda di atas, jika Anda menambahkan entri baru ke hash parameter Anda (seperti 'cylinders'), kode akan gagal dengan kesalahan metode yang tidak ditentukan.
Kevin Schwerdtfeger
1
menanggapi? dapat digunakan untuk mencegah kesalahan tersebut, jika diinginkan.
Richard_G
1
Ini penjelasan yang bagus! Terima kasih Jha!
Sharath
1
@Kevin Anda benar, tetapi terkadang mungkin perlu. Lebih banyak fleksibilitas berkorelasi dengan lebih banyak risiko, yang dapat dikurangi.
Akan Sheppard
12

Contoh lain, mirip dengan https://stackoverflow.com/a/26193804/1897857 Antonio Jha

adalah jika Anda perlu membaca atribut pada suatu objek.

Misalnya, jika Anda memiliki larik string, jika Anda mencoba mengulanginya dan memanggilnya pada objek Anda, itu tidak akan berhasil.

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.a
end
# => NoMethodError: undefined method `a'

Namun, Anda dapat sendmenggunakan string ke objek:

atts = ['name', 'description']
@project = Project.first
atts.each do |a|
  puts @project.send(a)
end
# => Vandalay Project
# => A very important project
Mike Vallano
sumber
Terima kasih atas penjelasannya yang sederhana dan mudah!
Junan Chakma
Terima kasih! Itulah jawaban yang saya cari. Ingin tahu apakah ini biasa digunakan? Saya menemukan sesuatu yang serupa di kode lama, tidak yakin saya harus tetap menggunakannya. @ Mike Vallano
B Liu
1
@ b-liu Saya telah melihatnya digunakan oleh pengembang berpengalaman dalam kode baru. Ini juga dapat membantu saat menggunakan define_method: apidock.com/ruby/Module/define_method .
Mike Vallano
Hebat! Terima kasih banyak! @MikeVallano
B Liu
4

Apa fungsi kirim?

send adalah cara lain untuk memanggil metode.

Ini diilustrasikan dengan baik dengan contoh:

o = Object.new
o.send(:to_s) # => "#<Object:0x00005614d7a24fa3>"
# is equivalent to:
o.to_s # => "#<Object:0x00005614d7a24fa3>"

Kirim nyawa di kelas Object .

Apa keuntungan dari ths?

Manfaat dari pendekatan ini adalah Anda dapat meneruskan metode yang ingin Anda panggil sebagai parameter. Berikut ini contoh sederhananya:

def dynamically_call_a_method(name)
    o = Object.new
    o.send name 
end
dynamically_call_a_method(:to_s) # => "#<Object:0x00005614d7a24fa3>"

Anda bisa memasukkan metode yang Anda inginkan untuk dipanggil. Dalam hal ini kami lolos :to_s. Ini bisa sangat berguna ketika melakukan metaprogramming ruby, karena ini memungkinkan kita memanggil metode yang berbeda sesuai dengan kebutuhan kita yang berbeda.

BKSpurgeon
sumber
0

Kasus penggunaan lain untuk tampilan:

    <%= link_to 
    send("first_part_of_path_#{some_dynamic_parameters}_end_path", 
    attr1, attr2), ....
    %>

Izinkan. Anda menulis tampilan skalabel yang bekerja dengan semua jenis objek dengan:

    render 'your_view_path', object: "my_object"
JustAnotherRubyLover
sumber
Ini menambahkan logika yang tidak perlu ke tampilan dan dapat memiliki implikasi keamanan. Jangan lakukan ini. Gunakan array dan hashes.
Derrek Bertrand