Come funziona instance_eval e perché DHH lo odia?

Verso le 19:00 nella sua presentazione RailsConf , David Heinemeier Hansson parla degli aspetti negativi di instance_eval :

Per molto tempo ho parlato e istigato contro instance_eval , che è il concetto di non usare un parametro ceduto (come do |people| ) e semplicemente do something e poi valutare cosa c’è in quel blocco nell’ambito della tua provenienza (I non so nemmeno se questa è una spiegazione coerente)

Per molto tempo non mi è piaciuto, perché in un certo senso sembrava più complesso. Se volevi inserire il tuo codice, avresti triggersto qualcosa che era già lì? Stavi per scavalcare qualcosa? Quando produci una variabile specifica puoi mettere tutto in ordine e sai che non stai scherzando con le cose di qualcun altro

Sembrava interessante, ma a) Non so come funziona instance_eval in primo luogo eb) Non capisco perché può essere cattivo / aumentare la complessità.

Qualcuno può spiegare?

La cosa che instance_eval fa è che esegue il blocco nel contesto di un’istanza diversa. In altre parole, cambia il significato di self che significa che cambia il significato dei metodi di istanza e delle variabili di istanza.

Ciò crea una disconnessione cognitiva: il contesto in cui viene eseguito il blocco non è il contesto in cui appare sullo schermo.

Permettetemi di dimostrarlo con una leggera variazione dell’esempio di @Matt Briggs. Diciamo che non stiamo costruendo un modulo, stiamo costruendo un’e-mail:

 def mail builder = MailBuilder.new yeild builder # executed after the block # do stuff with builder end mail do |f| f.subject @subject f.name name end 

In questo caso, @subject è una variabile di istanza dell’object e il name è un metodo della class. È ansible utilizzare una buona decomposizione orientata agli oggetti e archiviare il sobject in una variabile.

 def mail &block builder = MailBuilder.new builder.instance_eval &block # do stuff with builder end mail do subject @subject name name # Huh?!? end 

In questo caso, @subject è una variabile di istanza dell’object mail builder ! Potrebbe anche non esistere ! (O ancora peggio, potrebbe esistere e contenere qualche valore completamente stupido.) Non c’è modo per te di ottenere l’accesso alle variabili di istanza del tuo object. E come si chiama anche il metodo del name del proprio object? Ogni volta che provi a chiamarlo, ottieni il metodo del builder della posta .

In sostanza, instance_eval rende difficile l’utilizzo del proprio codice all’interno del codice DSL. Quindi, dovrebbe essere usato solo nei casi in cui ci sono poche possibilità che questo possa essere necessario.

Ok, quindi l’idea qui è invece di qualcosa di simile

 form_for @obj do |f| f.text_field :field end 

ottieni qualcosa come questo

 form_for @obj do text_field :field end 

la prima è abbastanza semplice, si finisce con uno schema simile a questo

 def form_for b = FormBuilder.new yield b b.fields.each |f| # do stuff end end 

produci un object builder sul quale il consumatore chiama i metodi, dopodiché chiami metodi sull’object builder per creare effettivamente il modulo (o qualsiasi altra cosa)

il secondo è un po ‘più magico

 def form_for &block b = FormBuilder.new b.instance_eval &block b.fields.each |f| #do stuff end end 

in questo, invece di cedere il builder al blocco, prendiamo il blocco e lo valutiamo nel contesto del builder

Il secondo aumenta la complessità perché si è in una sorta di giochi con scope, è necessario comprenderlo e il consumatore deve capirlo, e chiunque abbia scritto il builder deve comprenderlo. Se tutti sono sulla stessa pagina, non so che è nessicamente una brutta cosa, ma io metto in dubbio i vantaggi rispetto ai costi, cioè quanto è difficile attaccare solo su un f. di fronte ai tuoi metodi?

L’idea è che è un po ‘pericoloso in quanto non si può mai essere sicuri che non si possa rompere qualcosa senza leggere tutto il codice relativo all’object su cui si sta utilizzando instance_eval.

Inoltre, se tu, ad esempio, aggiorni una libreria che non ha cambiato l’interfaccia, ma ha modificato molti degli interni dell’object, potresti davvero fare dei danni.