16 Dec 2020
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
Homepage: https://github.com/jekyll/minima
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
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/LICENSE.txt
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/README.md
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/_includes/disqus_comments.html
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/_includes/footer.html
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/_includes/google-analytics.html
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/_includes/head.html
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1/_includes/header.html
...
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.
Homepage: https://github.com/jekyll/minima
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
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1
# Alternatively you can use Bundler's show command
$ bundle show minima
/home/bozhidar/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/minima-2.5.1
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!
08 Dec 2020
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_WEBSITE = YOUR_SITE_ID; // DO NOT CHANGE THIS
var HYVOR_TALK_CONFIG = {
url: '{{ page.url | absolute_url }}',
id: '{{ page.url | absolute_url }}'
};
</script>
<script async type="text/javascript" src="//talk.hyvor.com/web-api/embed"></script>
</section>
{% 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!
22 Nov 2020
Every time I change my computer or my operating system, 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 user.name "Bozhidar Batsov"
$ git config --global user.email bozhidar@example.com
# 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 user.email bozhidar@company.com
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. 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:
[user]
email = bozhidar@home.com
And the contents of .gitconfig-work
can be something like:
[user]
email = bozhidar@work.com
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.
21 Nov 2020
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 Linux. 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
rename(2008-05-05-first-post.markdown, 2008-05-05-first-post.md)
rename(2008-06-16-das-keyboard.markdown, 2008-06-16-das-keyboard.md)
rename(2008-06-19-emacs-rails.markdown, 2008-06-19-emacs-rails.md)
rename(2008-07-27-zsh-prompt.markdown, 2008-07-27-zsh-prompt.md)
rename(2008-09-29-singleton-java-ruby.markdown, 2008-09-29-singleton-java-ruby.md)
rename(2009-05-04-switch-string-idiom-java.markdown, 2009-05-04-switch-string-idiom-java.md)
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"
done
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' '$1.md'
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' '$1.md'
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!
19 Oct 2020
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 myfirstname@hey.com
if you’re into
e-mail.
I’ll keep my @batsov.com
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!