Sinatra Route returns nothing - ruby

I have a very simple example where sinatra simply returns no output.
The program enters the if clause but the block is not finished and therefore nothing is sent to rack, nothing goes to the browser... not a single character.
require 'sinatra'
get '/' do
var='confirmed'
if var == 'confirmed'
'Confirmed'
end
if var == 'declined'
'Declined'
end
end
The question is now: Is adding a "return" or "next" the way this is usually done? With it, its running... But I never found an example in the net that had to use a next statement...
So, is the "if logic" usually somewhere else and there is only a single erb :xyz at the end of a route?
I am confused...

You have the answer mostly. You always need to send something to rack to get a response.
You probably have a view to show the status on then you add at the end something like this (You can have multiple erb blocks just add for each route a erb call):
get '/' do
var='confirmed'
if var == 'confirmed'
st = 'Confirmed'
end
if var == 'declined'
st = 'Declined'
end
erb :myViewName, :locals => {:status => st}
end
Or just use return like this, if your response is just a string. Be aware that everything after this return isn't executed:
if var == 'confirmed'
return 'Confirmed'
end

It's nothing to do with the way Sinatra works, really. It's more of a Ruby matter. According to Sinatra readme:
The return value of a route block determines at least the response body passed on to the HTTP client, or at least the next middleware in the Rack stack. Most commonly, this is a string, as in the above examples. But other values are also accepted.
The problem in your code is that your last if is a statement itself. If your var variable isn't "declined", then the if block evaluates to nil, and as it is the last value in your route block, this is what gets returned by Sinatra.
When you use explicit return, you don't get to the second if and don't have this issue, which is why it works with explicit return.
You would not need an explicit return with a if/elsif block like this:
# This is one single statement that would return either confirmed or declined
# Request will always return a non-nil value
get '/' do
...
if var == 'confirmed'
'Confirmed'
elsif var == 'declined'
'Declined'
end
end
Or a case/when block:
# This is one single statement that would return either confirmed or declined
# Request will always return a non-nil value
get '/' do
...
case var
when 'confirmed' then 'Confirmed'
when 'declined' then 'Declined'
end
end

Related

Assign variable as either output of equation OR nil if equation can't run (circumventing NoMethodError)

