Err the Blog Atom Feed Icon
Err the Blog
Rubyisms and Railities
  • “Sake Bomb!”
    – Chris on June 25, 2007

    Before moving to California, I’d never had sake. Ever. Any. What a mistake that was. My west coast friends made fun of me then promptly taught me the trick where you drop a shot of sake into a glass of beer then drink really fast. It’s called a sake bomb. Pretty intense.

    Sake bomb!

    Also intense are Rake tasks, something you and I have talked about before. They are so handy! The only problem is I find myself installing the same base set of Rake tasks on every new Rails project I start. Over and over. Stuff like db:version, routes, yaml_to_spec, etc.

    I wish Rake tasks were more like Rubygems: system-wide instead of project specific, easy to install or share, and fun to use.

    Well, now they are.

    S-s-s-sake!

    Sake is system-wide Rake. It works pretty much like how you’d expect it to work: use it to install then run Rake tasks. Let me show you.

    $ sudo gem install sake

    Imagine I have an err.rake file with two tasks in it. Imagine it looks like this:

    namespace :db do
      desc "Returns the current schema version"
      task :version => :environment do
        puts "Current version: " +
          ActiveRecord::Migrator.current_version.to_s
      end
    end
    
    desc "Show specs when testing"
    task :spec do
      ENV['TESTOPTS'] = '--runner=s'
      Rake::Task[:test].invoke
    end
    

    Okay. First, let’s check and see what’s in the file:

    $ sake -T err.rake
    sake db:version   # Returns the current schema version
    sake spec         # Show specs when testing
    

    Cool. I wonder what spec looks like?

    $ sake -e err.rake spec
    desc 'Show specs when testing'
    task 'spec' do
      ENV["TESTOPTS"] = "--runner=s"
      Rake::Task[:test].invoke
    end
    

    Ah, neat-o. Let’s go ahead and install it.

    $ sake -i err.rake spec
    # Installing task `spec'
    

    Now let’s see what Rake tasks we have installed system wide.

    $ sake -T
    sake spec   # Show specs when testing
    

    Amazing. $ sake spec from anywhere. And, of course, we can remove it at any time:

    $ sake -u spec
    # Uninstalling `spec'.  Here it is, for reference:
    desc 'Show specs when testing'
    task 'spec' do
      ENV["TESTOPTS"] = "--runner=s"
      Rake::Task[:test].invoke
    end
    

    Railing Some Sake

    Did you notice the :environment task dependency in our db:version task? Yikes, that’s Rails-specific. Let’s install that task and see what happens.

    $ sake -i err.rake db:version
    # Installing task `db:version'
    $ sake db:version
    rake aborted!
    Don't know how to build task 'environment'
    
    (See full trace by running task with --trace)
    

    Failed. Hard. What if we try this from within a Rails app?

    $ sake db:version
    Current version: 106
    

    Disco. Sake picks up tasks in directory-local Rakefiles, in this case Rails’ Rakefile.

    Tasks Are Islands

    Sake only knows about Rake tasks, not any other Ruby code. If you’ve got a require in your Rakefile, Sake has no idea. It just cares about the tasks. So, sometimes we need to tweak a task or two.

    Let’s say you have this:

    require 'rubygems'
    require 'hpricot'
    require 'open-uri'
    
    desc "Today's sports scores"
    task :scores do
      doc = Hpricot open('http://sports.com')
      doc.search('.score').each do |score|
        puts score
      end
    end
    

    Sake will only pull in the task, not the require statements. No big deal. Just tweak it:

    task :hpricot do
      require 'rubygems'
      require 'hpricot'
      require 'open-uri'
    end
    
    desc "Today's sports scores"
    task :scores => :hpricot do
      doc = Hpricot open('http://sports.com')
      doc.search('.score').each do |score|
        puts score
      end
    end
    

    Rinse and repeat for any code which depends on plain jane Ruby executed outside of a task.

    A Place For All Your Ruby Slippers

    I used to have a bunch of Ruby scripts laying around, for all kinds of simple tasks.

    Here’s one I had to apply a patch from a pastie url:

    #!/usr/bin/env ruby
    # Usage:
    #   $ pastie_patch 69664 
    #   $ pastie_patch http://pastie.caboo.se/69895
    
    require 'open-uri'
    
    pastie_url = 'http://pastie.caboo.se/%s.txt'
    patch_id   = ARGV.first.gsub(/\D/, '')
    
    patch = open(pastie_url % patch_id).read
    
    File.open('patch.diff', 'w+') do |f|
      f.puts patch
    end
    
    `patch -p0 < patch.diff && rm patch.diff`
    
    puts "Patched with pastie ##{patch_id}."
    

    Now, thanks to Sake, I can just make these scripts into Rake files and have a fun interface to manage them with:

    # Usage: sake pastie:patch PASTE=12345
    desc "Apply a patch directly from Pastie"
    task 'pastie:patch' do
      require 'open-uri'
    
      pastie_url = 'http://pastie.caboo.se/%s.txt'
      patch_id   = ENV['PASTE'].gsub(/\D/, '')
    
      patch = open(pastie_url % patch_id).read
    
      File.open('patch.diff', 'w+') do |f|
        f.puts patch
      end
    
      `patch -p0 < patch.diff && rm patch.diff`
    
      puts "Patched with pastie ##{patch_id}."
    end
    

    Your productivity will thank you. There’s really no reason not to use Ruby all over the place now.

    Sharing Your Sake

    Remember how we peeked around a Rakefile? Well, you can do that with URLs, too. It’s just open-uri, y’know?

    $ sake -T http://pastie.caboo.se/73211.txt
    sake development   # Runs the following task in the development...
    sake production    # Runs the following task in the production...
    sake testing       # Runs the following task in the test environment
    sake dev
    sake prod
    

    Make sure you inspect the code and know what you’re installing. Just like you would with any random RubyGem or snippet, right?

    $ sake -e http://pastie.caboo.se/73211.txt production
    desc 'Runs the following task in the production environment'
    task 'production' do
      RAILS_ENV = ENV["RAILS_ENV"] = "production"
    end
    

    Rad. If you want to share your Rake tasks with other Sake users, just throw them up somewhere. Like Pastie.

    Sharing Your Sake (Hardcore)

    Or, also, use -S. Sake comes built in with a Mongrel handler which will serve all your installed Rake tasks. Maybe you’re at a hip European conference or a hip European cafe with no Internet. And maybe your hip European friend needs a specific Rake task right this second. Waste no time.

    $ sake -S
    # Serving warm sake tasks on port 4567...
    

    As the -h explains, -d will daemonize and -p lets you set the port. (The code for the handler is here if you wanna take a peak. It’s fun.)

    The Morning After

    When both sake and bombs are involved, nay, combined, the night seldom goes as planned. That’s okay. Please report bugs over at our Lighthouse project and, if you can, send patches as pasties.

    Sake probably only runs on Windows using Cygwin, due to its ruby2ruby dependency. So, yeah. Let us know if you can prove otherwise.

    I Owe You a Sake Bomb

    Special thanks to Ryan Davis & Eric Hodel for ruby2ruby (which is brilliant) and also to Josh Susser, Brian Donovan, and Zack Chandler for their work on the predecessor to Sake.

    One Last Thing!

    A parting gift. Here are some Rake tasks you may want to $ sake -i.

    • database.rake
    • svn.rake
    • pastie:patch
    • rake routes

     

    Hey, while we’re on the topic, why notshare with us your favorite rake tasks? Put them up somewhere (as plaintext) so we can -T and -e and -i them, then let us know where that somewhere is in the comments. Killer.

    Update: Okay, db:version and all the database.rake magics are in edge now. That’s pleasant.

    Update 2: There are some good command completion tips for Sake in the comments. Also, Dr Nic has added more pastie support to Sake. Wicked!

  • Anil Wadghule, about 2 hours later:

    Nice one! But IMO there should be equivalent of “gem list—remote”. So we need central repository for all such nice Rake tasks. :)

  • François Beausoleil, about 3 hours later:

    Unfortunately, I hit a little roadblock. This is right after installation—Did not run anything else.

    $ sake -i http://svn.integrumtech.com/public/plugins/rake_tasks/tasks/database.rake
    # Installing task `db:create'
    # Installing task `db:drop'
    # Installing task `db:reset'
    # Installing task `db:shell'
    /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:416:in `<<': can't convert nil into String (TypeError)
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:416:in `to_ruby'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:268:in `to_ruby'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:268:in `map'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:268:in `to_ruby'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:358:in `to_ruby'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:469:in `save!'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:468:in `open'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:468:in `save!'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:185:in `install'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/lib/sake.rb:114:in `run'
            from /usr/local/lib/ruby/gems/1.8/gems/sake-1.0.3/bin/sake:5
            from /usr/local/bin/sake:16:in `load'
            from /usr/local/bin/sake:16
    

    Great idea ! Thanks ! François

  • mikemondragon, about 5 hours later:

    Awesome idea Chris, your the bomb.

    I know you said that you only know this to work on windows+cygwin but I just wanted to give my bug feedback that I get the exact same error as François on Debian Testing (Lenny).

    Growing up in Eastern Washington we had our own regional sake bomb. Montana was the closest state that sold 190 proof Everclear Whenever a bottle would show up we’d do flaming torpedos … light a shot of Everclear on fire, drop it in a mug of cheap beer and chug it down your gultch.

  • Chris Carter, about 5 hours later:

    This looks pretty cool! You most definatly need to setup a central repository though. Weren’t you working on one for a bit?

    BTW- I am now going to start using disco as an exclamative in every day conversation.

  • Zack, about 6 hours later:

    Very cool man!

  • evan, about 6 hours later:

    Fuckin’ jank! In a good way.

  • DHH, about 6 hours later:

    Great stuff! The database.rake tasks linked to are already in edge, but this is definitely nice for those not on edge. Also, I grabbed the db:version and included it in edge.

  • Mislav, about 7 hours later:

    Awesome!! I don’t think which is cooler today – the new gem/generator/Rails stuff, or this baby!

    But - regarding the sake bomb - isn’t the beer going to spill all over the place once you remove the sticks?

  • Mislav, about 7 hours later:

    Awww … textile :)

  • Chris, about 8 hours later:

    Yeah, the beer gets everywhere.

    François and Mike: That bug is gone. Whoops.

  • josh, about 9 hours later:

    Are you going to call your next hack “boilermaker”? :-)

    Nice solution to the repository issue. ruby2ruby is awesome.

  • Rob, about 10 hours later:

    If you want to have tasks that span multiple rails projects you can fake out the environment task like so [pastie]

  • Seth Thomas Rasmussen, about 11 hours later:

    You’re the man now d-aw forget it..

  • Dr Nic, about 12 hours later:

    Here’s a modded pastie:patch to accept any url (via PASTE or PATCH)

    # Usage: sake pastie:patch PASTE=12345 # Usage: sake pastie:patch PATCH=http://dev.rubyonrails.org/attachment/ticket/8730/gem_generators.patch?format=txt desc “Apply a patch directly from Pastie (or absolute url)” task ‘pastie:patch’ do require ‘open-uri’ pastie_url = ‘http://pastie.caboo.se/%s.txt’ patch_id = ENV[‘PASTE’].to_i url = patch_id > 0 ? (pastie_url % patch_id) : (ENV[‘PASTE’] || ENV[‘PATCH’]) patch = open(url).read File.open(‘patch.diff’, ‘w+’) do |f| f.puts patch end `patch -p0 < patch.diff && rm patch.diff` puts “Patched with #{url}.” end
  • Dr Nic, about 12 hours later:

    Here’s a modded pastie:patch to accept any url (via PASTE or PATCH)

    # Usage: sake pastie:patch PASTE=12345
    # Usage: sake pastie:patch PATCH=http://dev.rubyonrails.org/attachment/ticket/8730/gem_generators.patch?format=txt
    desc "Apply a patch directly from Pastie (or absolute url)"
    task 'pastie:patch' do
      require 'open-uri'
    
      pastie_url = 'http://pastie.caboo.se/%s.txt'
      patch_id   = ENV['PASTE'].to_i
    
      url = patch_id > 0 ? (pastie_url % patch_id) : (ENV['PASTE'] || ENV['PATCH'])
      patch = open(url).read
    
      File.open('patch.diff', 'w+') do |f|
        f.puts patch
      end
    
      `patch -p0 < patch.diff && rm patch.diff`
    
      puts "Patched with #{url}."
    end
    
  • Dr Nic, about 12 hours later:

    Or get it via:

    sake -u pastie:patch
    sake -i http://pastie.caboo.se/73373.txt
    
  • Dr Nic, about 12 hours later:

    And here’s the sake_autocomplete script (modded from the rake_autocomplete script):

    http://pastie.caboo.se/73375

    Sake Sake Sake Oi Oi Oi!

  • evan, about 12 hours later:

    So if it recognizes local tasks, can I just symlink rake to sake and then be done with it?

  • Dr Nic, about 13 hours later:

    @evan – “sake -T” doesn’t show all the available tasks.. yet. But I guess once it does, “rake” could be disused.

  • topfunky, about 13 hours later:

    This is Dy-no-mite!

    A combination of why’s balloon and plain shell scripts.

    I predict a Ruby-based shell by the end of the year.

  • Chris, about 13 hours later:

    The reason ‘sake -T’ doesn’t show local tasks was a philosophical decision rather than a technical one. I wanted to have a clear separation between Sake tasks and local Rake tasks.

    We could always add another switch which’ll show both. -T or -a or something or -drnic or -evan or something.

  • Matt, about 15 hours later:

    just a quick note to let you know that the tasks from database.rake are now built in Edge. ( rake db:create db:drop db:reset )

  • labrat , about 16 hours later:

    You guys leaving CNet was the best thing to happen for the rest of us. lol

    Have a cup of sake on me: http://pastie.caboo.se/73245

    This will allow you to do “rake db:rollback:1 or even rake db:rollback:-1 (to go forward after rollback)” without being aware of which database version you’re on.

  • Dr Nic, about 20 hours later:

    As cool as the server/daemon option is, sending 1+ tasks off to pastie for sharing would be a cool option. It seems most ppl want to share things via pastie.

  • Hendy Irawan, about 22 hours later:

    ceefour@ojalanow:/media/prestige/home/ceefour/project/primacom/parlys/trunk$ sake add_new_files A app/models/guest.rb A app/views/hobolib A app/views/hobolib/themes A app/views/hobolib/application.dryml A public/hobothemes

    Dang… Sake is god.

  • Dr Nic, 1 day later:

    What was the “predecessor to sake”?

  • Dr Nic, 1 day later:

    Latest version (1.0.6) allows you to send a task to a pastie.

    sake -P db:version

    On osx this means you can do:

    open `sake -P db:version`

    and the pastie will open in your browser. Neat.

  • kimtrott, 1 day later:

    next ruby shell? name it rush! :-)

  • Dr Nic, 1 day later:

    Want to send any file or STDIN to pastie?

    sake -i http://pastie.caboo.se/73801

    USAGE:

    cat some_tasks.rake | sake pastie:send
    

    Or

    sake pastie:send FILE=some_tasks.rake
    

    It returns the pastie url, so on OSX you can do:

    open `cat some_tasks.rake | sake pastie:send`

    and the pastie will open in the browser.

  • Dr Nic, 1 day later:

    Oh, I almost forgot why I wrote that! Try this for sharing patches…

    open `svn diff | sake pastie:send`

    Instant pastie patches! (wrapped in the OSX open command to get the pastie in the browser)

  • Dr Nic, 1 day later:

    Or nicer still:

    svn diff | sake pastie:send | pbcopy

    Thx zenspider

  • Nick, 1 day later:

    Oh it’s neato. Sake, it’s a great name (+1), but -1 for a command to replace rake. Any possibility of making it so that you can use sake with rake? Some little auto-detect thunk inside of rake? Or did I miss the point entirely…

  • Saimon Moore, 1 day later:

    Hey Chris,

    Why don’t you convert this into a patch for rake? Shouldn’t be too difficult

    (I’d rather just have the one tool to remember)

    Regards,

    Saimon

  • Haakon Sorensen, 3 days later:

    Beautiful stuff! Add to the sake smoothness with shell tab completions for your sake tasks. On linux using Zshell, add the following to your dot files:

    function sake_task_list { reply=(`sake -T | awk ‘{ print $2 }’`); } compctl -K sake_task_list sake

    or in the spirit of things:

    http://pastie.caboo.se/74404

  • Kevin Marsh, 3 days later:

    Or maybe you use bash…

    complete -W ”$(sake -T | awk {‘print $2’})” sake

  • nilsense, 5 days later:

    a simple ncurses interface/gui using rdialog now helps me remember and typing down tasks..

    1. select task using ncurses/rdialog
    2. $ sake -t

    here’s a short patch : http://pastie.caboo.se/75088

    for further versions of sake (eg folderize or tagging the tasks) would love to see some sort of ncurses implementation.. sake your day!

    a ruby shell ‘rush’ ? i’m in ;D

  • DerGuteMoritz, 8 days later:

    Neat!! Here’s a namespaced version of the subversion tasks (add_new_files replaced in favor of just svn:add): http://pastie.caboo.se/75638.txt

  • Anil Wadghule, 9 days later:

    I get following message on Windows, when I run sake “Define INLINEDIR or HOME in your environment and try again” Can anybody help me out?

  • Dr Nic, 9 days later:

    Load up any RubyGem into an editor from cache.

    sake gems:find activerecord | xargs mate

    To install:

    sake -i http://pastie.caboo.se/76167.txt
  • Shane Vitarana, 23 days later:

    I like this version of Subversion tasks better. It does only one commit, removes log and tmp, leaves db and database.yml alone, and prints the status.

    sake -i http://pastie.caboo.se/79955.txt
    
  • Shane Vitarana, 23 days later:
    sake -i http://pastie.caboo.se/80145.txt
    sake app:copy_formats
    
    validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
    
    becomes
    validates_format_of :email, :with => Format.email
    

    Sake can effectively replace plugins that just copy code.

  • Chris, 24 days later:

    Shane, that’s brilliant!

  • gnufied, 24 days later:

    hit on some wierd bug,

    sake yaml_to_spec FILE=foobar.yml

    shows an empty line. although foobar.yml is alright, and works when invoked from rake.

  • namelessjon, 26 days later:

    sake -i http://pastie.caboo.se/80948.txt

    I quite often use mercurial on my projects, and especially for gem development with newgem. These are a couple of tasks I wrote.

    newgem:hg initializes a mercurial repository for the gem, with ignores for doc,pkg,generated html.

    newgem:tag creates a mercurial tag for the current rev like gem_name-0.1.0. It’s up to you to make sure this is right.

  • namelessjon, 26 days later:

    or extra special bonus version with working hgignore file.

    sake -i http://pastie.caboo.se/80957.txt

    (note to self, double check version of file before posting in public)

  • Matt Aimonetti, 28 days later:

    import ar-backup plugin in sake. (ar-backup is a backup tool for Active Record.)

    sake -i http://ar-backup.googlecode.com/svn/ar-backup/tasks/ar_backup_tasks.rake

    works very well with Sake, check it out:

    http://code.google.com/p/ar-backup/

  • Djur, about 1 month later:

    Undignified use of grep has yielded a much faster variant on the ZSH completion method given above:

    http://pastie.caboo.se/82299.txt

    The version using sake -T is probably more elegant and less likely to break, but I hate that little pause after hitting tab more than life itself. I don’t know how much of the difference is due to rubygem slowdown, but I was able to pare about 4 seconds off the 100x benchmark for sake -T by cleaning up my installed gems a bit. Still not enough. Hope this helps.

  • Pedro Costa, about 1 month later:

    Hi sounds very!

    i actually used a rake alias with a global tasks rake file. But this sounds much better!

    But i’m having a little problem running on cygwin. I’m having the follwing error:

    /cygdrive/c/Documents and Settings/pfaria is insecure (40770). It may not be group or world writable. Exiting.

    Any ideas??

    Thanks.

  • Shadowfiend, about 1 month later:

    Re: a Ruby Shell: I’ve just released version 0.1 of RubyShell. FYI.

  • meekish, about 1 month later:

    Could sake be used to make your capistrano mate_logs and console tasks global?

  • Dean, about 1 month later:

    So I’m not sure what happened or what changed, but all of the sudden, my sake bottle’s run dry.

    [~] sake -i http://svn.integrumtech.com/public/plugins/rake_tasks/tasks/database.rake
    # Installing task `db:create'
    # Installing task `db:drop'
    # Installing task `db:reset'
    # Installing task `db:shell'
    [~] sake -T
    [~]
    

    Nothing is written to my ~/.sake file either. I’ve tried updating to 1.0.8 and everything :( Any suggestions?

  • Brett, about 1 month later:

    I updated the pistoned task to report on all piston managed plugins as well as rails if it has been imported. It also formats the output nicely. sake -i http://pastie.caboo.se/84561.txt

  • Benjamin Jackson, 4 months later:

    Just installed sake and am able to install with -i but the tasks coming in from pastie are coming in empty:

    `sake -i http://pastie.caboo.se/73228.txt`

    And my ~/.sake file:

    desc ‘Configure Subversion for Rails’ task ‘configure_for_svn’ do

    end

    etc…

  • elliottcable, 5 months later:

    Sake’s got a pretty ouch-y bug. See here: http://rake.rubyforge.org/files/doc/rakefile_rdoc.html#Multiple_Definitions

    However, Sake won’t accept multiple definitions of the same one. Maybe have it as a prompt instead? `Sake already has a declaration for this task – do you want to Use (o)riginal, Overwrite with (n)ew, or keep original and (m)erge new prerequisites?`

    A lot of my rake tools lying around are rather complicated in structure, and like to merge into eachother in various ways to enhance functionality. This breaks with sake >.<

  • Jeremy (Eltiare), 6 months later:

    I’ve got it running on OS X with ruby2ruby. Most of the commands seems to work.

  • Adam Salter, 6 months later:

    One question… seems a bit silly. Why isn’t ruby2ruby a dependency of Sake?

  • Josh Nichols, 6 months later:

    My sake is empty too. I have sake-1.0.11 and rake-0.8.1 installed.

    $ sake --version
    sake, version 1.0.11
    rake, version 0.8.1
    $ cat hello.rake
    task(:hello) do
      puts "Hello world"
    end
    $ sake -e hello.rake
    task 'hello' do
    
    end
    $ sake -i hello.rake
    # Installing task `hello'
    $ sake hello
    $
    
  • Michael Hale, 10 months later:

    I could not get the gems:find sake task to work for me so I changed it to this: http://pastie.caboo.se/199846

  • cies, 11 months later:

    small problem with rubyinline it seems.. i ended up installing sake 1.0.10 that depends on an earlier, fechable version of rubyinline.

    $ sudo gem install sake Bulk updating Gem source index for: http://gems.rubyforge.org/ Bulk updating Gem source index for: http://gems.rubyforge.org/ ERROR: While executing gem … (Gem::RemoteFetcher::FetchError) Gem::RemoteFetcher::FetchError: bad response Not Found 404 reading http://gems.rubyforge.org/gems/RubyInline-3.7.0.gem

  • Fifty-nine people have commented.
    Chime in.
    Sorry, no more comments :(
This is Err, the weblog of PJ Hyett and Chris Wanstrath.
All original content copyright ©2006-2008 the aforementioned.