Post issue with sinatra - ruby

Following this tutorial to create the route for the POST:
post '/secret' do
params[:secret].reverse
end
When I view this on the local server I get my secret message in reverse. But if I want to print another line, below the reverse line, the output gets all messed up on the localhost:9393.
post '/secret' do
p params[:secret].reverse
p params[:secret].reverse
end
Any ideas why, in the second block of code, only one line of the reversed secret shows up on my output after inputting the secret in the form on the main page? I'm looking to write more complex code but it seems like it's only printing the last line. Is this a Sinatra thing?

In your first example you're seeing your secret in reverse because Sinatra is handed the reversed string from the block and displays it.
In your second bit of code you're printing the reversed strings to the local output, which is where you'd expect to see the logs displayed. You're seeing a messed up string because the result of p params[:secret].reverse is not something Sinatra apparently wants to display correctly. Sinatra will fall back to trying to display the value returned from a route block if no other output had already been built up.
The next section of the tutorial you're following covers Views and Templates. Those are what you'll use to output a webpage.

It's just how Sinatra works. The return value of the post block is sent as response. The return value of the block is always the return value of the last statement inside the block. So your p is actually useless, it will only print the secret to the log. It just happens that p returns the value it was passed after printing it, so you still see one line because that is the return value of the block. If you actually want to return a response that contains the line twice, you have to build a single string that contains two lines. There are many ways to do this, here are some:
# use string interpolation to create a string that
# contains reversed_secret, followed by a newline,
# then again followed by reversed_secret
post '/secret' do
reversed_secret = params[:secret].reverse
"#{reversed_secret}\n#{reversed_secret}"
end
# use an array and join the values with a newline
post '/secret' do
reversed_secret = params[:secret].reverse
[reversed_secret, reversed_secret].join("\n")
end
# "print" individual lines to a StringIO object
# and read the resulting string afterwards
post '/secret' do
result = StringIO.new
reversed_secret = params[:secret].reverse
result.puts reversed_secret
result.puts reversed_secret
result.string
end
Whatever works best for you.

Oh, I see. You're using p.
Unlike PHP, Sinatra does not capture standard output. Rather, what is returned as the content .of the page is what is returned from the function.
In the first example, you return the result of reverse.
In the first example, you return nil, which is the result of the second p call.
If you want to return both reverses, you need to construct a string that contains them both:
post '/secret' do
"#{params[:secret].reverse}\n#{params[:secret].reverse}"
end

Related

Windows terminal/Ubuntu not showing all response with pretty print

