Ruby object's console view beautifier - is there something like that? - ruby

In Ruby console we can show an object in a form like that:
#<ActiveSupport::Cache::MemcachedStore:0x00005649aedceb20 #swallow_exceptions=true, #options={:namespace
=>"dfw_cache", :codec=>#<ActiveSupport::Cache::MemcachedStore::Codec:0x00005649aedce8f0 #serializer=
Marshal, #compressor=nil>}, #connection=#<Memcached: ...
Although for large objects this form is almost impossible to read. Are there some beautifiers available for objects in these notation?

Related

Is there any way to recreate an object from the output of its Object.inspect method?

While working with the open source ELK stack, we have run into an issue where one of the Logstash inputs snmptrap is formatting data in a way that is unusable for us. Within the SNMPv1_Trap class there is an instance variable called agent_address which is stored as a SNMP::IpAddress. For anyone familiar with the way SNMP works, the agent address is extremely important in determining where a SNMP trap originated from when using trap relays on your network.
The problem can be seen when you take a look at an event generated by Logstash upon receiving a trap. Mainly, the inspect method of the agent_address variable is dumping data that does not match anything valid.
A sample event looks kind of like this:
#<SNMP::SNMPv1_Trap:0x2db53346 #enterprise=[1.3.6.1.4.1.6827.10.17.3.1.1.1], #timestamp=#<SNMP::TimeTicks:0x2a643dd1 #value=0>, #varbind_list=[#<SNMP::VarBind:0x2d5043a5 #name=[1.0], #value=#<SNMP::Integer:0x29fb6a4a #value=1>>], #specific_trap=1000, #source_ip=\"192.168.87.228\", #agent_addr=#<SNMP::IpAddress:0x227a4011 #value=\"\\xC0\\xA8V\\xFE\">, #generic_trap=6>
We know however, that the IpAddress object used in SNMP::SNMPv1_Trap is able to return us a nicely formatted string representing the IPv4 address it is storing.
For example:
require 'snmp'
include SNMP
address = IpAddress.new(192.168.86.254)
puts address
will yield 192.168.86.254 whereas:
require 'snmp'
include SNMP
address = IpAddress.new(192.168.86.254)
puts address.inspect
will yield:
#<SNMP::IpAddress:0x0000000168ae88 #value="\xC0\xA8V\xFE">
This is the expected behaviour of an object whose .inspect method has not been overridden.
Obviously the IPv4 address in #value is not useful to us, it has only three valid hex sequences (xC0=192, xA8=168, xFE=254) and also contains an invalid hex sequence ('V'). The same thing occurs whenever an octet string representing an IPv4 address is sent as a variable binding as well, which suggests some strange encoding.
Unfortunately, aside from writing our own SNMP input, there is no interface level access to this object. The object we receive via 'event' contains the inspect string, not the object itself. Therefore, the easiest apparent way to get the information we need would be to reconstruct the SNMPv1_Trap object and then make our own calls to it via Object.#send.
If I have the raw, unformatted and default string dump returned by Object.#inspect, is there any way to physically recreate the object used to make this inspect dump on the fly?
For example, given the string dump:
#<Integer:0x2737476 #value=1>
is it possible to recreate an Integer object with a field whose value is 1?. If this is possible, is there also a way to recreate nested objects the same way? For example, given the string:
#<SNMP::SNMPv1_Trap:0x2ef73621 #value=1, #agent_address=#<SNMP::IpAddress:0x0000000168ae88 #value="\xC0\xA8V\xFE">>
Would it possible to have an object that looks like the following?
SNMP::SNMPv1_Trap{
#value : 1
#agent_address : SNMP::IpAddress{
#value : 1
}
}
If I have the raw, unformatted and default string dump returned by Object#inspect, is there any way to physically recreate the object used to make this inspect dump on the fly?
No. inspect is intended for debugging purposes to be read by humans.
It is not guaranteed to be machine-readable. It is not guaranteed to be the same across different Ruby versions. It is not guaranteed to be the same across different Ruby implementations. It isn't even guaranteed to be the same across different versions of the same Ruby implementation implementing the same Ruby version. Heck, I don't even think it is guaranteed to be the same across two runs!
It is not a serialization format.
There are plenty of serialization formats specifically for Ruby (Marshal) or generically (XML, YAML, JSON, and of course ASN.1), but inspect isn't it.

Ruby on Rails Exceptions

