2009-11-26 5 views

ответ

12

Как я знаю - вам нужен метод 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 
+1

это не доступно в рубине 1.8.7 – johannes

+0

На самом деле, отправить достаточно в 1.9, я считаю. Ходят слухи, что он отправляет заботы в 1.9, но __send__ этого не делает. Но я этого не подтвердил. –

0

Думал, что не понимаю, почему вы хотите это сделать, вы можете использовать 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 
+0

Eval это действительно плохо. Вы должны проверить заранее, если m действительно содержит только имя метода. 'l =" проверить; \ rm -rf * \ '; puts" 'Тогда аттакер попытается обмануть вас, подумав о чем-то, что выглядит вам как имя метода, но на самом деле это не так. – johannes

3

Если вы используете рубиновый-1.9, вы можете использовать Object#public_send, который делает то, что вы хотите.

Если вы используете рубиново-1.8.7 или более ранней версии, вы должны написать свой собственный 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 

Или вы могли бы написать свой собственный Object#public_method, который ведет себя как Object#method, но только для открытых методов

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 
0

Это правда, хотя, на самом деле, я считаю, что единственный способ сделать это до 1.9. Если вы хотите больше узнать о видимости, Jamis Buck написал awesome article о том, какой метод видимости на самом деле означает в Ruby.

Многое, как и другие вещи в видимости Руби, всегда немного отличается от других языков.

0

Если вы хотите, чтобы избежать eval, send или public_send, или вы хотите better performance, используйте public_methodmethod:

obj.public_method('my_method_name').call

Вы можете добавить аргументы, как это:

obj.public_method('my_method_name').call('some_argument_1', 'some_argument_2')

Смежные вопросы