Ruby delete keys from hash only while printing without modifying the hash - ruby

I am a newbie to ruby. I am trying to delete keys which are passwords from the hash while printing the hash, with/without modifying the original hash
request = {"name"=>"test-2",
"dns_zone_status"=>"valid",
"certificate_status"=>"valid",
"db_password"=>"GS*8qLiU",
"af_pass"=>"rk*pp2",
"master_password"=>"sfvrere",
"db_user"=>"testuser2"}
puts "request : #{request}"
puts "request : #{request.except("db_password","af_pass","master_password")}"
The first puts print the whole hash and the 2nd one errors undefined method 'except' for #<Hash:0x00000001dbcd00> (NoMethodError)

Use Hash#reject, which is non-destructive and returns a hash.
puts "request : #{
request.reject do |k,_|
["db_password","af_pass","master_password"].include?(k)
end
}"
#request : {"name"=>"test-2", "dns_zone_status"=>"valid", "certificate_status"=>"valid", "db_user"=>"testuser2"}
As is commonly done, I've used an underscore for the block variable holding the value associated with the key k to tell the reader that it is not used in the block calculation.

Instead of having to bring all of ActiveSupport in as a dependency (which is the gem implementing Hash#except, if you don't need it in other ways you can just inline this tiny method as:
puts "request : #{request.slice(*request.keys - ["db_password","af_pass","master_password"])}"

The Hash#except method actually comes from ActiveSupport, which is loaded by default in Rails but not in standalone Ruby.
However, that's easily remedied, all you have to is add the activesupport gem and one of the following requires:
# require everything in active support, the easy/lazy option
# this is what rails does and it's probably fine to do
# unless you really, really care about how much memory you use
require 'active_support/all'
# require only the hash extensions
require 'active_support/core_ext/hash'
# require only this one specific method
require 'active_support/core_ext/hash/except'
Note that the name of the gem (activesupport) and the library you require (active_support) are indeed named differently, which can be a little tricky to remember.

Related

Is there a way to convert an object to json or other printable format?

I have an object, and I want to dump all its information.
For debugging purpose, can I convert it to json or other suitable format?
I know using p object, sometimes can print out all its information; but not always. So I am asking is there other ways when p object becomes invalid
Try pry
It is excellent for exploring objects. Especially large objects because it has paging. The objects are also color coded to make it more readable.
Steps:
install pry
Add require 'pry' to the script file you want to debug
Add binding.pry below the object declaration of anywhere the object is in scope
Run your script
This will pop open the pry console with access to your object
Type in the object name
'ls object' will list all the instance variables of the object
There's couple ways you can go. Using json from the standard lib is one way to convert an object to json.
this converts an object in ruby to a json string:
require "json"
h = Hash.new(name: "example")
JSON.dump(h)
But you seem to be wanting to inspect an object in detail. The best solution is probably the "Pry" gem that others have suggested if you really need a lot of detail.
so after installing pry you can "cd" into objects and inspect instance variables public/private methods and the source code of methods/classes, etc. its really great stuff.
require "pry"
cd SomeObject
ls # this shows you everything defined in the object.
The problem with object.inspect that others have suggested is that it can be overriden, often is, and thus will oft not show you enough information. So it depends on what you want to find out.
puts o.instance_variables #=> shows all an objects instance variable names
puts o.instance_variable_get :#some_var #=> returns the value held by #some_var
puts o.methods - Object.methods #=> roughly speaking, shows you the methods defined on an object itself and not inheritted from parent objects
Unfortunately there's not a perfect answer here, but for debugging purposes I personally think nothing beats out pry especially with the pry debugger addon gem (forgot actual name) that allows you to step through the call stack.
You can use "inspect" : "Returns a string containing a human-readable representation of obj. By default, show the class name and the list of the instance variables and their values (by calling inspect on each of them). "
Example:
puts object.inspect
you can try puts my_object.as_json.to_json

How do I have Ruby YAML dump a Hash subclass as a simple Hash?

I have a class Foo that is a subclass of Hash.
class Foo < Hash
# whatever Foo-specific methods/etc
end
When I dump it to a file with YAML, it is written with a tag indicating the class.
> f = Foo.new
> f[:bar] = "baz"
> puts YAML.dump(f)
--- !ruby/hash:Foo
:bar: baz
I would like it to just be written as a plain old hash (not !ruby/hash:Foo)
> puts YAML.dump({bar:"baz"})
---
:bar: baz
...so that consumers of my data don't need to know about Foo. Is there a magic method to add to my class to convert itself for serialization, or a magic option to pass to YAML.dump?
Of course it is easy to convert one Foo object to a hash, but they may show up nested at any level inside the actual hash I'd like to dump, and I'd rather not have to do a search and replace.
You can achieve this with the (poorly documented) encode_with and represent_map methods. To customise the YAML serialisation of an object you provide it with an encode_with method which accepts a coder object, one of the methods on which is represent_map.
class Foo < Hash
# other methods ...
def encode_with coder
coder.represent_map nil, self
end
end
Now YAML.dump will just output your object as a normal hash.
However
There is a bit of a problem because there is a bug that will cause this to fail and is only fixed in the latest Gem version of Psych. It is not fixed in the current latest Ruby version (ruby 2.0.0p247). It is fixed in Ruby trunk so later patch releases should be okay.
In order to use this you will have to make sure you are using the latest Psych Gem, rather than the version bundled with Ruby. This should be as easy as
gem 'psych', '2.0.0'
before you require Yaml, but it seems that in Ruby 2.0 this doesn’t work for some reason that I can’t figure out. Using Bundler to specify the Gem versions does work though, so you might need to create a Gemfile and specify Psych in there if you’re not already using it.
Search and replace was actually not too bad:
# Convert Hash/Array subclasses into plain hashes/arrays for YAML dump.
# Assumptions:
# Hash keys will be simple objects - don't need to clear them
# No custom objects containing Hash/Array subclass instances
def deep_clear_subclasses(obj, dedup = {})
case obj
when Hash
return dedup[obj] if dedup.has_key? obj
dedup[obj] = copy = {}
obj.each {|k,v| copy[k] = deep_clear_subclasses(v, dedup)}
copy
when Array
return dedup[obj] if dedup.has_key? obj
obj.inject(dedup[obj] = []) {|a,v| a << deep_clear_subclasses(v,dedup)}
else
obj # not going to operate on other kinds of objects
end
end

Is there a simple way to iterate through pairs in a Ruby hash in order of addition (i.e. oldest first)?

I create an empty hash chickens, and key-value pairs are then added over time.
chickens = {}
chickens.merge!("Davison"=>"plucky")
chickens.merge!("Mortimer"=>"sullen")
chickens.merge!("Chauncey"=>"forlorn")
for name,mood in chickens do puts "#{name}: #{mood}" end
produces
Mortimer: sullen
Chauncey: forlorn
Davison: plucky
but I don't desire this. How do I cycle through the chickens in the order they were added?
The short answer is: update your Ruby. Ruby 1.9 is years old and works the way you want (as does current Ruby, 2.0). If you insist on ancient, unsupported Ruby, there's an OrderedHash class available via a gem:
gem install orderedhash
Your code would then become:
require 'rubygems'
require 'orderedhash'
chickens = OrderedHash.new
# The rest stays the same
Again, I recommend instead just upgrading your Ruby installation.
P.S. Your code would be much more Ruby-like if you iterate over the Hash like this:
chickens.each do |name, mood|
puts "#{name}: #{mood}"
end
#DarshanComputing began talking about making the code more Ruby-like, but here's how I would do the sample code in its entirety:
chickens = {}
chickens["Davison"] = "plucky"
chickens["Mortimer"] = "sullen"
chickens["Chauncey"] = "forlorn"
chickens.each do |name, mood|
puts "#{name}: #{mood}"
end
Using merge is fine, but it's also verbose. You'll see a lot of Ruby programmers take the more simple path and add the key/value pair directly, rather than rely on a mutating method.
Using for to loop over something is definitely not Ruby-like. Though the language supports it, its use is eschewed by Ruby developers consistently. The reason is, for name, mood ... adds name and mood to the local variables, unlike each which confines them inside the do block. Littering the variable space with temporary variables is bad-form. For instance, after running the original code I can do:
[7] (pry) main: 0> name
"Chauncey"
[8] (pry) main: 0> mood
"forlorn"

How do you check if a library/ruby-gem has been loaded?

In ruby code, how would I check what external libraries are loaded? For example,
require 'some-library'
if is-loaded?('some-library')
puts "this will run"
end
or
# require 'some-library' Don't load it in here
if is-loaded?('some-library')
puts "this will not run"
end
Is there a way to do this?
Note on why I need this:
I'm working on boom, and on windows, it will try to include 'Win32/Console/ANSI', to enable ANSI color codes like \e[36m. What I'm trying to do is if the system is windows and 'Win32/Console/ANSI' is not loaded, it would append the color codes, so the color codes are not outputted. Here is the file.
Most libraries will typically define a top-level constant. The usual thing to do is to check whether that constant is defined.
> defined?(CSV)
#=> nil
> require "csv"
#=> true
> defined?(CSV)
#=> "constant"
> puts "loaded!" if defined?(CSV)
loaded!
#=> nil
require will throw a LoadError if it can't find the library you are trying to load. So you can check it like this
begin
require 'some-library'
puts 'This will run.'
rescue LoadError
puts 'This will not run'
# error handling code here
end
If you want to safely try requiring a gem/library that may or may not be available, use something like this:
begin
require 'securerandom'
rescue LoadError
# We just won't get securerandom
end
This works even if the gem in question has already been required. In that scenario the require statement will do nothing and the rescue block will never execute.
If you are just interested in whether or not a gem/library has already been loaded, check to see if one of its constants is present. I do something like this to dynamically load additional functionality if ActiveSupport is loaded:
if defined?(ActiveSupport)
require "active_support/cache/redis_store"
end
You can also use the opposite to load a compatibility layer if the gem/library is NOT present. For example, I use some Hash methods that don't exist in Ruby's core Hash implementation, but are added by ActiveSupport. So, I define those methods when my gem runs in an environment where ActiveSupport doesn't exist.
require 'core_ext/hash' unless defined?(ActiveSupport)
Require Library Unless Already Loaded
For simplicity, here's how you load a library unless it's already loaded:
require 'RMagick' unless defined?(Magick)
try this :
def loaded?(name)
r = Regexp.new("#{name}.rb$")
$LOADED_FEATURES.select{|t| t.match(r) }.any?
end
Be sure of the name of your module (search here $LOADED_FEATURES).

Truncate #inspect output in irb (ruby)

I want to truncate #inspect output in irb (a large output must be cropped to MAX_LEN).
Currently, I override :inspect, :to_s methods for all specific objects.
Is there are other solution?
change $stdout ?
other?
For a clean solution, gem install hirb. hirb pages irb's returned values if they get too long.
If you want to monkeypatch irb:
module IRB
class Irb
def output_value
#context.last_value.to_s.slice(0, MAX_LEN)
end
end
end
I don't recommend this because it's a hack and breaks any time gems like ap and hirb are required.
Instead of monkeypatching irb, I'd recommend trying ripl, an irb alternative that is meant to extended.
The above as a ripl plugin would be:
require 'ripl'
module Ripl::SlicedInspect
def format_result(result)
result_prompt + result.inspect.slice(MAX_LEN)
end
end
Ripl::Shell.send :include, Ripl::SlicedInspect
With this plugin, you could require it as needed or add to your ~/.riplrc if you want to always use it.
Your solution is good.
It involves no dark magic, which might make the code less understandable and error-prone.
If you're just in IRB - you could define a monkeypatch in irb itself and or load a file that monkeypatches inspect via 'load'. This way you keep it out of your core codebase but you still get the functionality you need w/o having to override inspect in every class you wish to inspect....
If it's because you have a nested hash or something that's hard to decipher, try awesome_print. You can make it the default output formatter in irb by placing the following in your .irbrc:
require 'ap'
module IRB
class Irb
def output_value
ap #context.last_value
end
end
end
This makes objects with lots of data easy to decipher in IRB.
Even if you don't use awesome_print, you can truncate output using this same technique so you don't have to override to_s in your code.
For rails 3.1.1+, place the code below in helpers/irb_helper.rb
module IRB
class Irb
MAX_LEN = 10000
def output_value
if (#context.inspect_last_value.length > MAX_LEN)
printf #context.return_format, "#{#context.inspect_last_value[0..MAX_LEN]} <- Truncated"
else
printf #context.return_format, #context.inspect_last_value
end
end
end
end
If you'd like to customize your output more, check irb's source at https://github.com/Ruby/Ruby/blob/trunk/lib/irb.rb
I sometimes modify the objects themselves (via a module called BoringInspect which I include into the relevant classes) so that exception messages are also manageable.

Resources