#02Amazing Print tips and tricks

AmazingPrint Tips and tricks

On a previous episode we saw the basic usage of the AmazingPrint gem.

That's the basic usage of the gem. It's very nice to have a way to pretty print strings and to put some color to our outputs.

But that's not the end of the story.

I want to show a couple of tricks I developed over the years to use AmazingPrint to assist my day to day work.

Hash printing

Both tricks hinge on the fact that I tend to debug using ap and puts most of the time.

Say we have this code, that lists all the index files under the episodes directory, and then extracts the slugs for each of them, looking at the directory each of them is located under.

require "rake/file_list"
require "pathname"

episode_indexes = Rake::FileList["/sandbox/episodes/*/index.{md,org,adoc}"]

episode_slugs = episode_indexes.map { |episode_index|
  Pathname(episode_index).dirname.basename.to_s
}

If you don't use FileLists and Pathnames often, you can easily get lost in this code.

When I have to write code like this one, I use the ap method heavily. I'll probably do the following experiments on a test file that I'm running after each change.

First lets write that first FileList, but starting from the beginning.

require "rake/file_list"
require "pathname"

ap Rake::FileList["/sandbox/episodes"]
$ $ ruby amazing-print.rb
["/sandbox/episodes"]

Ok, this just shows us the root directory of the project. Maybe add an asterisk to show all the folders inside.

require "rake/file_list"
require "pathname"

ap Rake::FileList["/sandbox/episodes/*"]

This shows all the directories. But we need just the ones that contain an index.md, index.org or index.adoc file, since those are our actual episodes. Anything else is just noise.

$ ruby amazing-print.rb
["/sandbox/episodes/amazing-print", "/sandbox/episodes/faker", "/sandbox/episodes/pathname", "/sandbox/episodes/seeing-is-believing", "/sandbox/episodes/tempdir", "/sandbox/episodes/tempfile"]

But I don't want to loose track of all the directories in the output just yet so I can compare them.

And here is where the trick comes in. I could just print one and then print the other.

ap Rake::FileList["/sandbox/episodes/*"]
ap Rake::FileList["/sandbox/episodes/*/index.{md,org,adoc}"]

But if we're doing this in a codebase with some extra output, this could get confusing

$ ruby amazing-print.rb
["/sandbox/episodes/amazing-print", "/sandbox/episodes/faker", "/sandbox/episodes/pathname", "/sandbox/episodes/seeing-is-believing", "/sandbox/episodes/tempdir", "/sandbox/episodes/tempfile"]
["/sandbox/episodes/amazing-print/index.adoc", "/sandbox/episodes/faker/index.adoc", "/sandbox/episodes/pathname/index.md", "/sandbox/episodes/seeing-is-believing/index.md", "/sandbox/episodes/tempdir/index.adoc"]

So instead of doing so, we're going to print a hash, where the keys are some sort of explainatory symbol. and the values are the actual method calls.

We don't even need too descriptive keys, just something that makes sense to us at that moment. This code will get axed very soon.

ap({
     all: Rake::FileList["/sandbox/episodes/*"],
     episodes: Rake::FileList["/sandbox/episodes/*/index.{md,org,adoc}"]
   })

And now we can actually see a more explicit output.

$ ruby amazing-print.rb
{
    :all => ["/sandbox/episodes/amazing-print", "/sandbox/episodes/faker", "/sandbox/episodes/pathname", "/sandbox/episodes/seeing-is-believing", "/sandbox/episodes/tempdir", "/sandbox/episodes/tempfile"],
    :episodes => ["/sandbox/episodes/amazing-print/index.adoc", "/sandbox/episodes/faker/index.adoc", "/sandbox/episodes/pathname/index.md", "/sandbox/episodes/seeing-is-believing/index.md", "/sandbox/episodes/tempdir/index.adoc"]
}

This gets really useful when we have a lot of variables or method names that are somewhat similar, either in names or values. I use this all the time.

dg* snippets

My second trick makes use of my editor's capacity to add custom text snippets. You can do it without snippets, but having them makes trivial to implement.

Say we have some code with lots of logging or text output.

We want to see if our code has actually reached a specific point in the execution chain, but it's difficult to see.

Take a look at this piece of very contrived source code.

We want to see if step C has been performed and we don't want to jump into the debugger.

puts "Starting execution"

puts "Performing step pre A"
puts "Doing something"

puts "Doing something else"

puts "Performing step A"

if flag_one
  puts "Performing step B"
  if flag_two
    puts "Performing step C"
  end
end

puts "Performing step D"
puts "Performing step E"
puts "Performing step F"

But from the output is hard to quickly assess if it was run, at least for me.

So what I do is go to the line I want to see printed, type the string dg, which is the trigger for my snippet

if flag_two
  dg
  puts "Performing step C"
end

and then expand it. The expanded string adds a bit of whitespace at the beginning of the line in order to force some indentation. Then it adds a couple of > characters in order to make the line longer. Then adds the file we're currently in, and the line number. This way, if we run this file as a step on a bigger context, we know exactly where we are.

if flag_two
  puts "  >>>>> #{__FILE__}:#{__LINE__}".red
  puts "Performing step C"
end

We still can't see anything different, which means the path is not being triggered.

But if we change one of those flags we're switching on, we can see our red marker.

I have snippets for several colors in case I want to drop in more than one marker.

dg
dgr
dgg
dgp
dgy
dgc

Which expand to:

puts "  >>>>> #{__FILE__}:#{__LINE__}".red
puts "  >>>>> #{__FILE__}:#{__LINE__}".red
puts "  >>>>> #{__FILE__}:#{__LINE__}".green
puts "  >>>>> #{__FILE__}:#{__LINE__}".purple
puts "  >>>>> #{__FILE__}:#{__LINE__}".yellow
puts "  >>>>> #{__FILE__}:#{__LINE__}"

and renders as:

Of course, we could combine this trick with the one before,

# ...
puts "Performing step A"

ap({
     flag_one: flag_one,
     flag_two: flag_two,
   })

if flag_one
  puts "Performing step B"
  if flag_two
    puts "  >>>>> #{__FILE__}:#{__LINE__}".red
    puts "Performing step C"
  end
end

puts "Performing step D"

and see exactly which flag is the one that's failing us.

Conclusion

We learned how to use AmazingPrint and how I actually use it in my day to day work.

I'm sure there are a million other tips and tricks for using this gem and I'll be sharing them if they come my way. I encourage you to try out both AmazingPrint and the tricks I showed you in this episode. And please, if you have any other tips, leave them in the comments so other folks can benefit.

Thanks for watching and I'll see you in the next one.

Comments