I'm making a request to another server as part of a POST method to my Sinatra application. The library I'm using to make the request is an EventMachine library that immediately returns an EM::Deferrable object when a request is made, but I need to block in the controller method until the asynchronous request completes so I can return a partial with data returned in the request. What's the best approach for doing this?
One solution would be to use async_sinatra and an EM based webserver like Thin. With async_sinatra you would have a body method for explicit rendering. It would work like this:
require 'sinatra/async'
require 'em-http-request'
class Application < Sinatra::Base
register Sinatra::Async
apost '/' do
http = EM::HttpRequest.new('http://www.google.de/').get
http.callback do
body do
# your http processing in here, will be rendered
end
end
http.errback do
body { 'error' }
end
end
end
When you block on an evented API, you get worst of the two worlds.
I would try to avoid calls through EM in favor of more 'traditional' methods (a-la curl).
If this is not possible, then I would return an empty partial and have client poll the server for updates.
Related
I'm using savonrb (2.1.2) to perform my SOAP requests against a Web Service.
The problem I came into is that it looks like I do not have any chance to perform an HTTP GET request instead of a POST request using this library.
Please, note that even though I can agree with the fact that a SOAP over HTTP done through a GET method, instead that through a POST, might look unconventional or even an error, but
I cannot modify the server side, and, as client, I MUST accept this behavior as a matter of fact.
How can I overcome this problem?
Standing to what I've seen so far inside the code of savon, it looks like it is an immutable design decision:
# operation.rb
module Savon
class Operation
...
def call_with_logging(request)
#logger.log(request) { HTTPI.post(request, #globals[:adapter]) }
end
...
end
end
I just wonder if there should be a trick, through the mechanism of the savon adapters, to avoid this kind of (bad) solution.
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
What's the difference between the use and run methods in rackup files? It seems run is always at the end of config.ru, but it seems as if you should just be able to use use. Enlightening resources would also be very much appreciated.
use is for middlewares
class MyCustomMiddleware
def initialize(app)
#app = app
end
def call(env)
if condition
env['set-header'] = 'Middleware Can modify the response & pass it into next middleware'
end
#app.call(env)
end
run takes an argument that responds to call and returns a final Rack response with a HTTP Response code like 200.
class MyApp
def self.call(env)
[200, { "Content-Type" => "text/html" }, ["OK"]]
end
end
To understand the difference between use & run. lets see structure of typically rack app.
Typical Rack App
Rack app includes multiple middleware(s) which respond to call but do not return final rack response & an Object that responds to call which returns final rack response which includes HTTP response code(200,404, 500 etc). so typically there will be multiple objects which act as middlewares & then a object which returns final rack response with response code.
Difference between use & run
Now with this,
it seems can we can invoke use multiple times, once for each middleware & run only once in a single Rack App. so use will only invoke a middleware, while run will run the rack object which will return final rack response with HTTP status code.
example config.ru
use MyCustomMiddleware
use MyCustomMiddleware2
use MyCustomMiddleware3
run MyApp
In case if anything above is wrong, Let me know. So I can correct it.
I'm creating a Sinatra web application which I would like to serve mime type application/xhtml+xml for all web pages served. I am using Builder as template engine. Note that the application will also be serving application/xml for various AJAX api interfaces, also using the Builder template engine, as well as text/css using the scss template engine.
I notice that the webpages that get generated receive a text/html content type while the api interfaces get an application/xml content type instead. However, I don't know how the mime type is decided; presence of the html tag? usage of a layout or not? Whichever the case, I would like the application to issue a application/xhtml+xml mimetype instead of the text/html mime type.
I know I can specify the mime type with the content_type call, but I'd like to refrain from needing this in every routing function. Is there any way I can set a default mime type for a given template engine? Or can the mime type be controlled by the view's filename? Does Tilt provide any means to control mime type in a nice way?
Since your application is factored into separate objects, it should be straightforward to pull all of the AJAX routes into a separate Sinatra application. This will allow you to use Sinatra's after hook. The only requirement is for your AJAX calls to have something that identifies them as AJAX; for example, by setting a request header to a special value, or by using a specially formatted route. Here is an example rackup file of the unique route method:
require 'sinatra/base'
class MainApp
def get_info params
# return some data structure
end
end
class MainAppRoutes < Sinatra::Base
def initialize mainapp
super()
#mainapp = mainapp
end
get '/' do # main page
data = #mainapp.get_info(params)
# render response from data
end
end
class AjaxRoutes < Sinatra::Base
def initialize mainapp
super()
#mainapp = mainapp
end
get '/getinfo' do # handler for /ajax/getinfo
data = #mainapp.get_info(params)
# generate XML response from data
end
after do
content_type 'application/xml'
end
end
mainapp = MainApp.new
map '/ajax' do
run AjaxRoutes.new(mainapp)
end
run MainAppRoutes.new(mainapp)
In this example, all routes starting with /ajax are handled by instances of the AjaxRoutes class. The after hook ensures that the content type for those responses are 'application/xml'. The non-ajax routes are not affected.
A few things to remember:
Rack::Builder.map strips off the parts of the URL it matches. So a '/' route in AjaxRoutes would actually answer a request for '/ajax/'.
Always call super() in the initializers of your Sinatra-derived endpoint classes. For Sinatra middleware, pass the first argument up (Rack calls #new with the next rack app as the first parameter).
Review the doc about Sinatra filters. There is a potentially frustrating quirk about modifying responses with the after hook.
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.