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

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.

Related

How to perform a savon client.call use a GET request with a payload instead of a POST?

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.

Grab Facebook signed_request with Sinatra

I'm trying to figure out whether or not a user likes our brand page. Based off of that, we want to show either a like button or some 'thank you' text.
I'm working with a sinatra application hosted on heroku.
I tried the code from this thread: Decoding Facebook's signed request in Ruby/Sinatra
However, it doesn't seem to grab the signed_request and I can't figure out why.
I have the following methods:
get "/tab" do
#encoded_request = params[:signed_request]
#json_request = decode_data(#encoded_request)
#signed_request = Crack::JSON.parse(#json_request)
erb :index
end
# used by Canvas apps - redirect the POST to be a regular GET
post "/tab" do
#encoded_request = params[:signed_request]
#json_request = decode_data(#encoded_request)
#signed_request = Crack::JSON.parse(#json_request)
redirect '/tab'
end
I also have the helper messages from that thread, as they seem to make sense to me:
helpers do
def base64_url_decode(payload)
encoded_str = payload.gsub('-','+').gsub('_','/')
encoded_str += '=' while !(encoded_str.size % 4).zero?
Base64.decode64(encoded_str)
end
def decode_data(signed_request)
payload = signed_request.split('.')
data = base64_url_decode(payload)
end
end
However, when I just do
#encoded_request = params[:signed_request]
and read that out in my view with:
<%= #encoded_request %>
I get nothing at all.
Shouldn't this return at least something? My app seems to be crashing because well, there's nothing to be decoded.
I can't seem to find a lot of information about this around the internet so I'd be glad if someone could help me out.
Are there better ways to know whether or not a user likes our page? Or, is this the way to go and am I just overlooking something obvious?
Thanks!
The hint should be in your app crashing because there's nothing to decode.
I suspect the parameters get lost when redirecting. Think about it at the HTTP level:
The client posts to /tab with the signed_request in the params.
The app parses the signed_request and stores the result in instance variables.
The app redirects to /tab, i.e. sends a response with code 302 (or similar) and a Location header pointing to /tab. This completes the request/response cycle and the instance variables get discarded.
The client makes a new request: a GET to /tab. Because of the way redirects work, this will no longer have the params that were sent with the original POST.
The app tries to parse the signed_request param but crashes because no such param was sent.
The simplest solution would be to just render the template in response to the POST instead of redirecting.
If you really need to redirect, you need to carefully pass along the signed_request as query parameters in the redirect path. At least that's a solution I've used in the past. There may be simpler ways to solve this, or libraries that handle some of this for you.

How can I block on an EventMachine deferrable object?

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.

Parse body of POST reqest in self-made server in Ruby

I am writing mock Ruby servers to test components of API. I send a POST request with a body, and I'd like my mock server to return a body of that POST request. Currently i have this code:
require 'socket'
webserver = TCPServer.new('127.0.0.1', 7125)
loop do
session = webserver.accept
session.print "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
request = session.gets
session.puts request
session.close
end
A POST request with body FOO returns a response with body that contains only POST / HTTP/1.1 How to fix it?
Writing your own HTTP server is really going to get you into trouble because the specification, while superficially simple, has a number of subtle nuances that can trip you up. In this case, you're reading one line with gets and ignoring the bulk of the submission. You're going to have to address that by reading in and properly decoding the posted data.
For something with a familiar interface you might start with Net::HTTP::Server instead.

Using Cookies with Rack::Test

I'm trying to write RSpec tests for my Sinatra application using Rack::Test. I can't understand how I can use cookies. For example if my application set cookies (not via :session) how can I check whether that cookie is properly set?
Also, how can I send requests with that cookie?
Rack::Test keeps a cookie jar that persists over requests. You can access it with rack_mock_session.cookies. Let's say you have a handler like this:
get '/cookie/set' do
response.set_cookie "foo", :value => "bar"
end
Now you could test it with something like this:
it 'defines a cookie' do
get '/'
rack_mock_session.cookie_jar["foo"].should == "bar"
end
You can also access cookies with last_request.cookies, but as the name says, it contains the cookies for the last request, not the response. You can set cookies with set_cookie and clear them with clear_cookies.
it 'shows how to set a cookie' do
clear_cookies
set_cookie "foo=quux"
get '/'
last_request.cookies.should == {"foo" => "quux"}
end
Update: If you want the cookie jar to persist across the test cases (it blocks), you need to initialize the Rack session before executing any test cases. To do so, add this before hook to your describe block.
before :all do
clear_cookies
end
Alternative, you could for example use before :each to set up the necessary cookies before each request.

Resources