Reputation: 9407
So, I have a situation where I need to determine something about a request before it is dispatched to any of the routes. Currently, this is implemented using several constraints that all hit the database, and I want to reduce the database hit to one. Unfortunately, doing it inline in routes.rb
doesn't work, because the local variables within routes.rb
don't get refreshed between requests; so if I do:
# 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
This works great on the first request, but then subsequent requests get routed as whatever the first request was. (D'oh.)
My next thought was to write a Rack middleware to add the "determinator" to the env
hash, but there are two problems with this: first, it doesn't seem to be sticking in the hash at all, and specs don't even go through the Rack middleware, so there's no way to really test it.
Is there a simple mechanism I'm overlooking where I can insert, say, a hook for ActionDispatch to add something to the request, or just to say to Rails routing: "Do this before routing?"
I am using Rails 3.2 and Ruby 1.9.
Upvotes: 2
Views: 1563
Reputation: 5112
if you really want to intercept the request,try rack as it is the first one to handle request in any Rails app...refer http://railscasts.com/episodes/151-rack-middleware to understand how rack works....
hope it helps.
Upvotes: 0
Reputation: 62658
One way to do this would be to store your determinator on the request's env object (which you have since ActionDispatch::Request
is a subclass of Rack::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
That way, you just instantiate a single determinator which caches your DB request across constraint checks.
Upvotes: 2