ruby whitespace gotcha

02/23/13

One of the things I love about ruby is that whitespace doesn't matter. At least that's what I thought.

I tried writing a lambda using the short hand version, but it blew up:

x = -> (a,b) { a + b } 
# syntax error, unexpected tLPAREN_ARG, expecting keyword_do_LAMBDA or tLAMBEG

It turns out in ruby 1.9.3, you can't have a space between the -> and the args. That's very un-rubylike to me. Luckly it works with the extra whitespace in ruby 2.0

x = ->(a,b) { a + b }  # good
x = -> (a,b) { a + b } # bad

frozen ssh connections

02/16/13

One of the most frustrating things about being ssh'd into a server is when the connection freezes. I have to kill the local terminal window and fire up a new one. I recently came across a more elegant way to kill the frozen ssh connection: <enter>~. That will instantly kill any ssh connection.

bundler remembers

02/09/13

Be careful using the --without flag with bundle install. I ran bundle install --without=test but then decided I needed the test group on that machine so I ran bundle install, but no test gems were installed.

After a little bit of google, I found out that unlike most command line flags, bundler remembers the --without flag for subsequent calls. I ran bundle install --without=nothing and my test group was installed. Bundle install also remembers the --path flag.

rspec-given gem

02/02/13

I was watching jim weirich's peep code video and he used his rspec add-on rspec-given. I love the syntax so much more than the standard rspec syntax. It reads more like the way I think. It feels like it leads to better organized and more concise tests.

The gem replaces (adds to) the let/before(:each)/it methods with Given/When/Then methods. Like cucumber (gherkin), Given is the setup, When is the action, and Then is the post-condition checks. Rspec-gvien also adds the Invariant keyword which is like a Then block that is ran for each test.

The project readme has an outstanding example using stacks.

rvm and wrapping gems

01/26/13

Sometimes interacting with gems installed under rvm through capistrano can be a little painful. To get gem's command line bins to work you have to create an rvm wrapper.

Here is the synax for creating a wrapper:

rvmsudo rvm wrapper ruby-1.9.3@gemset scope binary
  • rvmsudo is optional. it depends on how your system is setup.
  • ruby-1.9.3@gemset is the version of ruby and the gemset name you want the gem running in.
  • scope is the prefix of the command.
  • binary is the name of the gem's binary you want to wrap.

example

Let's say we wanted to wrap jazor.

rvm wrapper ruby-1.9.3-p125@tools run jazor

This creates the file: .rvm/bin/run_jazor and now we can run the jazor gem while we are working in any gemset.

wrapper contents

#!/usr/bin/env bash

if [[ -s "/Users/me/.rvm/environments/ruby-1.9.3-p125@tools" ]]
then
  source "/Users/me/.rvm/environments/ruby-1.9.3-p125@tools"
  exec jazor "$@"
else
  echo "ERROR: Missing RVM environment file: '/Users/me/.rvm/environments/ruby-1.9.3-p125@tools'" >&2
  exit 1
fi

list all cron jobs

01/19/13

I was trying to figure out how to list all cron jobs on the on a server and I found the answer on stack overflow.

Here's the answer in bash script form:

#!/bin/bash
for user in $(cut -f1 -d: /etc/passwd) 
do
  echo $user 
  crontab -u $user -l
  echo " "
done

and on a single line (for copy/paste purposes):

for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; done

Single line with sudo:

for user in $(cut -f1 -d: /etc/passwd); do echo $user; sudo crontab -u $user -l; done

setting up tmux

01/12/13

If you spend long periods of you day playing around in the command line, you should be thinking of installing tmux.

what is tmux?

The official definition is:

tmux is a terminal multiplexer: it enables a number of terminals (or windows), each running a separate program, to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached.

The tl;dr is:

tmux lets you open a million terminal windows but without making a mess.

installation ( on a mac )

As long as you have homebrew installed, you can run:

$ brew install tmux

That's it.

configuring tmux

The tmux is looking for the config file ~/.tmux.conf by default.

Interacting with tmux is by an action key followed by a key command. By default the action key for tmux is ctrl+b. I find that a little unwieldy to type, so I switched it to be ctrl+a

# allow more colors
set -g default-terminal "screen-256color"

# replace action key as C-a
set-option -g prefix C-a

# C-a C-a will swap to last used window
bind-key C-a last-window

# kill the session
bind R kill-session

# reload the config file
bind r source-file ~/.tmux.conf

vim-ify tmux

I find it much easier to interact with anything using the vim-style key bindings. Tmux is no exceptions.

# use vim keybindings
set-window-option -g mode-keys vi

# nav keys
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# resize panes
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# split panes
bind s split-window -v
bind v split-window -h

# start index with 1. very un-dev
set -g base-index 1
setw -g pane-base-index 1

mac clipboard settings

Tmux doesn't play well with the mac osx clipboard. Thoughbot has a pretty awesome blog post on how to get copy and paste working again. Here is a quick summary:

Install the homebrew package reattached-to-user-namespace

$ brew install reattach-to-user-namespace

and add these bindings to your .tmux.conf. Now copy is ctrl-a + ctrl-c

# reattach to user namespace
set-option -g default-command "reattach-to-user-namespace -l zsh"
# bind copy
bind C-c run "tmux show-buffer | reattach-to-user-namespace pbcopy"
# bind paste
bind C-v run "reattach-to-user-namespace pbpaste | tmux load-buffer - && tmux paste-buffer"

working with the mouse

It's always nice to have the option. Sometimes when I'm combing through log files and I find it a easier to use the mouse. It also makes it easier to pair with people if they aren't framiliar with tmux.

