String#tr in ruby (like tr in Linux) complete with figuring out slashes.

It seems like I’ve seen quite a few programming puzzles in the last few weeks that involved translating mistyped input in which the hands were shifted (right) on the keyboard. My first thought was the tr utility in *nix operating systems, but didn’t immediately go looking for or notice that ruby has a tr method on string. However, after doing a trivial implementation involving keyboard rows like the following, I stumbled on the tr method.

  # initial array of characters/strings to shift back to the left with [index-1]
  KEYBOARD_ROWS= [
    '`1234567890-=',
    'qwertyuiop[]\\', # need to escape the backslash or else debugging pain
    "asdfghjkl;'", # double-quotes here because single quote embedded
    'zxcvbnm,./',
    '~!@#\$%^&*()_+',
    'QWERTYUIOP{}|',
    'ASDFGHJKL:"',
    'ZXCVBNM<>?'
  ].join

Attempting to rewrite this for .tr presented a few challenges, however. If you are substituting for \, -, or ~, you have to escape the characters. You also have to escape them from their string representation, which makes for some head-spinning levels of escaping (zsh users who run shell commands through kubectl might be familiar with this pain as well):

# puts '\\~-'.tr('\\', 'a') # doesn't match because \ is passed to tr and not escaped
a~-
# puts '\\~-'.tr('\\\\', 'a') # now \\ is passed to tr, which is
a~-
# puts '\\~-'.tr("\\\\\\", 'a') # with double quotes, you need an extra pair, for 6 total.
a~-
# puts '\\~-'.tr('\\~', 'b') # the escaping backslash needs to be doubled
\b-
# puts '\\~-'.tr("\\\~", 'b') # the escaping backslash needs to be tripled
\b-
# puts '\\~-'.tr('\\-', 'c') # the escaping backslash needs to be doubled
\~c
# puts '\\~-'.tr("\\\-", 'c') # the escaping backslash needs to be tripled
\~c

So if you’re going to use translate to “shift” hands back to the left, the two arguments to tr, SHIFTED_KEYBOARD_ROWS and UNSHIFTED_KEYBOARD_ROWS would have to be defined with the following escaping:

  SHIFTED_KEYBOARD_ROWS =
    [
      '1234567890\\-=',
      'wertyuiop[]\\\\', # 4x backslash = backslash
      "sdfghjkl;'",
      'xcvbnm,./',
      '!@#\$%\^&*()_+',
      'WERTYUIOP{}|',
      'SDFGHJKL:"',
      'XCVBNM<>?'
  ].join

  UNSHIFTED_KEYBOARD_ROWS= [
    '`1234567890\-',
    'qwertyuiop[]', # need to escape the backslash or else debugging pain
    'asdfghjkl;',
    'zxcvbnm,.',
    '~!@#\$%\^&*()_',
    'QWERTYUIOP{}',
    'ASDFGHJKL:',
    'ZXCVBNM<>?'
  ].join

  def self.translate(string)
    string.tr(SHIFTED_KEYBOARD_ROWS, UNSHIFTED_KEYBOARD_ROWS)
  end

mkdir with intermediate directories and rails generate view spec

I’ve been running into a lot of cases where I need to build intermediate directories for a path. Apparently, the correct option is -p

mkdir -p dir/tree/to/make

From the mkdir man page:

     -p      Create intermediate directories as required.  If this option is not specified, the full path prefix of each operand must already exist.  On the
             other hand, with this option specified, no error will be reported if a directory given as an operand already exists.  Intermediate directories
             are created with permission bits of rwxrwxrwx (0777) as modified by the current umask, plus write and search permission for the owner.

I’ve assigned it an alias in my .zlogin:

alias mkdirtree='mkdir -p'

Of course, this all came about because I needed to an generate rspec file for a view, which can be done with:

rails g rspec:view name/of/controller action another_action

Results in the following files in the spec/name/of/controller directory:

action.html.erb_spec.rb
another_action.htl.erb_spec.rb


Script to Change the Title of Your Terminal Window

I decided to create a small bash script to change the title of my current terminal window in bash on my Mac:

change_title:
#!/bin/bash

echo -ne "33]0;" $* "07"

The script then runs as:

change_title Your title goes here.


rush: the ruby shell

rush.

rush is a replacement for the unix shell (bash, zsh, etc) which uses pure Ruby syntax. Grep through files, find and kill processes, copy files – everything you do in the shell, now in Ruby.

Install rush

If you’re running windows and have Ruby installed, go to Start->Ruby-{version}->RubyGems->RubyGems Package manager and type in “gem install rush” at the cmd prompt.