Passing data between blocks using sinatra - ruby

I'm trying to pass data between blocks using sinatra. For example:
#data = Hash.new
post "/" do
#data[:test] = params.fetch("test").to_s
redirect "/tmp"
end
get "/tmp" do
puts #data[:test]
end
However whenever i get to the tmp block #data is nil and throws an error. Why is that?

The reason is because the browser actually performs two separate HTTP requests.
Request: POST /
Response: 301 -> Location: /tmp
Request: GET /tmp
Response: ...
Two requests means two separate processes thus the #data instance variable is cleared once the first response is sent.
If you want to preserve the information, you need to use cookies or sessions, otherwise pass the data in querystring
post "/" do
test = params[:test]
redirect "/tmp?test=#{test}"
end
get "/tmp" do
puts params[:test]
end

Related

How do I use double and message chain in ruby rspec

I am working on Plain Ruby Project(Non Rails Environment). However, I am getting an error,
#<Double "pr"> received unexpected message :client with (no args)
This error is returned from the double object with label pr.
Here's my rspec, I have configured the Sinatra as a fake github server and its returning a JSON response.
I have verifed the result and its returning a JSON response.
RSpec.describe Humdrum::DefaultVerifications::CodeReviewsSignedOff do
describe '.Code Reviews Signed Off' do
let(:org){
'octocat'
}
let(:number){
123
}
before do
uri = URI('https://api.github.com/repos/octocat/hello-world/pulls/42/reviews')
result=JSON.load(Net::HTTP.get(uri))
github=double
allow(github)
.to receive_message_chain(:client,:pull_request_reviews)
.with(:org,:number)
.and_return (result)
end
it 'should check pull request review response' do
object=Humdrum::DefaultVerifications::CodeReviewsSignedOff.new
github=double("pr")
expert(object.pull_request_reviews_response(github)). to eq(1)
end
end
end
As you can see in the function pull_request_reviews_response, I want to stub the github.client.pull_request_reviews, hence, in the rspec for this file,
I wrote allow, message chain and from there it returns json response.
That json response will be proccessed inside the same function and return a integer response
module Humdrum
module DefaultVerifications
class CodeReviewsSignedOff
def pull_request_reviews_response(github)
#Counting total number of user who have approved the pull request
approveCount=0
github.client.pull_request_reviews("#{github.organization}/#{github.repository}", github.number).each do |review|
username = review[:user][:login]
state = review[:state]
if state == "APPROVED" and !##approvedUser.include?(username)
##approvedUser.add(username)
puts "Changes #{state} by #{username}"
approveCount += 1
end
end
return approveCount
end
What am I doing wrong?
What am I doing wrong?
You have defined github as a local variable, in two different places:
before
# ...
github = double # <----- HERE
allow(github)
.to receive_message_chain(:client, :pull_request_reviews)
.with(:org, :number)
.and_return(result)
end
it 'should check pull request review response' do
# ...
github = double("pr") # <-- AND ALSO HERE
expert(object.pull_request_reviews_response(github)).to eq(1)
end
So the object you send to the method doesn't have any stubs. Hence the error message.
There are various ways you could choose to structure this test (for instance, we could talk about how using double is generally a bad idea, and so is using receive_message_chain !... I'd opt to at least use instance_double, or potentially even just pass a real object here.).
But as a minimal change, here's a way you could define the github variable once, and reference the same object in the before block and the spec itself:
let(:github) { double("pr") } # <---- !!!
before
# ...
allow(github)
.to receive_message_chain(:client, :pull_request_reviews)
.with(:org, :number)
.and_return(result)
end
it 'should check pull request review response' do
# ...
expert(object.pull_request_reviews_response(github)).to eq(1)
end

Ruby Http gem (http client) inside Sinatra POST

I am trying to relay a GET request so when the user does a POST request to the server then the server does another GET request to some other URL and returns the result.
The issue is that when I use puts to print the result I see the correct result that I am expecting but the last line (I believe in ruby the last line of the function automatically returns) does not respond to the POST request (it returns empty response). Coming from JavaScript I believe it is doing an asynchronous GET call and POST response is not waiting until GET client is finished.
Any help would be appreciated.
require 'rubygems'
require 'sinatra'
require "http"
my_app_root = File.expand_path( File.dirname(__FILE__) + '/..' )
set :port, 80
set :bind, '0.0.0.0'
set :public_dir, my_app_root + '/public'
post "/weather" do
puts HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee') # prints correct result
HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee') # response of POST method is empty string!
end
link to http gem
Changes to
post "/weather" do
puts HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee') # prints correct result
HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee').to_s
end
The HTTP.get method returns a HTTP::Response object rather than a String object.
From the source code only specific types of object can be returned.
res = [res] if Integer === res or String === res
if Array === res and Integer === res.first
res = res.dup
status(res.shift)
body(res.pop)
headers(*res)
elsif res.respond_to? :each
body res
end
nil # avoid double setting the same response tuple twice

How do I get a redirected status code using NET:HTTP?

Similar to "getting the status code of a HTTP redirected page", but with NET::HTTP instead of curb I am making a GET request to a page that that will redirect:
response = Net::HTTP.get_response(URI.parse("http://www.wikipedia.org/wiki/URL_redirection"))
puts response.code #{
puts response['location']
=> 301
en.wikipedia.org/wiki/URL_redirection
The problem is that I want to know the status code of the redirected page. In this case it is 200, but in my app I want to check if it is 200 or something else.
The solution I've seen is to just call get_response(response['location']), but that won't work in my application because the way the redirect is designed makes it so that the redirect can only be followed once. Since the first GET consumes that one redirect, I can't then follow it again.
Is there some way to get the last status code that is a result of a GET?
EDIT: Further clarification of the situation:
The application that I'm sending GET to has a single sign-on authentication mechanism where, if I want to access 'myapp/mypage', I have to first send a post:
postResponse = Net::HTTP.post_form(URI.parse("http://myapp.com/trusted"), {"username" => #username})
Then make the GET request to:
'http://myapp.com/trusted/#{postResponse.body}/mypage
*The postResponse.body is a 'ticket' which can be redeemed once.
That GET verifies that the ticket is valid and then redirects to:
myapp.com/mypage
So whether that ticket is valid or not, I get a 301.
I want to check the status code of the final get to myapp.com/mypage.
If I manually try to follow the redirect, whether it's a HEAD request or a GET, the original redirect will have already consumed the ticket, so I will get an error that the ticket is expired even if the original redirect was a 200.
The Net::HTTP documentation has example code showing how to deal with redirects. Have you tried it? It should make it easy to get inside the redirect mechanism and grab statuses for later.
Here's their example:
Following Redirection
Each Net::HTTPResponse object belongs to a class for its response code.
For example, all 2XX responses are instances of a Net::HTTPSuccess subclass, a 3XX response is an instance of a Net::HTTPRedirection subclass and a 200 response is an instance of the Net::HTTPOK class. For details of response classes, see the section “HTTP Response Classes” below.
Using a case statement you can handle various types of responses properly:
def fetch(uri_str, limit = 10)
# You should choose a better exception.
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
end
end
print fetch('http://www.ruby-lang.org')
A minor change like this should help:
require 'net/http'
RESPONSES = []
def fetch(uri_str, limit = 10)
# You should choose a better exception.
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
RESPONSES << response
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
end
end
print fetch('http://jigsaw.w3.org/HTTP/300/302.html')
puts RESPONSES.join("\n") # =>
I see this when I run it:
redirected to http://jigsaw.w3.org/HTTP/300/Overview.html
#<Net::HTTPOK:0x007f9e82a1e050>#<Net::HTTPFound:0x007f9e82a2daa0>
#<Net::HTTPOK:0x007f9e82a1e050>
If it's enough just to make an HTTP HEAD request without 'consuming' your URL (this would be the usual expectation for a HEAD request), you can do it like this:
2.0.0-p195 :143 > result = Net::HTTP.start('www.google.com') { |http| http.head '/' }
=> #<Net::HTTPFound 302 Found readbody=true>
So in your example you'd do this:
...
result = Net::HTTP.start(response.uri.host) { |http| http.head response.uri.path }
If you want to preserve a history of response codes, you could try this. This retains the last 5 response codes from calls to get_response and exposes them through a Net::HTTP.history method.
module Net
class << HTTP
alias_method :_get_response, :get_response
def get_response *args, &block
resp = _get_response *args, &block
#history = (#history || []).push(resp.code).last 5
resp
end
def history
#history || []
end
end
end
(I don't entirely get the usage scenario, so adapt to your needs)

Parse html GET via open() with nokogiri - redirect exception

I'm trying to learn ruby, so I'm following an exercise of google dev. I'm trying to parse some links. In the case of successful redirection (considering that I know that it its possible only to get redirected once), I get redirect forbidden. I noticed that I go from a http protocol link to an https protocol link. Any concrete idea how could I implement in this in ruby because google's exercise is for python?
error:
ruby fix.rb
redirection forbidden: http://code.google.com/edu/languages/google-python-class/images/puzzle/p-bija-baei.jpg -> https://developers.google.com/edu/python/images/puzzle/p-bija-baei.jpg?csw=1
code that should achieve what I'm looking for:
def acquireData(urls, imgs) #List item urls list of valid urls !checked, imgs list of the imgs I'll download afterwards.
begin
urls.each do |url|
page = Nokogiri::HTML(open(url))
puts page.body
end
rescue Exception => e
puts e
end
end
Ruby's OpenURI will automatically handle redirects for you, as long as they're not "meta-refresh" that occur inside the HTML itself.
For instance, this follows a redirect automatically:
irb(main):008:0> page = open('http://www.example.org')
#<StringIO:0x00000002ae2de0>
irb(main):009:0> page.base_uri.to_s
"http://www.iana.org/domains/example"
In other words, the request to "www.example.org" got redirected to "www.iana.org" and OpenURI tracked it correctly.
If you are trying to learn HOW to handle redirects, read the Net::HTTP documentation. Here is the example how to do it from the document:
Following Redirection
Each Net::HTTPResponse object belongs to a class for its response code.
For example, all 2XX responses are instances of a Net::HTTPSuccess subclass, a 3XX response is an instance of a Net::HTTPRedirection subclass and a 200 response is an instance of the Net::HTTPOK class. For details of response classes, see the section “HTTP Response Classes” below.
Using a case statement you can handle various types of responses properly:
def fetch(uri_str, limit = 10)
# You should choose a better exception.
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
end
end
print fetch('http://www.ruby-lang.org')
If you want to handle meta-refresh statements, reflect on this:
require 'nokogiri'
doc = Nokogiri::HTML(%[<meta http-equiv="refresh" content="5;URL='http://example.com/'">])
meta_refresh = doc.at('meta[http-equiv="refresh"]')
if meta_refresh
puts meta_refresh['content'][/URL=(.+)/, 1].gsub(/['"]/, '')
end
Which outputs:
http://example.com/
Basically the url in code.google that you're trying to open redirects to a https url. You can see that by yourself if you paste http://code.google.com/edu/languages/google-python-class/images/puzzle/p-bija-baei.jpg into your browser
Check the following bug report that explains why open-uri can't redirect to https;
So the solution to your problem is simply: use a different set of urls (that don't redirect to https)

Retrieving full request string using Ruby curl

I intend to send a request like the following:
c = Curl::Easy.http_post("https://example.com", json_string
) do |curl|
curl.headers['Accept'] = 'application/json'
curl.headers['Content-Type'] = 'application/json'
curl.headers['Api-Version'] = '2.2'
end
I want to log the exact http request that is being made. Is there a way to get the actual request that was made (base path, query parameters, headers and body)?
The on_debug handler has helped me before. In your example you could try:
curl.on_debug do |type, data|
puts type, data
end
You can reach the solution in differents manner:
Inside your block you can put:
curl.verbose = true # that prints a detailed output of the connection
Or outside the block:
c.url # return the url with queries
c.total_time # retrieve the total time for the prev transfer (name resolving, TCP,...)
c.header_str # return the response header
c.headers # return your call header
c.body_str # return the body of the response
Remember to call c.perform (if not yet performed) before call these methods.
Many more option can be found here: http://curb.rubyforge.org/classes/Curl/Easy.html#M000001

Resources