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"
end
In 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 [email protected] git pull && thin restart"
end
end
And 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}"
end
Example:
$ 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})"
end
Example:
$ 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
end
You 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?"
}
end
In 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
end
Then use them in your tasks.
class Shake
task(:info) do
say_status :info, "It's a fine day"
end
end
Manual 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"
end
Alternatively:
class Shake
task(:reset) do
# ...
end
task(:reset).description = "Resets passwords."
end