When parsing an API response (using pretty print), most of the response is being cut off and just "..." is being shown.
irb(main):022:0> data = JSON.parse(response.read_body)
=>
{"data"=>
...
How do I display the whole response?
You can type data after the assignment (at the next prompt), and you'll get the entire contents. If you don't actually need the assignment, and only want to inspect the parsed response, use JSON.parse(response.read_body)
By the way, the truncation behaviour of IRB is the default, but it can be changed with IRB.CurrentContext.echo_on_assignment = true - see https://docs.ruby-lang.org/en/master/IRB/Context.html

Ruby extract single key value from first block in json

I'm parsing a very large json output from an application API and end up with a ruby array similar to the sanitized version below:
{"log_entries"=>
[{"id=>"SDF888B2B2KAZZ0AGGB200",
"type"=>"warning",
"summary"=>"Things happened",
"created"=>"2017-07-11T18:40:31Z",
"person"=>
{"id"=>"44bAN8",
"name"=>"Harry"}
"system"=>"local",
"service"=>"syslog"
{"id=>"HMB001NBALLB81MMLLABLK",
"type"=>"info",
"summary"=>"Notice",
"created"=>"2017-06-02T11:23:21Z",
"person"=>
{"id"=>"372z1j",
"name"=>"Sally"}
"system"=>"local",
"service"=>"syslog"}]},
"other"=>200,
"set"=>0,
"more"=>false,
"total"=nil}
I just need to be able to print the value of the "created" key only in the first block. Meaning, when the program exits, I need it to print "2017-07-11T18:40:31Z." I've googled a lot but wasn't successful in finding anything. I've tried something like:
puts ["log_entries"]["id"]["created"]
My expectation was to print all of them to start somewhere and even that yields an error. Forgive me, I don't use ruby much.
Since log_entries is an array you can just access the first element and get its created value.
Assuming the variable result holds the whole hash (the JSON you parse from the API):
puts result['log_entries'][0]['created']
will print out the first date. Now you might want to guard that for cases where log_entries empty, so wrap it in a if:
if result['log_entries'].any?
puts result['log_entries'][0]['created']
end
Your json is not in valid format. But assuming you have the right format, following should work
result["log_entries"].collect{|entry| entry["created"]}
=> ["2017-07-11T18:40:31Z", "2017-06-02T11:23:21Z"]
Above code will collect all the created date and give you an array

unwrapping an object returned from twitter api

While reading some data from the Twitter api, I inserted the data into the file like this
results.each do |f|
running_count += 1
myfile.puts "#{f.user_mentions}"
...
The results (2 sample lines below) look like this in the file
[#<Twitter::Entity::UserMention:0x007fda754035803485 #attrs={:screen_name=>"mr_blah_blah", :name=>"mr blah blah", :id=>2142450461, :id_str=>"2141354324324", :indices=>[3, 15]}>]
[#<Twitter::Entity::UserMention:0x007f490580928 #attrs={:screen_name=>"andrew_jackson", :name=>"Andy Jackson", :id=>1607sdfds, :id_str=>"16345435", :indices=>[3, 14]}>]
Since the only information I'm actually interested in is the :screen_name, I was wondering if there's a way that I could only insert the screen names into the file. Since each line is in array brackets and then I'm looking for the screen name inside the #attrs, I did this
myfile.puts "#{f.user_mentions[0]#attrs{"screen_name"}}"
This didn't work, and I didn't expect it to, as I'm not really sure if that's technically array etc. Can you suggest how it would be done?
You need to access the #attrs instance variable in the Twitter UserMention object. If you want to puts the screen name from the first object, based on your current output, I would write
myfile.puts "#{f.user_mentions[0].attrs[:screen_name]"
Also, putting the code on how results is returned would help get a definite answer quickly. Cheers!
Assuming that results is an array of Twitter::Entity::UserMention
results.each do |r|
myfile.puts r.screen_name
end

Ruby - Files - gets method

I am following Wicked cool ruby scripts book.
here,
there are two files, file_output = file_list.txt and oldfile_output = file_list.old. These two files contain list of all files the program went through and going to go through.
Now, the file is renamed as old file if a 'file_list.txt' file exists .
then, I am not able to understand the code.
Apparently every line of the file is read and the line is stored in oldfile hash.
Can some one explain from 4 the line?
And also, why is gets used here? why cant a .each method be used to read through every line?
if File.exists?(file_output)
File.rename(file_output, oldfile_output)
File.open(oldfile_output, 'rb') do |infile|
while (temp = infile.gets)
line = /(.+)\s{5,5}(\w{32,32})/.match(temp)
puts "#{line[1]} ---> #{line[2]}"
oldfile_hash[line[1]] = line[2]
end
end
end
Judging from the redundant use of quantifiers ({5,5} and {32,32}) in the regex (which would be better written as {5}, {32}), it looks like the person who wrote that code is not a professional Ruby programmer. So you can assume that the choice taken in the code is not necessarily the best.
As you pointed out, the code could have used each instead of while with gets. The latter approach is sort of an old-school Ruby way of doing it. There is nothing wrong in using it. Until the end of file is reached, gets will return a string, and when it does reach the end of file, gets will return nil, so the while loop works as the same when you use each; in each iteration, it reads the next line.
It looks like each line is supposed to represent a key-value pair. The regex assumes that the key is not an empty string, and that the key and the value are separated by exactly five spaces, and the the value consists of exactly thirty-two letters. Each key-value pair is printed (perhaps for monitoring the progress), and is stored in oldfile_hash, which is most likely a hash.
So the point of using .gets is to tell when the file is finished being read. Essentially, it's tied to the
while (condition)
....
end
block. So gets serves as a little method that will keep giving ruby the next line of the file until there is no more lines to give.

Get full path in Sinatra route including everything after question mark

I have the following path:
http://192.168.56.10:4567/browse/foo/bar?x=100&y=200
I want absolutely everything that comes after "http://192.168.56.10:4567/browse/" in a string.
Using a splat doesn't work (only catches "foo/bar"):
get '/browse/*' do
Neither does the regular expression (also only catches "foo/bar"):
get %r{/browse/(.*)} do
The x and y params are all accessible in the params hash, but doing a .map on the ones I want seems unreasonable and un-ruby-like (also, this is just an example.. my params are actually very dynamic and numerous). Is there a better way to do this?
More info: my path looks this way because it is communicating with an API and I use the route to determine the API call I will make. I need the string to look this way.
If you are willing to ignore hash tag in path param this should work(BTW browser would ignore anything after hash in URL)
updated answer
get "/browse/*" do
p "#{request.path}?#{request.query_string}".split("browse/")[1]
end
Or even simpler
request.fullpath.split("browse/")[1]
get "/browse/*" do
a = "#{params[:splat]}?#{request.env['rack.request.query_string']}"
"Got #{a}"
end

Resources