如何 DRY ruby 中仅一行代码不同的函数列表?

How to DRY a list of functions in ruby that are differ only by a single line of code?

我在 ROR 应用程序中有一个用户模型,它有多个像这样的方法

#getClient() returns an object that knows how to find certain info for a date
#processHeaders() is a function that processes output and updates some values in the database
#refreshToken() is function that is called when an error occurs when requesting data from the object returned by getClient()


  def transactions_on_date(date)
    if blocked?
      # do something
    else
      begin
        output = getClient().transactions(date)
        processHeaders(output)
        return output
      rescue UnauthorizedError => ex
        refresh_token()
        output = getClient().transactions(date)
        process_fitbit_rate_headers(output)
        return output
      end
    end
  end

  def events_on_date(date)
    if blocked?
      # do something
    else
      begin
        output = getClient().events(date)
        processHeaders(output)
        return output
      rescue UnauthorizedError => ex
        refresh_token()
        output = getClient().events(date)
        processHeaders(output)
        return output
      end
    end
  end

我的用户 class 中有几个功能看起来完全一样。这些函数之间的唯一区别是 output = getClient().something(date) 行。有什么方法可以让这段代码看起来更简洁,这样我就没有重复的函数列表了。

是的,您可以使用 Object#sendgetClient().send(:method_name, date)

顺便说一句,getClient 不是正确的 Ruby 方法名称。应该是 get_client.

答案通常是传入一个块并以函数式的方式进行:

def handle_blocking(date)
  if blocked?
   # do something
  else
    begin
      output = yield(date)
      processHeaders(output)

      output
    rescue UnauthorizedError => ex
      refresh_token
      output = yield(date)
      process_fitbit_rate_headers(output)

      output
    end
  end
end

那你这样称呼它:

handle_blocking(date) do |date|
  getClient.something(date)
end

这允许进行大量自定义。 yield 调用执行您提供的代码块并将 date 参数传递给它。

干燥代码的过程通常涉及寻找模式并将它们归结为像这样的有用方法。使用函数式方法可以保持整洁。

两个答案的组合如何:

class User

  def method_missing sym, *args
    m_name = sym.to_s
    if m_name.end_with? '_on_date'
      prop = m_name.split('_').first.to_sym
      handle_blocking(args.first) { getClient().send(prop, args.first) }
    else
      super(sym, *args)
    end
  end

  def respond_to? sym, private=false
    m_name.end_with?('_on_date') || super(sym, private)
  end

  def handle_blocking date
    # see other answer
  end

end

然后你可以调用 "transaction_on_date", "events_on_date", "foo_on_date" 就可以了。