Keith Smiley

The 'Best' Text Editor

Permalink

I'm tired of people asking about the 'best' IDE for xyz purpose. The answer to this question is there is no best. The answer is always 'it depends.' Not only does it depend on what you're doing but more importantly it depends on you. It depends on your work flow. It depends tons of other indiscernible factors.

It seems like people think they work in exactly the same way as enough other people. That asking this question will yield a useful result. The truth is that there are far fewer text editors than people who need text editors so it's impossible not to overlap with someone. We misconstrue this overlap in thinking that now this person knows exactly what we want. In reality they just happen to share some arbitrary subset of the way we work and therefore ended up with the same text editor.

So how can you decide which editor is best for you? Try them. This sounds obvious to you? Good, this article is not for you and you can safely leave now. These days text editors are either free, cheap or have trials. So download them all try them out and see if they make sense to you. Weed out the ones you really hate or the ones that crash and spend a little more time with the remaining editors. Some, like Vim, you may have to spend a little more time with to grasp but this still doesn't seem like a high order.

But for the love of coding please stop asking questions on StackOverflow and similar sites where you expect people to throw their vote into the hat for the 'best' editor and make a decision for yourself.

Objective-C on Travis-CI

Permalink

Recently Travis added support for Objective-C and there for OS X and iOS projects for continuous integration testing. I gather that people have previously done this with self-hosted dedicated Jenkins machines but since Apple is so aggressive about dropping support for previous versions of the OS it seems like a pain to have to replace your build server every few years. Enter Travis, a great hosted continuous integration server that hosts a huge amount of open source projects. I figured with this new support I could host some of my smaller libraries just to set how well it worked. The initial setup process was a bit tedious but I eventually got it to work.

Assumtions:

  • You have a test framework already integrated with your project (I like Specta/Expecta)
  • You have your project on Github in a public repository. Travis offers a pro account if you'd rather

Steps

  • Create a .travis.yml file in the root of your repository (leading dot is intentional). For many projects a file may just look like:
language: objective-c

