Inspecting the Contents of a Ruby Gem

From time to time you’ll need to inspect the contents of a locally installed Ruby gem. For instance - I needed to check the contents of my Jekyll theme (minima) earlier today, so I could override something that was hardcoded there.

Each installed gem corresponds to a directory in your local file system, so all you need to do is find out where a particular gem resides. There are several ways to do this. The first option is to use Ruby’s gem command directly:

$ gem info minima

*** LOCAL GEMS ***

minima (2.5.1)
    Author: Joel Glovier
    License: MIT
    Installed at: /home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0

    A beautiful, minimal theme for Jekyll.

$ gem contents minima

The gem contents command is extra useful as it effectively combines something like this in a single step:

$ gem info gem-name
$ cd gem-dir
$ ls -l

You don’t really have to visit the folder in which the installed gem resides to play with its contents. With gem unpack you can dump the contents of any gem in the current folder:

$ gem unpack minima
Unpacked gem: '/home/bozhidar/minima-2.5.1'
# You can also specify the folder in which to unpack the gem like this
$ gem unpack minima --target ~/unpacked-gems

The main benefit of using gem unpack is that you can’t modify some gem by accident, with the added bonus that you’ll get an easier to remember directory path.

An alternative option is to use bundler to procure the gem installation directory information (assuming you’re using it):

$ bundle info minima

 * minima (2.5.1)
        Summary: A beautiful, minimal theme for Jekyll.
        Path: /home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1

# This is a variant of the previous command that returns only the gem path
$ bundle info --path minima
# Alternatively you can use Bundler's show command
$ bundle show minima

Unfortunately, there’s no bundler equivalent of the gem contents command, but that’s not that big of deal.

Bundler and gem actually have one more extremely useful command that will directly open the gem’s folder in your default editor:

$ gem open minima
# or alternatively
$ bundle open minima

Probably that’s my favorite way to navigate to an installed gem’s contents.

As you can imagine it’s pretty straight-forward to change the behavior of a gem - just go to its directory and edit some of its contents. That’s an useful debugging technique, but it opens up one question - how to restore a gem to its original pristine state? Well, turns out there’s a command for this as well:

$ gem pristine gem-name
# or alternatively
$ bundle pristine

The bundler command will restore all installed gems for a particular bundle to their original state.

That’s all I have for you today. I hope you learned something useful! Keep hacking!

Farewell Disqus

Just wanted to let you know that for various reasons I’ve migrated the comments of (think) from Disqus to Hyvor Talk. I’ve described the process in a dedicated article.

One small difference in the migration procedure for (think) in particular is that because it uses the Hydeout Jekyll theme, I’ve opted to change _includes/comments.html instead of _layouts/posts.html. Here’s how it looks now:

