require "optparse" module CLI # An App class App attr_accessor :binary, :name, :version attr_reader :proxy, :args, :opts, :options def initialize(binary, &block) @binary = binary @name = binary @actions = {} @use_actions = false @proxy = Proxy.new(self) @args = [] @opts = OptionParser.new @options = {} proxy.instance_eval &block end # Hash of action_name => block to be executed def actions @actions end # Defines an action def action(name, &block) @use_actions = true if name != "default" @actions[name] = block end # Runs an action def run(action_name) block = @actions[action_name] unless block if action_name == "default" raise RuntimeError, "Error: you have to define 'default' action" end raise ArgumentError, "action '#{action_name}' not found" end instance_eval &block end # Parses the arguments (with OptionParser) and runs # specified or default action. def run!(args) @opts.parse!(args) @args = args begin if args == [] || @use_actions == false run "default" else run args[0] end rescue => e puts e.message.capitalize end end end # Proxy class is responsible for cool DSL inside CLI.app do ... end block class Proxy attr_reader :app def initialize(app) @app = app end # Sets the App binary_name def binary(val) app.binary = val end # Sets the App name def name(val) app.name = val end # Sets the App version def version(val) app.version = val end # Defines the default action def default(&block) app.action("default", &block) end # Define option for OptionParser def option(*args, &block) app.opts.on(*args, &block) end # Delegates to App def method_missing(name, *attrs, &block) app.send(name, *attrs, &block) end end end