开发者

how to dynamically call a method while respecting privacy

Using d开发者_如何学Goynamic method calls (#send or #method) the methods' visibility is ignored.

Is there a simple way to dynamically call a method that will fail calling a private method?


As I know - you need method 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


Use public_send instead of send:

my_object.public_send :method, *args

It's new to Ruby 1.9, so for older Ruby, you can require 'backports/1.9.1/kernel/public_send'.


If you are using ruby-1.9, you can use Object#public_send which does what you want.

If you use ruby-1.8.7 or earlier you have to write your own 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

Or you could write your own Object#public_method which behaves like Object#method but only for public methods

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


Thought I don't understand why you want to do so, you can use 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


It's true though, eval really is I think the only way to actually do it pre-1.9. If you want to read more about visibility Jamis Buck wrote an awesome article about what method visibility actually means in Ruby.

Much like other things in Ruby visibility is ever so slightly different than other languages.


If you want to avoid eval, send or public_send, or you want better performance, use the public_method method:

obj.public_method('my_method_name').call

You can add arguments like this:

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