MacBook Hacking (Part 14: Git, Ruby, and Haskell)

Our installation tour is quickly coming to the end. Today with clean up by talking about three final command-line tools.

Git

Today’s preferred version control system. Made it easy on myself: just installed the binary. Use Dropbox to keep .gitconfig synchronized:

> ln -s Dropbox/Preferences/tilde-slash/dot-gitconfig .gitconfig

My .gitconfig makes output simple and colorful. I use TextMate as my comment editor:

[user]
    name = William Taysom
    email = wtaysom@gmail.com
[color]
    diff = auto
    status = auto
    branch = auto
[core]
    pager = cat
    editor = mate -wl1

Ruby

My Ruby setup is not beautiful. I’ve been running 1.8.7 and 1.9.1. Ruby Version Manager is probably the best solution, but I’ll share my solution with you.

Standard Gems

Ruby without other gems will never meet your jeweling needs:

> sudo gem update --system
> sudo gem update # Comes with Rails and a bunch of others.
> sudo gem install assert2 assit bundler capistrano-ext columnize fxruby gchartrb json json_pure linecache passenger rb-appscript redis rspec rspec-rails ruby-debug ruby-debug-base rubydbc echoe rubydbc yajl-ruby unroller

Some of these merit additional comment:

  • gchartrb provides a Ruby wrapper for Google’s chart API. Everybody needs a chart now and then.
  • rspec is my preferred testing library. I like its lexical scoping goodness. Ruby testing the Lispy way.
  • ruby-debug because hacking without a debugger is hacking in the dark.
  • echoe is my preferred gem builder.
  • unroller — why debug step-by-step when what you really want is a full execution trace?

.irbrc

I share my IRB configuration via DropBox:

> ln -s ~/Dropbox/Preferences/tilde-slash/dot-irbrc .irbrc

What sort of configuration do I like? A simple prompt just ?:

IRB.conf[:PROMPT][:WLS_PROMPT] = {
  :PROMPT_I => "? ",
  :PROMPT_S => '" ',
  :PROMPT_C => "| ",
  :RETURN   => "%s\n"
}
IRB.conf[:PROMPT_MODE] = :WLS_PROMPT

IRB.conf[:AUTO_INDENT]  = true
IRB.conf[:USE_READLINE] = true
IRB.conf[:LOAD_MODULES] = [] unless IRB.conf.key?(:LOAD_MODULES)

Get tab to do its most:

## Tab for command completion.
unless IRB.conf[:LOAD_MODULES].include?('irb/completion')
  IRB.conf[:LOAD_MODULES] << 'irb/completion'
end

Everyone needs gems:

## I just expect this.
require 'rubygems'

I define r as a method to reload a file of interest:

## For quick loading.

$wl_main_file_name = "main.rb"
def r name = nil
  if name
    name += ".rb" unless name =~ /.rb$/
  elsif $wl_test_file
    name = $wl_test_file
  elsif Dir.new(Dir.pwd).detect {|n| n == $wl_main_file_name}
    name = $wl_main_file_name
  else
    name = Dir.new(Dir.pwd).detect {|n| n =~ /.rb$/}
  end
  $wl_test_file = name
  load $wl_test_file
end

Back when I started Ruby, Symbol#to_proc wasn’t standard:

## Symbol#to_proc
unless :symbol.respond_to? :to_proc
  class Symbol 
    def to_proc
      Proc.new {|*args| args.shift.__send__ self, *args}
    end
  end
end

Sometimes I want a quick summary of all the methods an object will respond to:

## ObjectSummary
# For quick inspection of an object.

module ObjectSummary
  # Prints the public methods of an object grouped by module.
  def _summary
    header = lambda do |obj|
      puts "=== #{obj} ==="
    end

    body = lambda do |array|
      puts "  "+array.sort.join(", ") unless array.empty?
    end

    sm = singleton_methods
    unless sm.empty?
      header["<< self"]
      body[sm]
    end

    self.class.ancestors.each do |ancestor|
      header[ancestor]
      body[ancestor.instance_methods(false)]
    end

    puts "==="
  end

  def _s
    _summary
  end
