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.
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.
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!
Nice one! But IMO there should be equivalent of “gem list—remote”. So we need central repository for all such nice Rake tasks. :)
Unfortunately, I hit a little roadblock. This is right after installation—Did not run anything else.
Great idea ! Thanks ! François
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.
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.
Very cool man!
Fuckin’ jank! In a good way.
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.
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?Awww … textile :)
Yeah, the beer gets everywhere.
François and Mike: That bug is gone. Whoops.
Are you going to call your next hack “boilermaker”? :-)
Nice solution to the repository issue. ruby2ruby is awesome.
If you want to have tasks that span multiple rails projects you can fake out the environment task like so [pastie]
You’re the man now d-aw forget it..
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}.” endHere’s a modded pastie:patch to accept any url (via PASTE or PATCH)
Or get it via:
And here’s the sake_autocomplete script (modded from the rake_autocomplete script):
http://pastie.caboo.se/73375
Sake Sake Sake Oi Oi Oi!
So if it recognizes local tasks, can I just symlink rake to sake and then be done with it?
@evan – “sake -T” doesn’t show all the available tasks.. yet. But I guess once it does, “rake” could be disused.
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.
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.
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 )
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.
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.
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.
What was the “predecessor to sake”?
Latest version (1.0.6) allows you to send a task to a pastie.
On osx this means you can do:
and the pastie will open in your browser. Neat.
next ruby shell? name it rush! :-)
Want to send any file or STDIN to pastie?
USAGE:
Or
It returns the pastie url, so on OSX you can do:
and the pastie will open in the browser.
Oh, I almost forgot why I wrote that! Try this for sharing patches…
Instant pastie patches! (wrapped in the OSX open command to get the pastie in the browser)
Or nicer still:
Thx zenspider
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…
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
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
Or maybe you use bash…
complete -W ”$(sake -T | awk {‘print $2’})” sake
a simple ncurses interface/gui using rdialog now helps me remember and typing down tasks..
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
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
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?
Load up any RubyGem into an editor from cache.
To install:
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 can effectively replace plugins that just copy code.
Shane, that’s brilliant!
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.
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.
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)
import ar-backup plugin in sake. (ar-backup is a backup tool for Active Record.)
works very well with Sake, check it out:
http://code.google.com/p/ar-backup/
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.
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.
Re: a Ruby Shell: I’ve just released version 0.1 of RubyShell. FYI.
Could sake be used to make your capistrano mate_logs and console tasks global?
So I’m not sure what happened or what changed, but all of the sudden, my sake bottle’s run dry.
Nothing is written to my ~/.sake file either. I’ve tried updating to 1.0.8 and everything :( Any suggestions?
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
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…
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 >.<
I’ve got it running on OS X with ruby2ruby. Most of the commands seems to work.
One question… seems a bit silly. Why isn’t ruby2ruby a dependency of Sake?
My sake is empty too. I have sake-1.0.11 and rake-0.8.1 installed.
I could not get the gems:find sake task to work for me so I changed it to this: http://pastie.caboo.se/199846
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
Chime in.