W jaki sposób przekazujesz argumenty do define_method?

155

Chciałbym przekazać argument (y) do metody definiowanej przy użyciu define_method, jak mam to zrobić?

Sixty4Bit
źródło

Odpowiedzi:

198

Blok, który przekazujesz do define_method, może zawierać pewne parametry. W ten sposób Twoja zdefiniowana metoda przyjmuje argumenty. Definiując metodę, tak naprawdę po prostu nazywasz blok i zachowujesz do niego odniesienie w klasie. Parametry są dostarczane z blokiem. Więc:

define_method(:say_hi) { |other| puts "Hi, " + other }
Kevin Conner
źródło
Cóż, to po prostu czysta niezafałszowana bestia. Dobra robota, Kevin Costner.
Darth Egregious
90

... i jeśli chcesz opcjonalne parametry

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

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

... tyle argumentów, ile chcesz

 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']

...kombinacja

 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]

... wszyscy

 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

Aktualizacja

Ruby 2.0 wprowadził podwójny splat **(dwie gwiazdki), który ( cytuję ):

Ruby 2.0 wprowadził argumenty słów kluczowych i ** zachowuje się jak *, ale dla argumentów słów kluczowych. Zwraca Hash z parami klucz / wartość.

... i oczywiście możesz go również użyć w definiowaniu metody :)

 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}

Przykład nazwanych atrybutów:

 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}

Próbowałem stworzyć przykład ze słowami kluczowymi argument, splat i double splat w jednym:

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

lub

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

... ale to nie zadziała, wygląda na to, że jest ograniczenie. Kiedy się nad tym zastanowić, ma to sens, ponieważ operator splat „przechwytuje wszystkie pozostałe argumenty”, a double splat „przechwytuje wszystkie pozostałe argumenty słów kluczowych”, dlatego ich mieszanie złamałoby oczekiwaną logikę. (Nie mam żadnego odniesienia, aby to udowodnić, doh!)

aktualizacja 2018 sierpień:

Artykuł podsumowujący: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html

odpowiednik 8
źródło
Ciekawe - szczególnie czwarty blok: zadziałał na 1.8.7! Pierwszy blok nie działał w 1.8.7, a drugi blok zawiera literówkę (powinno być a.foo 1zamiast foo 1). Dzięki!
Sony Santos
1
dzięki za informację zwrotną, literówka została naprawiona ... Na ruby ​​1.9.3 i 1.9.2 wszystkie przykłady działają i jestem pewien, że na 1.9.1 też (ale nie próbowałem)
odpowiednik 8
Połączyłem tę odpowiedź z zaakceptowaną odpowiedzią na stackoverflow.com/questions/4470108/ ... aby dowiedzieć się, jak nadpisać (a nie nadpisać) metodę w czasie wykonywania, która przyjmuje opcjonalne argumenty i blok i nadal może wywoływać oryginalną metodę za pomocą argumentów i blok. Ach, rubin. W szczególności musiałem nadpisać Savon :: Client.request w moim dev env dla pojedynczego wywołania API do hosta, do którego mam dostęp tylko w środowisku produkcyjnym. Twoje zdrowie!
pduey
59

Oprócz odpowiedzi Kevina Connera: argumenty blokowe nie obsługują tej samej semantyki, co argumenty metod. Nie można zdefiniować argumentów domyślnych ani argumentów blokowych.

Zostało to naprawione tylko w Rubim 1.9 dzięki nowej, alternatywnej składni „stabby lambda”, która obsługuje pełną semantykę argumentów metod.

Przykład:

# 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
źródło
3
W rzeczywistości uważam, że argumenty blokowe w define_method obsługują splat, co może również zapewnić okrągły sposób definiowania argumentów domyślnych.
Chinasaur
1
Chinasaur ma rację co do argumentów blokowych pozwalających na używanie ikon. Potwierdziłem to w Rubim 1.8.7 i 1.9.1.
Peter Wagenet
Dzięki, zapomniałem o tym. Naprawiono teraz.
Jörg W Mittag