end

include ObjectSummary

Sometimes I know the arguments, I know the return value, but I don’t know the method name:

## MethodFinder
# <http://www.nobugs.org/developer/ruby/method_finder.html>

class MethodFinder
  # Find all methods on [anObject] which, when called with [args] return [expectedResult]
  def self.find obj, res, *args
    obj.methods.select do |name|
      obj.method(name).arity == args.size
    end.select do |name|
      begin
        mega_clone(obj).method(name).call(*args) == res
      rescue
      end
    end
  end

  def self.mega_clone obj
    begin obj.clone rescue obj end
  end

  # Pretty-prints the results of the previous method
  def self.show obj, res, *args
    find(obj, res, *args).each do |name|
      print "#{obj.inspect}.#{name}" 
      print "(#{args.map{|o| o.inspect}.join(", ")})" unless args.empty?
      puts " == #{res.inspect}"
    end
  end
end

# _why's addition
# <http://redhanded.hobix.com/inspect/stickItInYourIrbrcMethodfinder.html>
class MethodFinder
  def initialize obj, *args
    @obj = obj
    @args = args
  end
  def ==res
    MethodFinder.find @obj, res, *@args
  end
end

class Object
  def what? *args
    MethodFinder.new self, *args
  end
end

Though RSpec is now my testing framework of choice, once upon a time I found it useful to Test::Unit tests from within IRB:

## For testing.

def t
  require 'test/unit'
  require 'test/unit/ui/console/testrunner'

  def Kernel.beep
    putc ?\a
    nil
  end

  def Test.run *names
    u = Test::Unit

    # collect tests in Test
    tests = []
    constants.each do |const_name|
      const = const_get const_name
      if const.kind_of? Class
        if const.subclass? u::TestSuite
          tests << const
        elsif const.subclass? u::TestCase
          tests << const.suite
        end
      end
    end
    tests.reject! {|t| not names.include? t.name } unless names.empty?

    # build suite of all tests
    suite = u::TestSuite.new "all tests"
    tests.each {|t| suite << t}
    result = u::UI::Console::TestRunner.run suite
    beep unless result.passed?

    tests.map {|t| t.name}
  end

  def t *names
    Test.run *names
  end
end

It’s amazing how the little utilities you make for yourself can become lost and forgotten.

Ruby 1.9

I use sudo port install ruby19 got get ruby1.9, rake1.9, etc. Might not be the best way to manage multiple Ruby versions, but it works well enough for me.

Ruby 1.9 has its own set of gems:

> sudo gem1.9 update --system
> sudo gem1.9 install thin uuid rack-contrib yajl-ruby redis usher em-http-request activerecord sqlite3-ruby mysql rspec
> sudo mv /opt/local/bin/spec /opt/local/bin/spec1.9 # To avoid name conflict.

Make your own Ruby Docs

I like the speed and convenience of local documentation. Though the following document generation commands work, they don’t seem to work well. They take a very long time and seem a bit finicky:

> svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8_7
ruby_1_8_7> rdoc --op ../ruby_1_8_7_doc
> svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_9_1
ruby_1_9_1> rdoc1.9 --op ../ruby_1_9_1_doc # Uses about 4GB of RAM.  I sense a memory leak.

For language reference, I keep a local copy of Ruby QuickRef.

Haskell

Though I’ve never used Haskell for a serious project, I do like reading programming language theory papers. Haskell is the lingua franca for pure functional programming. Haskell helps you distill the essential from the irrelevant. Paul Hudak once observed:

We provided them [DARPA] with a copy of P1 [implemented in Haskell] without explaining that it was a program, and based on preconceptions from their past experience, they had studied P1 under the assumption that it was a mixture of requirements specification and top level design. They were convinced it was incomplete because it did not address issues such as data structure design and execution order.

Next Time?

An addendum with all the cool things I’ve found since Apollo’s initial setup.