I'm saving an object as such:
#rentalrequest = RentalRequest.new do |rr|
rr.delivery_start = Time.zone.parse(request_params[:deliverydate] + " " + request_params[:deliverytime_start]).utc
...
end
Every once in a while, my front end validation fails, and somehow, the form is posted even though deliverydate and deliverytime_start are blank. In this case, the controller breaks with a NoMethodError because this statement doesn't make sense:
Time.zone.parse("")
However, rather than having to write a rescue for when this happens, I feel like it's so much easier if I can just say rr.delivery_start = nil if Time.zone.parse doesn't work. That way, the back end validation on the #rentalrequest object kicks in and serves as a rescue.
But I'm not sure how to write the rr.delivery_start = nil if Time.zone.parse doesn't work (like... if any part of it doesn't work)
Thoughts?
How about checking if those params exist instead?
#rentalrequest = RentalRequest.new do |rr|
rr.delivery_start = nil
if request_params[:deliverydate].present? && request_params[:deliverytime_start].present?
rr.delivery_start = Time.zone.parse(request_params[:deliverydate] + " " + request_params[:deliverytime_start]).utc
end
...
end
Your calculation is in the wrong place, your model should be maintaining its own state by itself. Something like this perhaps:
class RentalRequest < ActiveRecord::Base
before_validation :set_delivery_start
private
def set_delivery_start
# Presumably validations will catch these conditions...
return if(!deliverydate || !deliverytime_start)
self.delivery_start = ... # whatever calculation matches the deliverydate and delivertime_start types goes here
end
end
and then you'd have validations to ensure that all three delivery values made sense.
You can require those parameters if new block is in controller.
def request_params
params
.require(:rental_request)
.permit(..., :deliverydate, :deliverytime_start)
.require(:deliverydate, :deliverytime_start)
end

Not all methods are getting called Sinatra

I am building a Ruby app using Sinatra and the Twilio api.
Over a phone call to my assigned Twilio number, the user gets prompted to record an audio message. Once that message is recored the user gets redirected to the following route where if the user dials 1 (or anything else), they should get redirected to their feed of msgs, but if the user dials 2, then the user's message gets deleted and should get redirected to a route where they can record a new message.
Here is my route:
get '/playback/handle-recording/:recordingSID' do
if params['Digits'] = '2'
delete(params['recordingSID'])
deletedMsg = "Audio deleted."
getRecord(deletedMsg)
else
getFeed()
end
end
helper methods:
helpers do
def delete(recording)
recording = client().account.recordings.get(recording)
recording.delete
end
def getFeed()
redirect '/feed'
end
def getRecord(appendMsg)
Twilio::TwiML::Response.new do |response|
if appendMsg
response.Say appendMsg
end
response.Say "Record your message."
response.Record :maxLength => '5', :trim => "trim-silence", :playBeep => "true", :action => '/playback', :method => 'get'
end.text
end
end
My issue is that whether the user lands in the "if" or the "else" the first method, getRecord(deletedMsg) is the one that gets called, and not the getFeed().
How can I fix my route so that if the user lands in the else he does get redirected to his feed page and not to the getRecords method.
Are you sure they're actually making it into the else? Ruby won't just randomly not execute what's in there for no good reason.
One thing you may want to look at is you are assigning, not comparing the params value:
if params['Digits'] = '2'
You'll want to do:
if params['Digits'] == '2'
That could definitely lead to some strange behavior in an if statement, like, for instance always executing one path.

Adding a querystring to rack-rewrite response

I've got a simple 301 redirect to capture all non .com domains I have registered for my site as follows:
DOMAIN = 'www.mywebsite.com'
use Rack::Rewrite do
r301 %r{.*}, "http://#{DOMAIN}$&", :if => Proc.new {|rack_env|
rack_env['SERVER_NAME'] != DOMAIN && ENV["RACK_ENV"] == 'production'
}
end
I'd like to add a querystring to the response to add the original domain in the format
?utm_source=#{rack_env['SERVER_NAME']}
But can't quite work out how not to crash the server :) Can it be done & retain any original query string?
It's unlikely that anyone will hit any subpages under the main domain, but when I drop the $& from the rewrite, and replace it with my string, it blows up with no errors in the logs...
I think the reason your original code won't work is because rack_env is not available within your second argument as it's a block argument to the third. Does that make sense?
You can however pass a Proc as the second argument of a redirect, so I think something like this should work (partially tested :)
DOMAIN = 'www.mywebsite.com'
ORIGINAL_REQUEST = Proc.new do |match, rack_env|
"#{DOMAIN}?utm_source=#{rack_env['REQUEST_URI']}"
end
use Rack::Rewrite do
r301 %r{.*}, ORIGINAL_REQUEST, :if => Proc.new {|rack_env|
rack_env['SERVER_NAME'] != DOMAIN && ENV["RACK_ENV"] == 'production'
}
end

Using Open-URI to fetch XML and the best practice in case of problems with a remote url not returning/timing out?

Current code works as long as there is no remote error:
def get_name_from_remote_url
cstr = "http://someurl.com"
getresult = open(cstr, "UserAgent" => "Ruby-OpenURI").read
doc = Nokogiri::XML(getresult)
my_data = doc.xpath("/session/name").text
# => 'Fred' or 'Sam' etc
return my_data
end
But, what if the remote URL times out or returns nothing? How I detect that and return nil, for example?
And, does Open-URI give a way to define how long to wait before giving up? This method is called while a user is waiting for a response, so how do we set a max timeoput time before we give up and tell the user "sorry the remote server we tried to access is not available right now"?
Open-URI is convenient, but that ease of use means they're removing the access to a lot of the configuration details the other HTTP clients like Net::HTTP allow.
It depends on what version of Ruby you're using. For 1.8.7 you can use the Timeout module. From the docs:
require 'timeout'
begin
status = Timeout::timeout(5) {
getresult = open(cstr, "UserAgent" => "Ruby-OpenURI").read
}
rescue Timeout::Error => e
puts e.to_s
end
Then check the length of getresult to see if you got any content:
if (getresult.empty?)
puts "got nothing from url"
end
If you are using Ruby 1.9.2 you can add a :read_timeout => 10 option to the open() method.
Also, your code could be tightened up and made a bit more flexible. This will let you pass in a URL or default to the currently used URL. Also read Nokogiri's NodeSet docs to understand the difference between xpath, /, css and at, %, at_css, at_xpath:
def get_name_from_remote_url(cstr = 'http://someurl.com')
doc = Nokogiri::XML(open(cstr, 'UserAgent' => 'Ruby-OpenURI'))
# xpath returns a nodeset which has to be iterated over
# my_data = doc.xpath('/session/name').text # => 'Fred' or 'Sam' etc
# at returns a single node
doc.at('/session/name').text
end

return different value from a before do block in sinatra

is there a way to stop execution and return a different value in a before do block in sinatra ?
before do
# code is here
# I would like to 'return "Message"'
# I would like "/home" to not get called.
end
// rest of the code
get '/home' do
end
before do
halt 401, {'Content-Type' => 'text/plain'}, 'Message!'
end
You can specify only status if you want, here's example with status, headers and body
On http://www.sinatrarb.com/intro Filters section
Before filters are evaluated before
each request within the context of the
request and can modify the request and
response. Instance variables set in
filters are accessible by routes and
templates:
before do
#note = 'Hi!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
#note #=> 'Hi!'
params[:splat] #=> 'bar/baz'
end

Resources