首页 > 解决方案 > Ruby 动态方法调用的问题

问题描述

我正在 Elasticsearch 的 Ruby API 之上构建一些东西,并且有许多几乎相同的函数的常见 API 问题,这些函数实现对底层 API 的调用并执行错误处理。所以我想要一种方法来执行与调用底层 API 相关的杂乱内容:

 def perform_api_request( method, params ) 

  api_method = "#{@method_prefix}#{method}"
  puts api_method 
  begin 
    r = @es_conn.client.send(api_method, params )
  rescue => e 
    abort "there were errors calling #{method}:#{e}" unless extract_error(e) 
    return false 
  end 
  return true 
end 

我正在调用:

      perform_api_request 'delete_template', { name: @name }

这输出:

indices.delete_template 
there were errors calling delete_template:undefined method \
   `indices.delete_template' for #<Elasticsearch::Transport::Client:0x0000000282d758>

我已经尝试过self.send各种其他方法,但我无法让它发挥作用。我所有的努力都导致了“未定义的方法”。

有人可以指出我缺少什么吗?

标签: rubydynamic

解决方案


你可以这样做:

r = api_method.split('.').inject(@es_conn.client) { |obj, m| obj.public_send(m) }

如果你想支持任意方法链。这将允许您在@method_prefix和中需要任何点method。如果您也想允许私有方法,那么:

r = api_method.split('.').inject(@es_conn.client) { |obj, m| obj.send(m) }

这假设您正在控制一切,并且不允许在@method_prefixor中进行任何类型的外部输入method;如果您允许外部输入,那么您真的希望将所有内容列入白名单以避免调用意外和危险的方法。

编辑:如果您需要将参数传递给最终调用,您需要执行以下操作:

api_method = "#{@method_prefix}#{method}".split('.')
last  = api_method[-1]
r = api_method.inject(@es_conn.client) do |obj, m| 
  m == last ? obj.public_send(m, params) : obj.public_send(m) 
end

将参数添加到最终调用中。可能有一种更优雅的方式来做到这一点;)


推荐阅读