Utilizzando chiamate a metodi dinamici (#send
o #method
) la visibilità dei metodi 'viene ignorata.
C'è un modo semplice per chiamare dinamicamente un metodo che fallirà chiamando un metodo privato?come chiamare dinamicamente un metodo rispettando la privacy
risposta
Come so - è necessario metodo public_send:
----------------------------------------------------- Object#public_send obj.public_send(symbol [, args...]) => obj From Ruby 1.9.1 ------------------------------------------------------------------------ Invokes the method identified by _symbol_, passing it any arguments specified. Unlike send, public_send calls public methods only. 1.public_send(:puts, "hello") # causes NoMethodError
Pensavo che non capisco perché si desidera farlo, è possibile utilizzare eval
.
class Klass
private
def private_method(arg)
end
end
k = Klass.new
m = "private_method"
eval "k.#{m}('an arg')"
NoMethodError: private method `private_method' called for #<Klass:0x128a7c0>
from (irb):15
from (irb):15
Eval è davvero male È necessario verificare in anticipo, se m contiene veramente solo un nome di metodo. 'l =" inspect; \ 'rm -rf * \'; puts "" Allora l'attaker proverà a ingannarti pensando a qualcosa che ti assomiglia a un nome di metodo, ma in realtà non lo è. – johannes
Se si utilizza ruby-1.9, è possibile utilizzare Object#public_send
che fa quello che si vuole.
Se si utilizza rubino 1.8.7 o precedente si deve scrivere il proprio Object#public_send
class Object
def public_send(name, *args)
unless public_methods.include?(name.to_s)
raise NoMethodError.new("undefined method `#{name}' for \"#{self.inspect}\":#{self.class}")
end
send(name, *args)
end
end
Oppure si potrebbe scrivere il proprio Object#public_method
che si comporta come Object#method
ma solo per i metodi pubblici
class Object
def public_method(name)
unless public_methods.include?(name.to_s)
raise NameError.new("undefined method `#{name}' for class `#{self.class}'")
end
method(name)
end
end
È vero però, eval è davvero l'unico modo per farlo in realtà pre-1.9. Se vuoi saperne di più sulla visibilità, Jamis Buck ha scritto uno awesome article su che cosa significa in realtà la visibilità del metodo in Ruby.
Proprio come le altre cose in Ruby, la visibilità è sempre leggermente diversa dalle altre lingue.
Uso public_send
invece di send
:
my_object.public_send :method, *args
E 'nuovo a Ruby 1.9, quindi per anziani Ruby, è possibile require 'backports/1.9.1/kernel/public_send'
.
Se si vuole evitare eval
, send
o public_send
, o se si vuole better performance, utilizzare i public_method
method:
obj.public_method('my_method_name').call
È possibile aggiungere argomenti come questo:
obj.public_method('my_method_name').call('some_argument_1', 'some_argument_2')
questo non è disponibile in ruby 1.8.7 – johannes
In realtà, l'invio è sufficiente in 1.9 credo. Si dice che mandi le cure nel 1.9 ma __send__ no. Ma non l'ho confermato. –