Using thor as rake replacement

Thor is a ruby library for building powerful command-line interfaces. It also can be used for writing build scripts similar to rake scripts. Because thor tasks are implemented as classes, it is possible to reuse them in more object-oriented way.

We want to keep different thor tasks in separate files and even in separate thor folder. To do it we need to put this code into Thorfile within your project:

# Thorfile

# load thor files
Dir.glob("thor/**/*.thor") do |name|
  Thor::Util.load_thorfile(name)
end

If you want to debug thor script, add this code to the beginning of Thorfile:

# Thorfile

unless defined? Thor::Runner
  require 'bundler'

  gems = Bundler::Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil).requested_specs

  gem = gems.find { |gem| gem.name == 'thor'}

  load "#{ENV['GEM_HOME']}/gems/#{gem.name}-#{gem.version}/bin/thor"
end

# load thor files
...

This code will try to load thor command as if it was started from command line. This lets us to run Thorfile as a regular ruby file:

ruby Thorfile

As a result, all debugging abilities are available:

rdebug-ide --host 0.0.0.0 --port 1234 -- Thorfile

Now let’s build something useful for your current project. We want to automate some routine day-to-day tasks, like restarting postgres, initialize database, check status of jenkins CI server etc.:

class Utils < Thor
  desc "pg_restart", "Restarts Postgres server"
  def pg_restart

    exec %Q(
        launchctl unload -w ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
        launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
      )
    end
  end

  desc "create_tables",
       "Creates user and all required DB tables"
  def create_tables
    exec %Q(
      createuser -s -d -r rails_user

      createdb -U rails_user rails_test
      createdb -U rails_user rails_dev
    )
  end

  desc "drop_tables",
       "Removes all required DB tables and user"
  def drop_tables
    exec %Q(
      dropdb rails_test
      dropdb rails_dev

      dropuser rails_user
    )
  end

  desc "show_jenkins",
       "Displays projects on particular jenkins server"
  def show jenkins_url
    require 'json'

    request = get_request(jenkins_url)
    response = http.request(request)

    json = JSON.parse(response.body)

    jobs = json['jobs']

    jobs.each do |job|
      status = case job['color']
        when /blue/i then
          "success"
        when /red/i then
          "failure"
        else
          "failure"
      end

      puts "#{job['name']} -- #{status}."
    end

    puts "Total: #{jobs.size} jobs."
  end

  private

  def get_request url
    uri = URI.parse(URI.escape(url))
    http = Net::HTTP.new(uri.host, uri.port)

    Net::HTTP::Get.new(uri.request_uri)
  end
end

Now you can run these commands:

thor -T

thor utils:pg_restart
thor utils:create_tables
thor utils:drop_tables
thor utils:show_jenkins jenkins_server.your_domain.com