Within a Sinatra web app, how can I make a virtual request to the application and get the response body back as text? For example, these routes...
get('/foo'){ "foo" }
get('/bar'){ "#{spoof_request '/foo'} - bar" }
...should result in the response "foo - bar" when requesting "/bar" with the web browser.
My application has a page representing an bug entry, with lots of details about that bug entry: what version was the bug experienced in, how important is it, what tags are associated with it, to whom is the bug assigned, etc.
The user may edit individual pieces of data on this page interactively. Using my AJAXFetch jQuery plugin, JavaScript uses AJAX to swap out a read-only section of the page (e.g. the name of the person that this bug is assigned to) with an HTML partial form for editing just that section. The user submits the form, and AJAX makes a new request for the static version of that field.
In order to be DRY, I want the Haml view that creates the page to use the exact same request that AJAX makes when creating the individual static pieces. For example:
#notifications.section
%h2 Email me if someone...
.section-body= spoof_request "/partial/notifications/#{@bug.id}"
The following helper defining spoof_request
worked under Sinatra 1.1.2:
PATH_VARS = %w[ REQUEST_PATH PATH_INFO REQUEST_URI ]
def spoof_request( uri, headers=nil )
new_env = env.dup
PATH_VARS.each{ |k| new_env[k] = uri.to_s }
new_env.merge!(headers) if headers
call( new_env ).last.join
end
Under Sinatra 1.2.3, however, this no longer works. Despite setting each of the PATH_VARS
to the desired URI, the call( new_env )
still causes Sinatra to process the route for the current request, not for the specified path. (This results in infinite recursion until the stack level finally bottoms out.)
This question differs from Calling Sinatra from within Sinatra because the accepted answer to that (old) question does not maintain the session of the user.
The following appears to work as needed under Sinatra 1.2.3:
ENV_COPY = %w[ REQUEST_METHOD HTTP_COOKIE rack.request.cookie_string
rack.session rack.session.options rack.input]
# Returns the response body after simulating a request to a particular URL
# Maintains the session of the current user.
# Pass custom headers if you want to set or change them, e.g.
#
# # Spoof a GET request, even if we happen to be inside a POST
# html = spoof_request "/partial/assignedto/#{@bug.id}", 'REQUEST_METHOD'=>'GET'
def spoof_request( uri, headers=nil )
new_env = env.slice(*ENV_COPY).merge({
"PATH_INFO" => uri.to_s,
"HTTP_REFERER" => env["REQUEST_URI"]
})
new_env.merge!(headers) if headers
call( new_env ).last.join
end
where Hash#slice
is defined as:
class Hash
def slice(*keys)
{}.tap{ |h| keys.each{ |k| h[k] = self[k] } }
end
end
It feels like I'm missing what you are trying to do but why not just call the defined method?
get('/foo'){ "foo" }
get('/bar'){ "#{self.send("GET /foo")} - bar" }
That is one funky method name by the way. Don't ask me why it's even allowed.
PS. This only works pre version 1.2.3. DS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With