在 Rails 中实施 method_missing

Implement method_missing in Rails

我正在使用 OMDB API 了解如何在 Rails 中使用第 3 方 api。我已经设置了我的应用程序,所以我只需输入电影标题和 6 个其他属性,这些属性从 OMDB API 中填充。从 api 检索数据的所有方法调用都非常相似。唯一改变的是方法名中的一个词和方法中的一个词body。这是一个这样的调用:

app/services/omdb_service.rb

def get_image_by_title(title)
  response = HTTP.get("http://www.omdbapi.com/?t=#{title}&apikey=123456789").to_s
  parsed_response = JSON.parse(response)
  parsed_response['Poster']
end

改变的是方法名中get后面的词和parsed_response['Poster']中的词。它们会根据我要恢复的属性而改变。

我想我可以使用 method_missing 来防止重复,但我没有成功。这是我的 method_missing 电话:

app/services/omdb_service.rb

def method_missing(method, *args)
  if method.to_s.end_with?('_by_title')
    define_method(method) do | args |
      response = HTTP.get("http://www.omdbapi.com/?t=#{args[0]}&apikey=123456789").to_s
      parsed_response = JSON.parse(response)
      parsed_response['args[1]']
    end
  end
end

任何人都可以看到我的 method_missing 电话有什么问题吗?

首先,让我强调一下,这不一定是 method_missing 的好用例,因为似乎没有办法获得不言自明的方法名称、参数等。不过,我会尽量回答你的问题。

首先,你需要对API给你的东西采用你的方法命名,以减少参数的数量。在您给出的示例中,您希望将方法调用更改为 get_poster_by_t 因为 poster 是输出而 t 是基于 URL 的输入变量以及您分享的回复。

按照这个逻辑,你必须这样写 missing 方法:

def method_missing(method, *args)
  if method =~ /\Aget_([^_]+)_by_([^_]+)\z/
    response = HTTP.get("http://www.omdbapi.com/?#{$~[2]}=#{args[0]}&apikey=123456789").to_s
    parsed_response = JSON.parse(response)
    parsed_response[$~[1].capitalize]
  end
end

那么您还应该合并 Ruby 的规则来实现 method_missing,即当您的规则不匹配时调用 super 并覆盖 respond_to_missing?。这会给你:

def method_missing(method, *args)
  if method.to_s =~ /\Aget_([^_]+)_by_([^_]+)\z/
    response = HTTP.get("http://www.omdbapi.com/?#{$~[2]}=#{args[0]}&apikey=123456789").to_s
    parsed_response = JSON.parse(response)
    parsed_response[$~[1].capitalize]
  else
    super
  end
end

def respond_to_missing?(method, *args)
  method.to_s =~ /\Aget_([^_]+)_by_([^_]+)\z/ || super
end

另见 https://makandracards.com/makandra/9821-when-overriding-method_missing-remember-to-override-respond_to_missing-as-well


就我个人而言,我不会在这里使用 method_missing,而是使用富有表现力的方法调用——像这样:

def get_field_by_param(field:, param:, value:)
  response = HTTP.get("http://www.omdbapi.com/?#{param}=#{value}&apikey=123456789").to_s
  parsed_response = JSON.parse(response)
  parsed_response[field]
end

然后您可以执行类似 get_field_by_param(field: "Poster", param: :t, value: "Whatever") 的操作。