By default Travis runs this script for Objective-C projects I was informed on Twitter that the current script that runs Objective-C projects is actually located here. The was originally created by Justin Spahr-Summers here. This script seems to run my projects without any issue, they just occasionally require more initial setup (we'll get to that).

  • Enable your repository in Travis' settings. From your Travis profile page (after signing in with Github) you should see a list of your repositories, you may have to press 'Sync now', where you can switch on the repository you're planning on adding.

  • Configure your project within Xcode. As I assumed above you already have a test target setup. You do have to do a few things in Xcode to make everything work correctly.

  1. Go to 'Manage Schemes' in Xcode. Manage Schemes
  2. Check the 'Shared' box for the scheme that needs to be run. Shared Scheme
  3. Click 'Edit...' in the bottom left and go to your build action. Edit Scheme
  4. On the row of your Tests target check the box in the 'Run' column. Run Test
  • At this point for a simple project or a project using CocoaPods you should be good to go. If Travis finds a Podfile in the root of your repository it automatically runs pod install to get your dependencies (from their docs). Otherwise there are a ton of configuration options for your .travis.yml depending on how your repo is setup.

For one of my projects I created a setup.sh file at the root of my repo that looks like this:

#!/usr/bin/env bash

git submodule update --init --recursive
echo "Setting up test frameworks..."
cd Example/Vendor/Specta; rake > /dev/null
cd ../Expecta; rake > /dev/null
echo "Done"
cd ../../../

This script which I run using the before_install: ./setup.sh option in my .travis.yml gets all my submodules, sets up Specta and Expecta and then goes back to the root directory for running. If you just have a few simple steps you can also have multiple before_install actions like:

before_install:
  - cd Example
  - make

You can read more about other Travis configuration options in their documentation.

OS X + ZFS

Permalink

For a long time people have talked about how horrible HFS+ is. Most notably, in my opinion, Linus Torvalds (the creator of Linux and Git) who said "Their file system is complete and utter crap, which is scary.". John Siracusa also wrote about the problems with HFS+ in his 10.7 review along with talking about it on his podcast, Hypercritical in episodes 56 and 57.

The gist of all this really is that Apple needs a new filesystem. It's been a while since Apple had ZFS support on their website for release in Snow Leopard. After which Apple dropped ZFS support because of licensing issues. There had been talks of Apple developing their own file system, although I think that's the wrong way to go. I think Apple needs to approach file systems the way they approached Safari with Webkit. I think they would be best served by picking up a liberally open source file system, such as BTRFS, or grabbing up one of the ZFS ports and continuing development while leaving it open for anyone to use. One of the great things about this approach is exactly what happened with Webkit and Google Chrome. Google decided to use the Webkit engine to make a product that competes with the main developers of the Webkit project. By doing this with a file system other vendors could use the same implementation and therefore increase development and in turn stability of whichever system was chosen (not to mention compatibility).

One thing is for sure. People who understand how integral a file system is want something modern.

Terminal Shortcut in OS X

Permalink

One of my favorite defaults in some Linux distros is the ability to use T to open a new terminal window. I wanted to enable this same functionality in OS X using Quicksilver. I did this using iTerm 2 but you can do it with the default Terminal if that's what you want.

  1. Enable the Terminal and iTerm2 Quicksilver plugins. Quicksilver plugins

  2. Create a new custom hotkey trigger. Using the Home directory with the action Open Directory in Terminal Quicksilver trigger

  3. Set it's hotkey using the drawer to whatever you want. Quicksilver hotkey

  4. Set your default Command Line Interface Trigger to iTerm (if that's what you want) Quicksilver CLI

You're done! Now you can easily press your hotkey and pull up a new iTerm/Terminal window whenever and wherever.

Automated Google Reader Backups

Permalink

I spend a lot of time in my RSS Reeder (see what I did there?). I still find Google Reader to be the best and easiest way to manage my subscriptions, although I've been wanting to switch to Fever for a while.

One thing I wanted to do when I launched my new site (the one you're reading) was to have a downloadable up to date export of my Google Reader OPML file (which of course I never did). I looked around for good ways to automate this and I found a simple Python script to do it with (sorry I couldn't find it again for this post). I decided to rewrite it in Ruby and set it up on my server as an automated cron job.

To run the script I came up with use something like:

ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword

To add it to your crontab (to run every Sunday at 1:01am) use something like:

1 1 * * 7 ruby path/to/googleReaderOPML.rb username@gmail.com SekretPassword
googleReaderOPML.rbGist page
#!/usr/bin/env ruby
#
# => This script will authorize your Google credentials and download your Google Reader subscriptions
# => Usage: ./googleReaderOPML.rb GOOGLEUSERNAME PASSWORD
#
# The required networking shenanigans
require 'uri'
require 'net/http'
require 'open-uri'
require 'rubygems'
# This requires the 'colorize' gem. Install with '[sudo] gem install colorize'
require 'colorize'
# The base Google URLs for callback, authentication, and subscription export
$GOOGLE_URL = "http://www.google.com"
$LOGIN_URL = "https://www.google.com/accounts/ClientLogin"
$READER_URL = "http://www.google.com/reader/subscriptions/export"
# The user agent string, for some reason this is required, feel free to change it
$SOURCE = "keith.so"
# The default output filename, it is automatically overwritten if one already exists
$FILE_NAME = "googlereadersubscriptions.opml"
# Make sure there is the correct number of arguments
if ARGV.count != 2
# Print the instruction
puts "Usage: ./#{ File.basename(__FILE__) } USERNAME PASSWORD".red
exit
end
# Build the request URL
uri = URI.parse($LOGIN_URL)
# Setup the Parameters
params = { Email: ARGV.first, Passwd: ARGV.last, service: "reader", source: $SOURCE, continue: $GOOGLE_URL }
# Add the user-agent string, my website (feel free to replace it) to the headers
headers = { "User-agent" => $SOURCE }
# Encode the parameters into the url
uri.query = URI.encode_www_form(params)
# Create a new NET:HTTP object with the request URL
http = Net::HTTP.new(uri.host, uri.port)
# Require HTTPS without this net/http will not be happy with you
http.use_ssl = true
# Execute the request
request = Net::HTTP::Get.new(uri.request_uri, headers)
# Get the data from the request
response = http.request(request)
# Check for valid response code, should ONLY be 200
if response.code != '200'
puts "Google returned #{ response.code }, check your username and password".red
exit
end
# split each token into a different item then load them each into a hash with the key as the token key
auth_hash = Hash.new
response.body.split(/\n/).each do |token|
split_array = token.split('=')
auth_hash[split_array.first.downcase] = split_array.last
end
# Create a header hash for the request of the XML file
headers = { "user-agent" => $SOURCE, "cookie" => "Name=SID;SID=#{ auth_hash['sid'] };Domain=.google.com;Path=/;Expires=160000000000", "authorization" => "GoogleLogin auth=#{ auth_hash['auth'] }" }
# Open the URL for the Google Reader export with the setup headers
request = open($READER_URL, headers)
# Open the received XML feeds file
google_reader_file = File.open(request, 'r')
# Read the entire feeds file into 'subscriptions'
subscriptions = google_reader_file.read
# Close the downloaded file
google_reader_file.close()
# Open a new file with the global filename to write to, overwrite it if it exists
subscriptions_file = File.open($FILE_NAME, 'w')
# Verify the file was created
if File.exists?(subscriptions_file)
# Write the subscriptions to the file and close it
subscriptions_file.write(subscriptions)
subscriptions_file.close()
# Display a success message
puts "Wrote Google Reader subscriptions to #{ $FILE_NAME }".green
else
# If the file wasn't created print an error
puts "Couldn't write to #{ $FILE_NAME } (the process running this script may not have sufficient privileges".red
end

Boredom

Permalink

Working as a developer full-time can be very exciting. Dealing with new interesting problems gets me up in the morning. But I still find that after a few months of a specific project I find myself less and less interested with it. Not only projects but concepts and languages start to become less interesting. First it was websites then Objective-C then Ruby then C and now who knows. This scares me. At this point in my life I hope to work in this field for my 'career' meaning a significant amount of time. Yet I can't even keep myself working on a single project now, much less one that doesn't interest me.

I typically blame this on the difficulty of the project. As a lone developer I find that most projects I work on are pretty small in scope, since I just don't have the time or people-power to work on anything larger. Therefore I look at most of my projects without interest. I'm not sure what it will take to keep myself engaged and interested on my next project.

Learning C

Permalink

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

Permalink

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.

zbadVimKeys.mmGist page
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.

vimKeys.mmGist page
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.

goodVimKeysSandbox.mmGist page
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

Permalink

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).

CapfileGist page
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 cap -f ~/path/to/Capfile backup

Raking Podspecs

Permalink

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.

rakefile.rbGist page
#!/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-p327'
$RBENV = '.ruby-version'
# The gem to use
$GEM = 'cocoapods'
task :default => :lint
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
# 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