如何以可测试的方式在Rails中路由之前修改请求对象?

时间:2014-07-23 13:29:26

标签: ruby-on-rails ruby ruby-on-rails-3

所以,我有一种情况,我需要在将请求发送到任何路由之前确定一些请求。目前,这是使用几个全部命中数据库的约束来实现的,我想将数据库命中减少到一个。不幸的是,在routes.rb中内联它不起作用,因为routes.rb中的局部变量不会在请求之间刷新;所以,如果我这样做:

# Database work occurs here, and is then used to create comparator lambdas.
request_determinator = RequestDeterminator.new(request)

constraint(request_determinator.lambda_for(:ninja_requests)) do
  # ...
end

constraint(request_determinator.lambda_for(:pirate_requests)) do
  # ...
end

这在第一个请求上运行良好,但随后的请求将被路由为第一个请求。 (D'哦。)

我的下一个想法是编写一个Rack中间件来向env哈希添加“确定器”,但是这有两个问题:首先,它似乎根本没有粘在哈希中,规格甚至没有经过Rack中间件,所以没有办法真正测试它。

我是否有一个简单的机制我可以插入,例如,ActionDispatch的一个钩子可以向请求添加内容,或者只是对Rails路由说:“在路由之前执行此操作?”

我正在使用Rails 3.2和Ruby 1.9。

2 个答案:

答案 0 :(得分:2)

执行此操作的一种方法是将您的决定程序存储在请求的env对象上(自ActionDispatch::RequestRack::Request的子类以来):

class RequestDeterminator
  def initialize(request)
    @request = request
  end

  def self.for_request(request)
    request.env[:__determinator] ||= new(request)
  end

  def ninja?
    query_db
    # Verify ninjaness with @request
  end

  def pirate?
    query_db
    # Verify piratacity with @request
  end

  def query_db
    @result ||= begin
      # Some DB lookup here
    end
  end
end

constraint lambda{|req| RequestDeterminator.for_request(req).ninja? } do
  # Routes
end

constraint lambda{|req| RequestDeterminator.for_request(req).pirate? } do
  # Routes
end

这样,您只需实例化一个确定器,它可以跨越约束检查缓存您的数据库请求。

答案 1 :(得分:0)

如果您真的要拦截请求,请尝试使用机架,因为它是第一个在任何Rails应用程序中处理请求的机器...请参阅http://railscasts.com/episodes/151-rack-middleware以了解机架如何工作....

希望它有所帮助。