{% if page.comments != false %}
<section class="comments">
    <div id="hyvor-talk-view"></div>
    <script type="text/javascript">
     var HYVOR_TALK_CONFIG = {
         url: '{{ page.url | absolute_url }}',
         id: '{{ page.url | absolute_url }}'
    <script async type="text/javascript" src="//"></script>
{% endif %}

There are numerous ways to approach this, but that one seemed like the optimal one to me.

The migration process converted all Disqus comments to guest comments in Hyvor, which is a bit unfortunate, but unavoidable. Still, I think that’s a small price to pay for a privacy-focused solution with better usability. I hope that this transition will result in more interesting discussions taking place here!

Basic Git Setup

Every time I change my computer or my operating system1, one of the first things I have to do is to configure Git. This article simply covers the basic Git settings that I always adjust.

So, here’s what I’d typically do:

# user identity
$ git config --global "Bozhidar Batsov"
$ git config --global
# editor to use by default for things like commit messages
$ git config --global core.editor emacs
# auto-rebase when pulling
$ git config --global pull.rebase true
# auto-convert CRLF to LF
# useful if you're working on Windows and there are people on your team who are working on Unix
$ git config --global core.autocrlf true
# the name of the primary branch (formerly known as master)
# that's a pretty recent setting, but it's useful for new projects
$ git config --global init.defaultBranch main

I guess for many people it’d also be useful to specify their preferred merge tool:

$ git config --global merge.tool some-tool

To me, however, that’s irrelevant as almost all of the time I’m interacting with Git via Magit. If you’re looking for an excuse to try out Emacs, you’ll be hard pressed to find a better excuse than Magit.

The global Git user settings are simply stored under ~/.gitconfig, so you can easily review and update them there as well. You can check your current configuration by running this command:

$ git config --list

Note that this effective configuration would be a combination of OS-wide config (e.g. /etc/gitconfig), your user-wide config (e.g. ~/.gitconfig) and the config of the Git repo that you’re currently in (e.g. `repo/.git/config).

When working on company projects, I would change for each company repository my email to whatever my work email is:

$ cd company-project
$ git config

If you’re working on multiple company repositories the above solution will quickly become annoying. In such cases you may want to use Git conditional includes, which basically allow you to include a different configuration file in your main Git config, based so on some rules.2 In our case we can have a different configuration for the e-mail based on the repository directory path. Here’s an example .gitconfig to illustrate this:

[includeIf "gitdir:personal/"]
  path = .gitconfig-personal
[includeIf "gitdir:work/"]
  path = .gitconfig-work

Now, any Git repository under a folder called personal (anywhere on your file system) will use the personal email address, and any repository under a folder called work will use your work email address. This matches my preference to keep my personal projects under ~/projects/personal and the work under ~/projects/work.

The contents of .gitconfig-personal can be something like:

  email =

And the contents of .gitconfig-work can be something like:

  email =

And that’s it. Turns out my basic Git setup is pretty basic. Check out this section of the official docs for an expanded coverage of the topic. You can find way more configuration options here.

That’s all I have for you today. I’d appreciate it if you shared in the comments some snippets of Git configuration that you consider essential.

  1. This has been happening quite often recently and I’ll cover it in a separate article or two. 

  2. Special thanks to my readers who suggested this setup to me. 

Rename Multiple Files in Linux

From time to time we need to rename a bunch of files according to some pattern. One simple example that comes to mind is that recently I noticed that some articles in my blog had a .md extension and some had a .markdown extension. I don’t like inconsistencies, so I wanted them all to have a .md extension. Bellow I’ll cover several ways to do this on Linux1. All the examples will assume you’re renaming files in the same folder, but it’s typically trivial to extend them to a directory tree by combining a command with find -exec or extended globbing (e.g. **/*.markdown). So, here we go.

One simple option is to use the rename utility from the util-linux package:

$ rename markdown md *.markdown

Basically you’re doing a text substitution in the list of files passed to the command. The problem with this is that it won’t work properly in cases like markdown.markdown. Fortunately, Debian and Debian-derived distributions (e.g. Ubuntu) ship a more powerful version of this command, that’s written in Perl, and supports regular expressions.

$ sudo apt install rename
$ rename 's/\.markdown$/.md/' *.markdown

The regular expression allows us to be specific about the match and prevents the problem listed above. By the way, generally it’s a good idea to first preview any changes that the command might perform with the -n option:

$ rename -n 's/\.markdown$/.md/' *.markdown

Super handy!

If you don’t want to use an external command you can leverage some shell features instead:

$ for f in *.markdown; do
    mv -- "$f" "${f%.markdown}.md"

This relies on some relatively advanced substitution features of Bash and Zsh, that are beyond the scope of today’s article, but it gets the job done. Interestingly enough, Zsh provides a much simpler and way more powerful way to tackle mass rename, via the zmv utility it bundles:

$ zmv '(*).markdown' '$'

While zmv doesn’t use regular expressions, it’s matching and substitution functionality should cover pretty much everything you decide to throw at it. Note that zmv is usually not enabled by default and you might have to load it manually before using it:

$ autoload -Uz zmv

Notice also that in the first argument of zmv you’ve specifying both the search pattern for files and substitution groups you can use in the second argument. You can do way more complex renamings with zmv:

# rename dir1/file.txt, dir2/file.txt, etc to file-1.txt, file-2.txt, etc
$ zmv zmv 'dir(*)/file.txt' 'file-${1}.txt'

Obviously sky is the limit here, although this applies to the Perl version of the rename command as well. One cool thing about zmv is that you just like with rename you can preview the changes it’s going to do with the -n option.

$ zmv -n '(*).markdown' '$'

This will help you to quickly find the right pattern for the mass rename you’re trying to perform.

That’s all I have for you today. I’ve barely scratched the surface of what’s possible, but I still hope you learned something new and useful. Keep hacking!

  1. Most of those suggestions should also work on other Unix-like operating systems like macOS, *BSD, etc. 


That’s going to be one really short post. I just wanted to share with you that I’ve been using HEY (a new e-mail service from Basecamp) for the past few months and overall I’m quite happy with it. This article is not a review of HEY, it’s more an announcement that now you can reach me at if you’re into e-mail.1

I’ll keep my e-mail backed by GMail for the time being, as I didn’t really have any fundamental issues with GMail, other than my dislike for Google’s (mis)handling of private data. That being said, I want to expand my usage of HEY going forward. I’ve already moved there all my newsletter subscriptions, as HEY has great support for newsletter, and I’ve also been trying to have most of my conversations with people over HEY as well. To help with this I’ve updated most public mentions of my e-mail (e.g. on GitHub) to point to the new HEY-powered address.

HEY is definitely not perfect (e.g. the message editor feels inferior to what you get in GMail, and the same goes for search functionality), but I love the philosophy of the service and I’m always open to trying new ideas and workflows. I hope that down the road I’ll manage to break free from the grip of Google and leave GMail for good, but that mostly depends on how HEY will evolve and how well will I adapt to using it. In the mean time - I’m looking forward to chatting with more of you over HEY!

  1. I hope it’s clear that you have to replace myfirstname with my real first name. :D