Shake
Simple command runner.
Goals:
-
Shake lets you create extendable CLI runners very easily.
-
Shake intends to replicate Thor/Rake's basic functionality in one very small package.
Why not Rake or Thor?
- Shake starts up way faster than Thor and Rake.
- Rake doesn't support arguments.
- Rake can't be extended easily to support anything more than
rake+Rakefile. - Thor can be too huge for your purposes.
Shake was made with the idea of being easily-embeddable into your projects for your command line runners. It's a single ~4kb Ruby file.
# Shakefile
# Place this file in your project somewhere
class Shake
task(:start) {
puts "Starting '#{params.join(' ')}'..."
}
task.description = "Starts something"
task(:stop) {
puts "Stopping..."
}
task.description = "Stops it"
endIn your shell:
$ shake start server
Starting 'server'...
$ shake stop
Stopping...
$ shake help
Commands:
start Starts something
stop Stops it
help Shows a list of commands
Usage
Using Shakefiles
Using the command shake will load your project's Shakefile.
# ~/project/Shakefile
class Shake
task(:deploy) do
puts "Deploying..."
system "ssh admin@server.com git pull && thin restart"
end
endAnd in your shell:
$ cd ~/project
$ shake deploy
Deploying...
Parameters
Get the parameters with params (an array). Verify parameters with wrong_usage.
task(:init) do
wrong_usage if params.empty?
system "wget #{params.first}"
endExample:
$ shake init
Invalid usage.
See `shake help` for more information.
Parameter options and flags
You may get params from it with params.extract or params.delete. Doing extract
will remove it from params.
task(:create) do
type = params.extract('-t') || 'default'
quiet = params.delete('-q')
file = params.shift
wrong_usage if params.any?
puts "Creating '#{file}' (quiet: #{!!quiet}, type: #{type})"
endExample:
$ shake create #=> Invalid
$ shake create foobar #=> Creating 'foobar' (quiet: false, type: default)
$ shake create foobar -q #=> Creating 'foobar' (quiet: true, type: default)
$ shake create foobar -t xyz #=> Creating 'foobar' (quiet: false, type: xyz)Common commands
Use err to print something to STDERR. Use pass to halt execution.
task(:delete) do
unless File.exists?(params.first)
err 'You can't delete something that doesn't exist!'
pass
end
FileUtils.rm_rf params.first
endYou may also pass parameters to pass to have it printed out before halting.
pass 'The target already exists.' if File.exists?(target)Default tasks
Use default to specify a default task. (The default task is usually help)
class Shake
task(:test) do
Dir['test/**/*_test.rb'].each { |f| load f }
end
default :test
end
# Typing `shake` will be the same as `shake test`Invalid commands
Use invalid to define what happens when you invoke a wrong command.
class Shake
invalid {
err "Invalid command. What's wrong with you?"
}
endIn your shell:
$ shake foobar
Invalid command. What's wrong with you?
Defining helpers
Tasks are executed in the class's context, so just define your helpers like so:
module Helpers
def say_status(what, str)
puts "%15s %s" % [ what, str ]
end
end
class Shake
extend Helpers
endThen use them in your tasks.
class Shake
task(:info) do
say_status :info, "It's a fine day"
end
endManual invocation
You can use shake in your projects without using the shake command. (recommended!)
require 'shake'
# If you want to load your own project file (optional)
file = Shake.find_in_project('Projectfile') and load file
# Now define some tasks, and then:
Shake.run!Subclassing Shake
You may subclass shake for your own project.
By default, it will not have any of the default tasks (that is, shake help, and
the "invalid command" message). Use include Defaults if you want this behavior.
require 'shake'
class CLI < Shake
include Defaults # optional, see above
task(:flip) do
what = rand < 0.5 ? "heads" : "tails"
puts "The coin says #{what}"
end
end
CLI.run!Defining tasks
# In your Shakefile or something
class Shake
task(:reset) do
# ...
end
# These are optional
task.description = "Resets passwords."
task.usage = "reset USERNAME"
endAlternatively:
class Shake
task(:reset) do
# ...
end
task(:reset).description = "Resets passwords."
end