Keith Smiley - Who?

I've been using Objective-C and C++ for a while now. While I feel like I know them pretty well I had absolutely no grasp on C itself. Obviously when using those languages you pick up a bit about data types and conditionals but you couldn't take that knowledge and completely write something non-trivial in C. Because of this I figured it might be worth some time so I started looking for viable resources. In that search I found Learn Code the Hard way. They publish physical and online books on different languages and so far I've found it to be a delight. It's not your typical easy walk through and on lessons 17 (of 51) you'll start actually having to think. This for me has been much better so far than typically online learning where the introductory course ends on how to write a for loop.

If you're interested in picking up a C background I would definitely recommend you start here.

NSTableView vim keys

I'm currently working on a OS X application that uses a few different NSTableViews to display user data. I was testing them out a bit to make sure multiple deletions worked correctly from my database and I found myself pressing 'j' and 'k' to try and move down and up. I decided it would be pretty cool to implement those two vim shortcuts into my table view just in case anyone else thinks like me.

This functionality already exists in The Hit List an awesome GTD app that has a lot of baggage with me, and I'm sure it exists in other applications as well.

In my NSTableView subclass' keyDown: method I tried a few things.

Attempt 1: First I tried to re implement the functionality myself. In retrospect this doesn't make any sense but at first it was pretty simple. It looked something like this.

NSUInteger flags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
NSNumber *shiftPressed = (flags & NSShiftKeyMask);
 
if ([theEvent keyCode] == 38) { // j
    NSUInteger index = [[self selectedRowIndexes] lastIndex] + 1;
    if ([shiftPressed boolValue]) {
        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:YES];
    } else {
        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
    }
} else if ([theEvent keyCode] == 40) { // k
    NSUInteger index = [[self selectedRowIndexes] lastIndex] - 1;
    if ([shiftPressed boolValue]) {
        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:YES];
    } else {
        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
    }
}

The issue with this is the way NSTableView typically expands it's selection. I think of it as a pivot point where you start. Then you go up and down relative to that point. So if you start at index 2 and go down till index 4, you should have 2 rows selected. Then when you go back up you should deselect the rows and indexes 3 and 4 and select the rows and index 1 and 0. At this point I realized it was more difficult than I realized at first and went in search on another solution.

Attempt 2: The next solution I discovered used the Quartz Event Services APIs.

if ([theEvent keyCode] == 38) { // The letter 'j'
    CGEventRef e = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)125, true);
    CGEventPost(kCGSessionEventTap, e);
    CFRelease(e);
} else if ([theEvent keyCode] == 40) { // The letter 'k'
    CGEventRef e = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)126, true);
    CGEventPost(kCGSessionEventTap, e);
    CFRelease(e);
}

This solution worked perfectly, at first. This mainly emulates a key press with a different key code. So as you can see I was catching j and k and spitting them out as down and up. I spent a few minutes testing this before I remembered that I had sandboxing disabled so I could more easily delete my application support folder while messing with my Core Data stack. There went that solution.

Attempt 3: Before I used the weird CGEventRef solution I tried to create my own NSEvent passing it all the same attributes from the original event (all this code is being used in the keyDown: function of my subclass) but I couldn't figure out how to get the correct character string for the up and down arrows. I typically use Key Codes to get all the possible information you could want about each key you press. But for some keys, including the arrow keys, it returns garbage for the character code. Then I discovered this answer on StackOverflow where there is a brief mention of NSUpArrowFunctionKey. With that I came up with this.

if ([theEvent keyCode] == 38) { // j
    unichar down = NSDownArrowFunctionKey;
    NSString *downString = [NSString stringWithCharacters:&down length:1];
    NSEvent *newEvent =[NSEvent keyEventWithType:NSKeyDown
                                        location:theEvent.locationInWindow
                                   modifierFlags:theEvent.modifierFlags
                                       timestamp:theEvent.timestamp
                                    windowNumber:theEvent.windowNumber
                                         context:nil
                                      characters:downString
                     charactersIgnoringModifiers:downString
                                       isARepeat:theEvent.isARepeat
                                         keyCode:down];
    
    [super keyDown:newEvent];
} else if ([theEvent keyCode] == 40) { // k
    unichar up = NSUpArrowFunctionKey;
    NSString *upString = [NSString stringWithCharacters:&up length:1];
    NSEvent *newEvent =[NSEvent keyEventWithType:NSKeyDown
                                        location:theEvent.locationInWindow
                                   modifierFlags:theEvent.modifierFlags
                                       timestamp:theEvent.timestamp
                                    windowNumber:theEvent.windowNumber
                                         context:nil
                                      characters:upString
                     charactersIgnoringModifiers:upString
                                       isARepeat:theEvent.isARepeat
                                         keyCode:up];
    
    [super keyDown:newEvent];
} else {
    [super keyDown:theEvent];
}

Not the prettiest solution I but one that seems to work perfectly, even sandboxed, to provide the expected behavior in an NSTableView subclass.

Backing up with Capistrano

