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 (comedo |people|
) e semplicementedo something
e poi valutare cosa c’è in quel blocco nell’ambito della tua provenienza (I non so nemmeno se questa è una spiegazione coerente)
- Scopo di "consider_all_requests_local" in config / environments / development.rb?
- Parametri delle rotaie spiegate?
- Che cosa significa rake db: test: preparare effettivamente fare?
- Come ottenere il percorso del referente delle richieste?
- Parametri non specificati che aggiungono nuovi campi a Devise in rail 4.0
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.