2013-04-07 10 views
12

dire che ho un oggetto con un metodo che accede ad un oggetto:Rubino annidato inviare

def foo 
    @foo 
end 

So che posso usare invia ad accedere a tale metodo:

obj.send("foo") # Returns @foo 

C'è un modo semplice per si fa un invio ricorsivo per ottenere un parametro sull'oggetto @pippo, come:

obj.send("foo.bar") # Returns @foo.bar 
+3

'obj.send ('foo'). Send ('bar')' –

+0

@SergioTulentsev - Scusa dovrei aver chiarito - C'è un modo per fallo in un solo passaggio. Quello che stai descrivendo richiede l'analisi della stringa "foo.bar" per rendersi conto che è necessario chiamare send() in modo ricorsivo. Mi stavo chiedendo se esistesse un meccanismo esistente che lo faccia automaticamente o più chiaramente. – Lynn

+1

@Lynn: non sono a conoscenza di tale meccanismo integrato. –

risposta

18

È possibile utilizzare instance_eval:

obj.instance_eval("foo.bar") 

È anche possibile accedere alla variabile di istanza direttamente:

obj.instance_eval("@foo.bar") 
+1

molto confuso in '" @ foo.bar' cosa è 'bar'? Dove è definito? –

+1

È un metodo' bar' chiamato sull'oggetto memorizzato in '@ foo'. È esattamente lo stesso come se lo aveste digitato in un metodo di istanza di '@ foo'. Consulta la documentazione –

+2

' instance_eval' di una stringa è pericoloso, costoso e molto raramente chiamato. – dbenhur

13

Mentre OP ha già accettato una risposta utilizzando instance_eval(string), io suggerisco vivamente OP per evitare forme di stringa di eval se non assolutamente necessario. Eval invoca il compilatore di rubini: è costoso da calcolare e pericoloso da utilizzare in quanto apre un vettore per gli attacchi di iniezione di codice.

quanto affermato non c'è alcuna necessità di inviare a tutti:

obj.foo.bar 

Se, infatti, i nomi di foo e bar sono provenienti da qualche calcolo non statico, quindi

obj.send(foo_method).send(bar_method) 

è semplice e tutto ce n'è bisogno per questo

Se i metodi provengono sotto forma di una stringa tratteggiata, si può usare dividere e iniettare a catena metodi:

'foo.bar'.split('.').inject(obj, :send) 

chiarendo in risposta alle osservazioni: String eval è uno dei più rischioso uno cose può fare da una prospettiva di sicurezza. Se c'è un modo in cui la stringa viene costruita dall'input fornito dall'utente senza un'ispezione e una convalida incredibilmente diligenti di quell'input, dovresti semplicemente considerare il tuo sistema di proprietà.

send (metodo) in cui il metodo è ottenuto dall'input dell'utente presenta anche dei rischi, ma esiste un vettore di attacco più limitato. L'input dell'utente può causare l'esecuzione di qualsiasi metodo 0-argument dispacciabile tramite il ricevitore. La buona pratica qui sarebbe di whitelist sempre i metodi prima della spedizione:

VALID_USER_METHODS = %w{foo bar baz} 
def safe_send(method) 
    raise ArgumentError, "#{method} not allowed" unless VALID_USER_METHODS.include?(method.to_s) 
    send(method) 
end 
+0

Una catena di chiamate 'send' basate su stringhe non è molto più sicura dell'uso' eval'. –

+0

Sono d'accordo con entrambi i sentori sicurezza.Maho, avrei whitelist quello che potrebbe essere chiamato sia per ragioni di sicurezza esplicite e maitenenza per gli sviluppatori.State creando un buco del lavandino quindi state attenti intorno ad esso – timpone

+0

@dbenhur - To Michael's commento - non vengono inviati e iniettati tanto quanto eval in termini di rischio per la sicurezza? O uno è preferibile rispetto all'altro? – Lynn

1

Un po 'in ritardo alla festa, ma ho dovuto fare qualcosa di simile che ha dovuto combinare sia 'invio' e l'accesso ai dati da un hash/array in una sola chiamata.Fondamentalmente questo ti permette di fare qualcosa di simile a quanto segue

value = obj.send_nested("data.foo['bar'].id")

e sotto il cofano questo farà qualcosa di simile a

obj.send(data).send(foo)['bar'].send(id)

Questo funziona anche con i simboli nella stringa di attributo

value = obj.send_nested('data.foo[:bar][0].id')

che farà qualche cosa simile a

obj.send(data).send(foo)[:bar][0].send(id)

Nel caso in cui si desidera utilizzare l'accesso indifferente è possibile aggiungere che come parametro pure. Per esempio.

value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)

Dal momento che è un po 'più complesso, here is the link to the gist che è possibile utilizzare per aggiungere che il metodo alla base di Ruby oggetto. (Include anche i test in modo da poter vedere come funziona)