We all know not backing up has consequences. While losing sentimental files would definitely ruin your day, losing your web server's data could be even worse. I've mentioned before that I use Linode for my server hosting, and while they do offer an automated backup service I decided I'd rather setup my own solution to back up periodically to my local machine.

Many people use rsync to do their server backups. In fact Linode even has a guide on how to set it up (there's a better one here). I decided that instead of a 1 for 1 directory backup, I would prefer to have a tarball) of the contents. While I could've easily done this with a few bash commands from the server that's not particular ideal for my setup. My local machines don't run 24/7 so if I set it up on the server to automate the backup every week, it may try to initiate the backup when my machine was off (I could try to guess when it's on every week but that's not ideal either).

The obvious solution to this is run it from my local machine instead every week. That way once a week when it's powered up it would log in to the server, create the tarball and pull it down. Insert Capistrano ([sudo] gem install capistrano) a RubyGem for 'Remote multi-server automation.' So I wrote a very basic Capfile to automate this for me (replace the path to your www folder accordingly).

load 'deploy'

$SERVER_USER = "username"
$SERVER_IP   = "1.1.1.1"

desc "Backs up server www files"
task :backup, :hosts => $SERVER_IP do
  run "cd /srv; tar -pvczf ~/backup.tar.gz www/"
  run_locally "scp #{ $SERVER_USER }@#{ $SERVER_IP }:~/backup.tar.gz ~/Dropbox/Backups/Server"
end

Then I added this to my crontab on my local machine by running crontab -e and adding the line:

@weekly /Users/ksmiley/.rbenv/shims/cap -f ~/path/to/Capfile backup

I included the path to the Capistrano executable since cron (on OS X) executes tasks with sh, which isn't setup with my $PATH.

Raking Podspecs

I spend a decent amount of time these days helping maintain the CocoaPods specs repo by managing pull requests and issues. CococaPods is an awesome dependency manager similar to Rubygems for Objective-C projects. Unfortunately a lot of submitted podspecs haven't been correctly formatted or they're missing required information. CocoaPods has an awesome build in command pod spec lint that allows you to make sure the spec is valid and complete. Understandably people who are new to CocoaPods trying to submit their libraries are unaware of this awesome tool. Therefore when I look through the pull requests, I like to lint them myself (CocoaPods does utilize Travis but unfortunately it can't do everything).

Since CocoaPods supports multiple versions of Ruby (1.8.7 and 1.9.3) to be complete ideally you'd lint them on both versions. Tools like RVM and rbenv(my tool of choice) make it easy to quickly switch between different versions of Ruby using .rvmrc and .rbenv-version respectively. As you can probably assume I wanted to automate this. So I wrote a quick Rakefile to do this for me.

#!/usr/bin/env rake

# NOTE: Must be using rbenv 4.0 to use `system` and `.ruby-version`
## Set your preferred ruby versions
$V18 = 'system'
$V19 = '1.9.3-p385'
$RBENV = '.ruby-version'

# The gem to use
$GEM = 'cocoapods'

task :default => :lint
task :c       => :clean

desc "Lint podspecs on multiple versions of ruby with rbenv"
task :lint do
  if Dir.glob('*.podspec').count < 1
    puts "No podspecs in #{ Dir.pwd }"
    exit
  end

  existed = versionFileExists?
  if existed
    old_version = currentVersion
  end

  # Loop through all podspecs
  Dir.glob('*.podspec').each do |file|
    # Loop through ruby versions
    2.times do |x|
      version = x == 0 ? $V18 : $V19
      writeVersion(version)

      puts "Linting #{ file } on Ruby version #{ currentVersion }"
      puts lint(file)
    end
  end

  # If the dotfile already existed rewrite the original code
  if existed
    writeVersion(old_version)
  else
    File.delete($RBENV) if versionFileExists?
  end
end

desc "Delete all podspec files"
task :clean do
  Dir.glob('*.podspec').each { |file| File.delete(file) }
  Dir.glob($RBENV).each { |file| File.delete(file) }
end

# Check to see if the current dotfile exists
def versionFileExists?
  File.exists?($RBENV)
end

# Retrieve the current version from the rbenv dotfile
def currentVersion
  File.open($RBENV, "r") { |io| io.read }
end

# Write out a version to .rbenv-version
def writeVersion(version)
  File.open($RBENV, 'w') { |file| file.write("#{ version }") }
end

# Run the lint
def lint(podspec)
  %x[pod spec lint "#{ podspec }"]
end

Spawning iTerm Windows

I've recently been searching around for a good way to 'spawn' an iTerm window (no I don't use tabs in iTerm), at the pwd in my current iTerm window. I couldn't find any good way to do it so I jumped in to AppleScript Editor and made something happen.

on run argv
    tell application "iTerm"
        set t to make new terminal
        tell t
            activate current session
            launch session "Default Session"
            tell the last session
                write text "cd \"" & item 1 of argv & "\"; clear; pwd"
            end tell
        end tell
    end tell
end run

I then added it to my zsh aliases with:

function spawn {
  osascript ~/Dropbox/Code/Applescript/Spawn/SpawniTerm.applescript $PWD
}

Now I can call spawn from any iTerm or Terminal window to open a new iTerm session wherever I called it from.