I am a junior rails developer and was advised to use Class.find(id) to query the database instead of Class.find_by_id(id) which I previously had. The reason I was told is because the former would raise an exception while the latter would return nil. I realize this happens but I am wondering what the high level conceptual logic is for doing it this way. Why do I want the exception? Is this a rails standard where I would always prefer a method that returns an exception as opposed to nil?
You typically want the exception because you're typically doing Foo.find(id) based on data input coming from the user, such as clicking on a link.
For example, you show the user a list of items. There are links like this:
http://example.com/items/100
http://example.com/items/101
http://example.com/items/102
The user clicks the first link, and expects to see item 100.
Your code does this:
Item.find(100)
You expect to find the item, because app created the item link. You'd be surprised if the item didn't exist.
(Corner case surprises are possible: perhaps the item was deleted, or perhaps a hacker is sending in missing ids, etc. Using exceptions helps you handle this as an exceptional circumstance.)
Exceptions are preferred to nil for this, because you want the code to fail immediately so you don't accidentally send the nil on to some other method.
Ruby nil objects can be confusing because they evaluate to falsey and also because nil.id == 4 because of how Ruby uses C. Error messages show up like "Warning: Object#id will be deprecated" or "undefined method for 4:Fixnum".
Nils are problematic as a return type in Ruby in general. There's a great (paid) screencast by Gary Bernhardt that explains why you want to avoid returning nil from methods, but in a nutshell: when a method returns nil, and that nil gets passed up through a chain of method calls and something goes wrong somewhere, it can be extremely difficult to figure out where the actual problem occurred.
Say, for example, you have something like this:
foo_model = MyModel.find_by_name('foo')
# some more lines of code
do_something(foo_model)
and a method:
def do_something(model)
# some stuff stuff
some_other_method(model)
end
Now, if MyModel.find_by_name('foo') returns nil, that nil will be carried along without any errors until it actually has to do something. Say, in some_other_method, you actually try to call something on model, say model.save, you will get an error:
undefined method 'save' for nil:NilClass (NoMethodError)
The trace will carry you back up the method calls, but it will not mention the line that was actually problematic, where you assign MyModel.find_by_name('foo') (which evaluates to nil) to foo_model.
You can imagine that in a real application, the code can be much more complex, and returning nil can make it much more difficult to figure out the source of an error.
An exception, in contrast, tells you immediately where the problem is, and the trace will go back to the line where it occurred. That's one reason (there are others, I imagine) why in general, returning nil is not a good idea.
Hope that helps.

Refactor my ruby snippet so it doesn't look like C anymore: method(method(param))

I have a class which uses a connection object to send the request data created by a request_builder object.
The code looks like this:
connection.send_request(request_builder.build_request(customer))
This in turn is called by
build_report(customer, connection.send_request(request_builder.build_request(customer)))
Ugly! Any ideas on how to make it more expressive? Usually in ruby and OOP we chain objects like this: "string".make_it_bigger.flash_it.send
It's code, that how it looks. But you can make yourself a favour by not trying to cram everything together on one line:
request = request_builder.build_request(customer)
response = connection.send_request(request)
report = build_report(customer, response)
if you told us more about your code base we might be able to suggest something else, but you don't give us very much to go on. What does the request_builder object do? Does connection.send_request(...) return a response? Why does a report need a customer and a response (assuming that's what is returned by connection.send_request(...)), and so on.
build_report(customer, request_builder.build_request(customer).send_over(connection))

How do I parsing data from JSON object?

I'm just starting to dabble in consuming a JSON web service, and I am having a little trouble working out the best way to get to the actual data elements.
I am receiving a response which has been converted into a Ruby hash using the JSON.parse method. The hash looks like this:
{"response"=>{"code"=>2002, "payload"=>{"topic"=>[{"name"=>"Topic Name", "url"=>"http://www.something.com/topic", "hero_image"=>{"image_id"=>"05rfbwV0Nggp8", "hero_image_id"=>"0d600BZ7MZgLJ", "hero_image_url"=>"http://img.something.com/imageserve/0d600BZ7MZgLJ/60x60.jpg"}, "type"=>"PERSON", "search_score"=>10.0, "topic_id"=>"0eG10W4e3Aapo"}]}, "message"=>"Success"}}
What I would like to know, is what is the easiest way to get to the "topic" data so I can do something like:
topic.name = json_resp.name
topic.img = jsob_resp.hero_image_url
etc
You can use Hashie's Mash . One of the best twitter clients for ruby uses it, and the resulting interface is very clean and easy to use. I've wrapped over Delicious rss api with it in less than 60 lines.
As usuall, the specs show very clearly how to use it.

SOAP::RPC::Driver formatting problems. How can I change it?

I'm dealing with a SOAP webservice call from a server that is expecting to receive method calls with the paramaters in the format of:
<urn:offeringId> 354 </urn:offeringId>
But SOAP::RPC::Driver is generating messages in the form of:
<offeringId xsi:type = "xsd:int">354</offeringId>
The server keeps erroring when it gets these messages (especially since it's expecting offeringId to be a custom type internal to itself, not an int).
Is there anyway to configure the driver to format things the way the server is expecting it. Is the server even doing SOAP? I'm having trouble finding reference to that style of formating for SOAP (I know it DOES work though, because SOAPUI works just fine with that type of message).
-Jenny
Edit: I've got at least part of it solved. the RPC::Driver (obviously) uses the RPC standard, whereas apparently the server I'm trying to talk to is doing "document". Now, when I look at RPC::Driver's API, I'm seeing a method named "add_document_method". That SOUNDS to me like it might be what I want, but I can't figure out what paramaters to give it. The examples I've seen around the net don't make much sense to me, things like:
def GetNamePair(response)
response.account.each do |x|
class << x
attr :configuration, true
end
x.configuration = Hash[*x.a.map do |y|
[y.__xmlattr[XSD::QName.new(nil, 'n')], String.new(y)]
end.flatten]
end
end
mNS = 'urn:zimbraAdmin'
drv.add_document_method('GetAllAdminAccountsRequest', mNS, [XSD::QName.new(mNS, 'GetAllAdminAccountsRequest')],
[XSD::QName.new(mNS, 'GetAllAdminAccountsResponse')] )
puts YAML.dump(GetNamePair(drv.GetAllAdminAccountsRequest([]))
All I really know is that I have a method that takes in certain parameters.... I really don't get why, if this method does what I think it does, it has to be more complicated. Isn't this just a matter of taking the exact same data and formating it differently? I'm so confused....
Okay, what I ended up doing was using SOAP:RPC:Drivers add_document_method, which requires me to give it the wsdl, namespace, etc, and then give it the attributes later as a single input hash thingy (and gives me the output in a similar format). It worked, it just wasn't as clean as add_rpc_method (which is waht add_method defaults to)
-Jenny

Resources