2015-11-14 7 views
8

Method#unbind restituisce un riferimento UnboundMethod al metodo, che può essere successivamente associato a un altro oggetto utilizzando UnboundMethod#bind.Qual è il punto del meccanismo di unbinding del metodo di Ruby?

class Foo 
    attr_reader :baz 

    def initialize(baz) 
    @baz = baz 
    end 
end 

class Bar 
    def initialize(baz) 
    @baz = baz 
    end 
end 

f = Foo.new(:test1) 
g = Foo.new(:test2) 
h = Bar.new(:test3) 
f.method(:baz).unbind.bind(g).call # => :test2 
f.method(:baz).unbind.bind(h).call # => TypeError: bind argument must be an instance of Foo 

Inizialmente, ho pensato che questo è incredibilmente impressionante, perché mi aspettavo che avrebbe funzionato in modo simile a di Function.prototype.call()/Function.prototype.apply() JavaScript. Tuttavia, l'oggetto a cui si desidera associare il metodo deve essere della stessa classe.

L'unica applicazione che riesco a pensare è se si disimpegna un metodo, si perde l'implementazione originale (ridefinire il metodo nella classe originale o singleton) e quindi ricollegarlo e chiamarlo.

+1

ho trovato una spiegazione ordinata qui - http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html –

+0

@WandMaker, idea pulito. Cade nella categoria che ho descritto. Sarei sorpreso se questa fosse l'unica ragione per la sua esistenza. – ndn

risposta

0

I metodi Method e UnboundMethod si aspettano che il target di collegamento debba essere sottoclasse della classe originale a cui è stato fatto riferimento al metodo. Tuttavia, il metodo ha implementato un metodo #to_proc e con questo è possibile eliminare il vincolo "stesso tipo di classe".

È necessario utilizzare il metodo #send, poiché #define_method è privato (non è possibile chiamarlo direttamente).

class A 
    def hoge ; "hoge" ; end 
end 

class B ; end 

hoge = A.new.method(:hoge) 

B.send(:define_method, :hoge_in_b, &hoge) #converting to proc 

b = B.new 
puts b.hoge_in_b 
+0

In primo luogo, questo valuterà il blocco nel contesto dell'oggetto originale (ovvero l'oggetto 'A.new', non quello di' B.new'). In secondo luogo, questo non risponde alla domanda su quale sia il punto della meccanica non vincolante. – ndn

+0

Ovviamente lo valuterà nel contesto di A.new, perché è un 'closure'.Non esiste un linguaggio di alto livello che usa le funzioni come oggetti di prima classe ma usa solo il 'nome della variabile 'e le valuta in un contesto diverso (a parte' eval'). In questo modo potresti usare una variabile nel contesto 'A' che non esiste nel contesto' B'. E il punto se la meccanica non funzionante? È come il punto delle implementazioni di "Proc, Lambda o Block" in Ruby. Fanno la stessa cosa, solo con piccole differenze. UnboundMethod è solo uno dei tanti strumenti, con le sue proprietà (e le sue carenze). – karatedog

+0

non è necessario che le chiusure contengano variabili di istanza. Blocchi e processi sono cose molto separate. Uno è una sintassi, l'altro è un oggetto reale. Per quanto riguarda i procs regolari vs lambda - Io uso entrambi spesso. Ciò significa che anche se le differenze sono sottili, sono sufficientemente significative in modo che ogni costrutto abbia una propria nicchia. Hai ragione che Ruby viene fornito con un sacco di meccaniche che una persona praticamente non userà mai nella sua base di codice di produzione. Tuttavia, anche con cose come gettare/prendere, fibre e variabili globali, posso almeno vedere da dove vengono. – ndn

2

Riassumo i buoni usi che ho trovato finora. Né utilizza unbind però.


primo luogo, sovrascrivendo un metodo, utilizzando la vecchia implementazione.Source, grazie a @WandMaker.

diciamo che si vuole fare qualcosa di simile:

class Foo 
    alias old_bar bar 

    def bar 
    old_bar 
    some_additional_processing 
    end 
end 

questo funzionerà, ma si lascerà alle spalle old_bar, che non è qualcosa di desiderabile. Invece, si potrebbe fare:

class Foo 
    old_bar = instance_method(:bar) 

    define_method(:bar) do 
    old_bar.bind(self).call 
    some_additional_processing 
    end 
end 

In secondo luogo, chiamando un'altra implementazione di un metodo da parte della gerarchia.Source.

L'esempio molto pratico fornito nel post è che spesso durante il debug, si desidera trovare dove è stato definito un metodo. In linea generale si può fare da:

method(:foo).source_location 

Tuttavia, questo non funziona se l'istanza corrente implementa un metodo method, come è il caso con ActionDispatch::Request. In questo caso si può fare:

Kernel.instance_method(:method).bind(self).call(:foo).source_location 
Problemi correlati