Windows terminal/Ubuntu not showing all response with pretty print - ruby

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

Related

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

Why do CSV::HeaderConverters stop processing when a non-String is returned?

Why does processing of header converters stop with the first non-String that's returned from a header converter?
Details
After the built-in :symbol header converter is triggered, no other converters will be processed. It seems that processing of header converters stops with the first converter that returns anything that's not a String (i.e. same behavior if you write a custom header converter that returns a Fixnum, or anything else).
This code works as expected, throwing the exception in :throw_an_exception
require 'csv'
CSV::HeaderConverters[:throw_an_exception] = lambda do |header|
raise 'Exception triggered.'
end
csv_str = "Numbers\n" +
"1\n" +
"4\n" +
"7"
puts CSV.parse(
csv_str,
{
headers: true,
header_converters: [
:throw_an_exception,
:symbol
]
}
)
However, if you switch the order of the header converters so that the :symbol converter comes first, the :throw_an_exception lambda is never called.
...
header_converters: [
:symbol,
:throw_an_exception
]
...
So I reached out to JEG2.
I was thinking that converters were intended to be a series of steps in a chain, where all elements were supposed to go through every step. In fact, that's not the way to best use the CSV library, especially if you have a very large amount of data.
The way it should be used (and this is the answer to the "why" question and the explanation for why this is better for performance) is to have the converters work like a series of matchers, where the first matched converter returns a non-String, which indicates to the CSV library that the current value has been converted successfully. When you do that, the parser can stop as soon as it's a non-String, and move on to the next header/cell value.
In this way you remove a TON of overhead when parsing CSV data. The larger the file you're processing, the more overhead you eliminate.
Here is the email response I got back:
...
The converters are basically a pipeline of conversions to try. Let's say you're using two converters, one for dates and one for numbers. Without a linked line, we would try both for every field. However, we know a couple of things:
An unconverterd CSV field is a String, because that's how we read it in
A field that is now a non-String, has been converted, so we can stop searching for a converter that matches.
Given that, the optimization helps our example skip checking the number converter if we already have a Date object.
...
For unknown reason CSV#convert_fields function has a hilarious
break unless field.is_a? String # short-circuit pipeline for speed
line in converters.each. I doubt I could suggest anything better than monkeypatching this function, but the cause is clear now.

Post issue with sinatra

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

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

How can I extract/parse from this SOAP envelope using Ruby?

I am using a gem which uses soap/wsdlDriver.
When I post I get back a SOAP response and am unable to easily parse it.
This is the response I get back:
#<SOAP::Mapping::Object:0x159e95faf098 {}id="27b907f8-da51-f611-ab02-4c5f88a8ec8
8" {}error=#<SOAP::Mapping::Object:0x159e95fae33c {}number="0" {}name="No Error"
{}description="No Error">>
I need to get the entire value in the id="xxxx"
This is what on get on heroku (note: it works locally). This comes from testing various variations of response.id (where response.inspect is what created the output above)
f"
{}error=#>
response[id]
/disk1/home/slugs/220752_47a08bb_10e7/mnt/app/controllers/sugarcrm_controller.rb
:77: warning: Object#id will be
deprecated; use Object#object_id nil
response.id:
/disk1/home/slugs/220752_47a08bb_10e7/mnt/app/controllers/sugarcrm_controller.rb
:79: warning: Object#id will be
deprecated; use Object#object_id
23891500658740
/disk1/home/slugs/220752_47a08bb_10e7/mnt/app/controllers/sugarcrm_controller.rb
:80: warning: Object#id will be
deprecated; use Object#object_id this
is the contact_id: 23891500658740
events:
Ok, I'm 95% sure that is the output of SOAP::Mapping::Object#inspect and not the actual response. And from that class it looks you use the [] method to pull out attributes.
So if I am reading that right, then it looks like you might want:
response_id = response_object['id']
Though each attribute being prefaced with {} seems pretty odd. So if that is actually part of the attribute name, you may need:
response_id = response_object['{}id']
But that seems pretty strange, and may indicate that the SOAP library you are using is not parsing the response properly.
Disclaimer: I've never used this lib before, and posted this from just perusing the docs... This may or may not be very accurate in the ways using this class is described.
I don't know how to do this with ruby, but may be like this code in javascript.
var t = "<SOAP message........>";
var id = t.replace(/.*id=\"(.*?)\".*/, "$1");
Regex details:
.*id=\"(.*?)\".*
.* = anything, 0 or N times.
(.*?) = anything, 0 or N times, stopping at first match.
$1 = first group, the content inside ().
So, everthing will be replaced by id content.

Resources