Rake Command Completion Using Rake 2

Posted by Lee Marlow Fri, 17 Nov 2006 15:57:00 GMT

This is an update of a previous post.

I just watched Jim Weirich's talk on Rake at the Rails Edge Studio and decided to update the command completion script to use Rake itself for caching the tasks. Err the Blog did an update on my script last month to include caching for a nice speedup, so I took that and made sure the cache is regenerated when the Rakefile or any rake tasks found in your rails app have been updated. I added this with just some straight ruby code, but Jim's talk made me realize that this type of thing is exactly is what rake is for. So, here is the updated version:

  #!/usr/bin/env ruby

  # Complete rake tasks script for bash
  # Save it somewhere and then add
  # complete -C path/to/script -o default rake
  # to your ~/.bashrc
  # Nicholas Seckar <nseckar@gmail.com>
  # Saimon Moore <saimon@webtypes.com>
  # http://www.webtypes.com/2006/03/31/rake-completion-script-that-handles-namespaces
  # http://errtheblog.com/post/33

  exit 0 unless File.file?(File.join(Dir.pwd, 'Rakefile'))

  require 'rubygems'
  require 'rake'
  DOTCACHE = File.join(File.expand_path('~'), ".rake_task_cache" , Dir.pwd.hash.to_s)
  RAKE_FILES = FileList[ __FILE__, 'Rakefile', 'lib/tasks/**/*.rake', 'vendor/plugins/*/tasks/**/*.rake']
  file DOTCACHE => RAKE_FILES do
    tasks = `rake --silent --tasks`.split("\n").map { |line| line.split[1] }
    require 'fileutils'
    dirname = File.dirname(DOTCACHE)
    FileUtils.mkdir_p(dirname) unless File.exists?(dirname)
    File.open(DOTCACHE, 'w') { |f| f.puts tasks }
  end

  Rake::Task[DOTCACHE].invoke
  tasks = File.read(DOTCACHE)

  exit 0 unless /\brake\b/ =~ ENV["COMP_LINE"]

  after_match = $'
  task_match = (after_match.empty? || after_match =~ /\s$/) ? nil : after_match.split.last
  tasks = tasks.select { |t| /^#{Regexp.escape task_match}/ =~ t } if task_match

  # handle namespaces
  if task_match =~ /^([-\w:]+:)/
    upto_last_colon = $1
    after_match = $'
    tasks = tasks.map { |t| (t =~ /^#{Regexp.escape upto_last_colon}([-\w:]+)$/) ? "#{$1}" : t }
  end

  puts tasks
  exit 0

Update: I fixed the bug that janfri pointed out. The bug caused the first task to be missed. I also changed it so it won't abort if rake isn't the first command on the command line. This will allow stringing multiple commands together. For instance:

rake db:migrate VERSION=0 && rake db:migrate

Enjoy

Comments

Leave a response

  1. janfri Wed, 31 Jan 2007 11:37:35 GMT

    Line 19 is not correct, change it to:

    tasks = `rake --silent --tasks`.split("\n").map { |line| line.split[1] }

    The warning is written to standarderr not standardout.

  2. Lee Wed, 11 Apr 2007 16:31:57 GMT

    Thanks, janfri

    I updated the post and added a new feature, too!

Comments