set-window-option -g mode-mouse on
set-option -g mouse-select-pane on
set-option -g mouse-resize-pane on
set-option -g mouse-select-window on

making it pretty

This is the theme I have wasted too many hours tweaking. Feel free to do the same.

set -g status-fg white
set -g status-bg black
setw -g window-status-fg green
setw -g window-status-bg default
setw -g window-status-attr dim
setw -g window-status-current-fg black
setw -g window-status-current-bg magenta
setw -g window-status-current-attr dim
set -g pane-border-fg green
set -g pane-border-bg black
set -g pane-active-border-fg green
set -g pane-active-border-bg white
set -g message-fg white
set -g message-bg black
set -g message-attr bright

# statusbar
set -g status-utf8 on
set -g status-interval 60
set -g status-left-length 40
set -g status-justify centre
set -g status-left "#[fg=green]#(git rev-parse --abbrev-ref HEAD)"

ruby case statement gotcha

01/05/13

Last week I ran into a switch gotcha while writing tests for some legacy code. I always assumed that the ruby case statement worked using the == method. It turns out it uses the === which behaves a little differently.

def show_message(target)
  case target
  when Manager
    puts "Target is a Manager"
  else
    raise "Not a Manager class instance"
  end 
end

class Manager < Employee; end
bender = Manager.new 

show_message(Manager) # ERROR: Not a Manager class instance
show_message(bender)  # target is a Manager

my git aliases

12/28/12

This is a breakdown of the git aliases I use most. Of all the aliases, git patch and git amend have been the most revolutionary for my workflow. My complete gitconfig and other dot files can be found in my dot_files repo on github.

# found in ~/.gitconfig 
[alias]
  st = status --short
  co = checkout --
  dc = diff --cached
  patch  = add -p 
  amend  = commit --amend
  last   = show HEAD
  undo   = reset --soft HEAD~1
  unstage = reset;
  us      = reset; 
  l  = log --pretty=oneline --abbrev-commit --graph --decorate
  la = log --pretty=oneline --abbrev-commit --graph --decorate --all

git st shows the current status of the repo in a format that is actually human readable. The first column is for shows the changes that are currently in staging. The second column shows the changes that are not staged. and last is the file name.

$ git st 
 M Gemfile                  # modified but not staged
A  app/models/post.rb       # new file added to staging
MM spec/models/post_spec.rb # some changed staged and others not staged  
?? routes.txt               # untracked file

git co is aliased so it will only work with files and not branches. I like having a separate command for restoring files and switching branches.

$ git co master
error: pathspec 'master' did not match any file(s) known to git.
$ git checkout master
Already on 'master'

git dc shows a diff of all the changes currently staged.

git patch does an interactive add. It walks through each unstaged change in the command line and prompts you if you want to stage the change hunk, split the hunk, or edit it.

$ git patch 
@@ -46,3 +45,4 @@ 
   gem 'pry'
+  gem 'spork'
   gem 'irbtools', :require => false
   gem 'rspec-rails'
Stage this hunk [y,n,q,a,d,/,K,j,J,g,e,?]?

git amend amends the previous commit with the changes currently staged.

git last shows all the modifications in the previous commit (HEAD).

git undo removes the last commit but leave the changes locally.

git unstage and git us will unstage all the staged modifications.

git l shows a version of the log that is human readable

$ git l
* aacddd1 (HEAD, master) fixed a logic error
* c62fd55 added different colors
* 88e591e (heroku/master) minor ui update
* ad5ccdd minor ui changes
* 94978a8 made the textarea more usable
* 051e75b removed ubuntu mono bold
* d4937ae minor admin changes

git la shows the above log and the log of the other branches an staged files.

$ git la
* 88e591e (heroku/master) minor ui update
* ad5ccdd minor ui changes
* 94978a8 made the textarea more usable
| *   cf4fafc (refs/stash) WIP on master: 051e75b removed ubuntu mono bold
| |\  
|/ /  
| * 7dbac55 index on master: 051e75b removed ubuntu mono bold
|/  
* 051e75b removed ubuntu mono bold

git bisect

12/23/12

My favorite tool in git is quickly becoming git bisect. It's useful for tracking down code that caused a bug in the system. When I was looking for a bug, I would often read through the commit history on github and through trial and error, try and track down the bug. I admit, it was not the most productive process.

Like everything in git, the first time I was shown bisect, I was confused and it seemed like there was a lot of magic. I thought, "How could this be easier than paging through the commits? I'm really good at that." After a few times using bisect, I've found it extremely powerful and much easier to use than I thought.

First run git bisect start and enter a good commit before the bug existed and a commit where you know the bug exists.

$ git bisect start
$ git bisect good f6082a4
$ git bisect bad HEAD

Now git will split the difference between the good commit and the bad commit and checkout the halfway-point commit. In the current commit, reproduce the bug and see if it still exists. This can be as simple as running a test, greping for some code, or refreshing the web page.

$ rspec ./spec/model/post_spec.rb:42
# or
$ grep -rin "some missing text" app/views

After you determine if the current commit has the bug or not, mark it good or bad.

$ git bisect good
# or
$ git bisect bad

Bisect will continue splitting the difference between the last good commit and the first bad commit until it finds a bad commit that immediately follows a good commit. At the end it will print out the bad commit so you can see what changed.

c4184463ed735e2777c33f9ae723fe0f23db9d1d is the first bad commit
commit c4184463ed735e2777c33f9ae723fe0f23db9d1d
Author: Bender Rodriguez
Date:   Sun Dec 16 12:19:03 2012 -0800

added markdown parser endpoint

:040000 040000 debd974 e66153 M      app
:040000 040000 d1509a9 a3e8ee M      config
:040000 040000 8636a5c 2d9105 M      spec