Ruby:重构包含正则表达式的条件语句

时间:2013-05-17 13:35:26

标签: ruby testing module refactoring

我有一个方法mock_request,我为了单元测试目的而凌驾于此。对于每个请求,我想执行一些代码:

def mock_request(method, url, params={})
  case "#{method} #{url}"
  when 'post /customers'
    # create customer
  when %r{post /customers/(.*)/items}
    id = $1 # update customer
  when %r{post /customers/(.*)}
    id = $1 # update customer
  else
    throw 'Unrecognized request'
  end
end

问题是这个案例陈述变得很长,每个案例的行数都在缓慢增加。此外,一些模型正在同时处理。

我想要做的是为每个模型提供单独的文件,并以某种方式包含它们以用于此mock_request方法。像这样:

# handlers/customers.rb
module RequestHandlers
  # post /customers
  # post /customers/(.*)
end

# handlers/items.rb
module RequestHandlers
  # post /customers/(.*)/items
end

# mock_request.rb
class MockServer
  def mock_request(method, url, params={})
    handle_request(method, url)
  end
end

但是我不确定如何实现这一点。在红宝石中处理这个问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

您可以创建一个哈希,其中包含方法和URL字符串作为您的键,以及您要作为值评估的代码块。

def mock_request(method, url, params={})
 #create a request hash where the default value is your else, this way if a key that doesn't exist is called, the unrecognized request is called

  req =  Hash.new ({throw 'Unrecognized request'})
  req['post /customers'] = {#create customer block/proc}
  req[%r{post /customers/(.*)/items}] = {#update customer block/proc}

  req["#{method} #{url}"]
end

对于您来说,这可能是一个更好的选择,并且可以摆脱冗长的案例陈述。

答案 1 :(得分:0)

为了记录,这是我最终如何重构它。

处理程序的每个“主题”都有自己的模块和文件:

module RequestHandlers
  module Customers

    def Customers.included(mock_server)
      mock_server.add_handler 'post /customers',      :new_customer
      mock_server.add_handler 'post /customers/(.*)', :update_customer
    end

    def new_customer(route, method_url, params)
      # create new customer
    end

    def update_customer(route, method_url, params)
      # update existing customer
    end

  end
end

主要类是处理程序生存和使用的地方:

class MockServer

  # Handlers are ordered by priority
  @@handlers = []

  include RequestHandlers::Customers
  include RequestHandlers::Items
  # etc.

  def self.add_handler(route, name)
    @@handlers << {
      :route => %r{^#{route}$},
      :name => name
    }
  end

  def mock_request(method, url, params={})
    method_url = "#{method} #{url}"
    handler = @@handlers.find {|h| method_url =~ h[:route] }

    if handler
      self.send(handler[:name], handler[:route], method_url, params)
    else
      throw 'Unrecognized request'
    end
  end
end