How do I add custom logging for which route is satisfying a request in Sinatra? - ruby

I'm working on a Sinatra app that has a bunch of routes of all sorts. I'd like to add some custom logging that logs the params of the get or post call that ends up generating the response for the request. I realize I could subclass the get/post definition to wrap the block with a logging call. But I suspect there is a more appropriate approach.

You can use Sinatra's before hook in your controller, and print out some information contained on the request
before do
if request.request_method == :get || request.request_method == :post
puts request.path_info, params.inspect # check out the request variable for more info you might like to ouput
end
end

Related

Internal redirection in Padrino

Is there a way to internally redirect in Padrino? I am writing a RESTful service, no HTML response of browser client.
I have a resource, lets say, xyz.
MyApp.controllers :xyz
I have two routes in a controller:
put :index, :with => :xyz_id do...end
and
get :show, :map => '/xyz/:xyz_id' do...end
Now to simplify (and centralize) view (which is a JSON document) creation, I want to just internally redirect the control so that it processes the :show method after creating the resource. Hence, for a client of the service, PUT /xyz/1234 will create a new resource and return the same and GET /xyz/1234 will return the resource if it exists.
Is there a way to INTERNALLY (not a 302 response sent to the client) redirect to get :show method from the put :index method (after creating the resource)? Something like:
redirect (:xyz, :index, {:xyz_id => '1234'})
First of, you can put logic behind the showing data into separate function that you can call from both GET and PUT routes. If you really want to pass processing to a different route, you can do it with rack's call method:
put '/foo' do
# putting related stuff
call env.merge('REQUEST_METHOD' => 'GET')
end

Handling 405's in Sinatra

In Sinatra if I create a simple endpoint such as:
post '/users' do
'posted'
end
curl -v -X GET http://localhost:8080/users returns a 404, when I would expect a 405.
I've looked through the documentation but not found anything. Before digging through the source code, does anyone know how to handle and return 405's in Sinatra? Sort of like the not found method:
not_found do
'Not found - ' + request.path
end
You got an 404 because sinatra can't find a get route /users. If you want return a custom error you may look at Halt
throw a error
The you can return a 405 on get /users like this:
get "/users" do
halt 405
end
For catch multiple http verbs at once you can use multi route
require 'sinatra'
require "sinatra/multi_route"
route :get, :post, '/foo' do
# "GET" or "POST"
p request.env["REQUEST_METHOD"]
end
# Or for module-style applications
class MyApp < Sinatra::Base
register Sinatra::MultiRoute
route :get, :post, '/foo' do
# ...
end
end
source
handle errors
If you want handle error codes in sinatra you can simple do for 404 errors:
not_found do
'This is nowhere to be found.'
end
In your case handle a 405 error:
error 405 do
'Access forbidden'
end
Looks like it's not possible without repeating yourself a lot. From looking through the Sinatra source code, the hash of routes has the verb as the key: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1513
It then looks up a route using that verb:
https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L943
Which is not ideal and I would consider a weakness/bug in the framework as throwing a 405 when a verb isn't allowed is standard HTTP Spec.
I'll maybe raise an issue see what the contributors say. Ideally it would store routes by their url first, and then check the appropriate verb can be executed for a given url. This would make handling something as standard as a 405, much easier.
In fact I found an issue raised on github for the above:
https://github.com/sinatra/sinatra/issues/24
As mentioned nearer the bottom, it's not currently handled and something they may work on for v2.0

Sinatra, retrieve only post parameters

I know I can retrieve every parameter in Sinatra using "params".
However, How can I retrieve only post parameters? I need to verify that a parameter was sent by POST, otherwise it should be ignored.
Thanks.
Accessing the request object directly gives you a simple way of accessing post data, much like the php $_POST variable:
post '/' do
request.POST.inspect # instead of params.inspect
end
More info on the Rack request object here: http://rack.rubyforge.org/doc/classes/Rack/Request.html#M000274

Sinatra app that redirects POST/GET requests with parameters

I'm migrating servers but unfortunately the old server IP is hardcoded inside my iPhone app. Obviously I'm going to submit an update that sets the API endpoint to my new server, but in the meantime I need to setup an app on the old server that redirects all the requests to the new server. I've heard Sinatra would be perfect for this.
require 'sinatra'
get "/foo/bar" do
redirect "http://new-server.com/foo/bar", 303
end
post "/foo/bar" do
redirect "http://new-server.com/foo/bar", 303
end
The problem is that these do not forward the GET or POST parameters along with the request. I read on the Sinatra doc that you can do that by putting them in the URL directly (works for GET requests), or by setting session variables.
Is manually parsing and formatting the GET params to put them back into the redirect URL the only way to go for GET redirects? How are you supposed to forward POST parameters?
For GET requests, use request.fullpath or request.query_string. For POST request, use status code 307, so the subsequent request will be a POST with the same parameters.
helpers do
def new_url
"http://new-server.com" + request.fullpath
end
end
get "/foo/bar" do
redirect new_url
end
post "/foo/bar" do
redirect new_url, 307
end
I would overload the Hash class in lib/overload_hash.rb, like so:
class Hash
def to_url_params
elements = []
keys.size.times do |i|
elements << "#{keys[i]}=#{values[i]}"
end
elements.join('&')
end
end
EDIT (Better solution using net / http)
Place a require "lib/overload_hash", require "net/http" and require "uri" under require 'sinatra'. The following example can be translated into GET easily.
post '/foo/bar' do
uri = URI.parse("http://example.com/search")
response = Net::HTTP.post_form(uri, params.to_ur_params)
response
end

Ruby Sinatra: Can I update a view template multiple times within one client request

I want to do this:
get '/test' do
#dog = 'WOOF'
erb :test
sleep(1)
#dog = 'BOWWOW'
erb :test
sleep(1)
#dog = 'ARF'
erb :test
end
Is it possible to do something like this where the client sees each update or no, I've tried but can't get it to work.
In short: no.
I think you're confusing the way HTTP works. First, HTTP is stateless. This means that multiple requests know nothing about each other (this is mitigated by the use of sessions via cookies, or possibly HTTP basic auth).
Further, you cannot resend the HTTP body like you're doing. Once it's sent there's no going back. Techniques like long-polling delay sending the body so they can send it whenever they like, but once they send something the request is complete and a new one must be started. Thus, once you've rendered the body once via erb, you're request is finished.
What it seems like you're trying to achieve can only be done via Javascript with AJAX, or with completely separate full-page requests.

Resources