Bagaimana Anda mengoper argumen ke define_method?

155

Saya ingin meneruskan argumen ke metode yang didefinisikan menggunakan define_method, bagaimana saya melakukannya?

Sixty4Bit
sumber

Jawaban:

198

Blok yang Anda lewati ke define_method dapat menyertakan beberapa parameter. Begitulah cara Anda mendefinisikan metode menerima argumen. Ketika Anda mendefinisikan sebuah metode, Anda benar-benar hanya menjuluki blok dan menyimpan referensi di kelas. Parameter datang dengan blok. Begitu:

define_method(:say_hi) { |other| puts "Hi, " + other }
Kevin Conner
sumber
Yah itu hanya hal beaty murni murni. Kerja bagus, Kevin Costner.
Darth Egregious
90

... dan jika Anda menginginkan parameter opsional

 class Bar
   define_method(:foo) do |arg=nil|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> nil
 a.foo 1
 # => 1

... Argumen sebanyak yang Anda inginkan

 class Bar
   define_method(:foo) do |*arg|                  
     arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> []
 a.foo 1
 # => [1]
 a.foo 1, 2 , 'AAA'
 # => [1, 2, 'AAA']

... kombinasi dari

 class Bar
   define_method(:foo) do |bubla,*arg|
     p bubla                  
     p arg                                                                                          
   end   
 end

 a = Bar.new
 a.foo
 #=> wrong number of arguments (0 for 1)
 a.foo 1
 # 1
 # []

 a.foo 1, 2 ,3 ,4
 # 1
 # [2,3,4]

... mereka semua

 class Bar
   define_method(:foo) do |variable1, variable2,*arg, &block|  
     p  variable1     
     p  variable2
     p  arg
     p  block.inspect                                                                              
   end   
 end
 a = Bar.new      
 a.foo :one, 'two', :three, 4, 5 do
   'six'
 end

Memperbarui

Ruby 2.0 memperkenalkan gambar percikan ganda **(dua bintang) yang ( saya kutip ):

Ruby 2.0 memperkenalkan argumen kata kunci, dan ** bertindak seperti *, tetapi untuk argumen kata kunci. Ini mengembalikan hash dengan pasangan kunci / nilai.

... dan tentu saja Anda dapat menggunakannya dalam metode define juga :)

 class Bar 
   define_method(:foo) do |variable1, variable2,*arg,**options, &block|
     p  variable1
     p  variable2
     p  arg
     p  options
     p  block.inspect
   end 
 end 
 a = Bar.new
 a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}

Contoh atribut yang dinamai:

 class Bar
   define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
     p  variable1
     p  color
     p  other_options
     p  block.inspect
   end
 end
 a = Bar.new
 a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
   'six'
 end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}

Saya mencoba membuat contoh dengan argumen kata kunci, splat, dan double splat semuanya dalam satu:

 define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
    # ...

atau

 define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
    # ...

... tapi ini tidak akan berhasil, sepertinya ada batasan. Ketika Anda berpikir tentang hal itu masuk akal karena operator percikan adalah "menangkap semua argumen yang tersisa" dan percikan ganda adalah "menangkap semua argumen kata kunci yang tersisa" karena itu pencampuran mereka akan mematahkan logika yang diharapkan. (Saya tidak punya referensi untuk membuktikan hal ini, doh!)

perbarui 2018 Agustus:

Artikel ringkasan: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html

setara8
sumber
Menarik - khususnya blok ke-4: berhasil pada 1.8.7! Blok pertama tidak berfungsi di 1.8.7, dan blok kedua memiliki salah ketik (seharusnya a.foo 1bukan foo 1). Terima kasih!
Sony Santos
1
terima kasih atas umpan baliknya, kesalahan ketik telah diperbaiki, ... Pada ruby ​​1.9.3 dan 1.9.2 semua contoh berfungsi dan saya yakin bahwa pada 1.9.1 juga (tetapi tidak mencoba)
setara 8
Saya menggabungkan jawaban ini dengan jawaban yang diterima di stackoverflow.com/questions/4470108/... untuk mengetahui cara menimpa (tidak menimpa) metode saat runtime yang mengambil args opsional dan blok dan masih dapat memanggil metode asli dengan args dan memblokir. Ah, ruby. Secara khusus, saya perlu menimpa Savon :: Client.request di dev env untuk panggilan API tunggal ke host yang hanya dapat saya akses dalam produksi. Bersulang!
pduey
59

Selain jawaban Kevin Conner: argumen blok tidak mendukung semantik yang sama dengan argumen metode. Anda tidak dapat mendefinisikan argumen default atau memblokir argumen.

Ini hanya diperbaiki di Ruby 1.9 dengan sintaks "stabby lambda" alternatif baru yang mendukung semantik argumen metode penuh.

Contoh:

# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end

# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }

# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }
Jörg W Mittag
sumber
3
Sebenarnya, saya percaya argumen blok pada define_method mendukung splat, yang dapat memberikan cara round-a-bout untuk mendefinisikan argumen default juga.
Chinasaur
1
Chinasaur benar tentang argumen blok yang memungkinkan percikan. Saya telah mengkonfirmasi ini di Ruby 1.8.7 dan 1.9.1.
Peter Wagenet
Terima kasih, saya lupa tentang ini. Diperbaiki sekarang
Jörg W Mittag