mercenary-0.3.6/0000755000004100000410000000000012705737440013546 5ustar www-datawww-datamercenary-0.3.6/Rakefile0000644000004100000410000000003412705737440015210 0ustar www-datawww-datarequire "bundler/gem_tasks" mercenary-0.3.6/Gemfile0000644000004100000410000000013612705737440015041 0ustar www-datawww-datasource 'https://rubygems.org' # Specify your gem's dependencies in mercenary.gemspec gemspec mercenary-0.3.6/examples/0000755000004100000410000000000012705737440015364 5ustar www-datawww-datamercenary-0.3.6/examples/logging.rb0000644000004100000410000000221012705737440017332 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib }) require "mercenary" # This example sets the logging mode of mercenary to # debug. Logging messages from "p.logger.debug" will # be output to STDOUT. Mercenary.program(:logger_output) do |p| p.version "5.2.6" p.description 'An example of turning on logging for Mercenary.' p.syntax 'logger_output' p.logger.info "The default log level is INFO. So this will output." p.logger.debug "Since DEBUG is below INFO, this will not output." p.logger(Logger::DEBUG) p.logger.debug "Logger level now set to DEBUG. So everything will output." p.logger.debug "Example of DEBUG level message." p.logger.info "Example of INFO level message." p.logger.warn "Example of WARN level message." p.logger.error "Example of ERROR level message." p.logger.fatal "Example of FATAL level message." p.logger.unknown "Example of UNKNOWN level message." p.action do |args, options| p.logger(Logger::INFO) p.logger.debug "Logger level back to INFO. This line will not output." p.logger.info "This INFO message will output." end end mercenary-0.3.6/examples/help_dialogue.rb0000755000004100000410000000250012705737440020512 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib }) require "mercenary" # This example sets the logging mode of mercenary to # debug. Logging messages from "p.logger.debug" will # be output to STDOUT. Mercenary.program(:help_dialogue) do |p| p.version "2.0.1" p.description 'An example of the help dialogue in Mercenary' p.syntax 'help_dialogue ' p.command(:some_subcommand) do |c| c.version '1.4.2' c.syntax 'some_subcommand [options]' c.description 'Some subcommand to do something' c.option 'an_option', '-o', '--option', 'Some option' c.alias(:blah) c.command(:yet_another_sub) do |f| f.syntax 'yet_another_sub [options]' f.description 'Do amazing things' f.option 'blah', '-b', '--blah', 'Trigger blah flag' f.option 'heh', '-H ARG', '--heh ARG', 'Give a heh' f.action do |args, options| print "Args: " p args print "Opts: " p options end end end p.command(:another_subcommand) do |c| c.syntax 'another_subcommand [options]' c.description 'Another subcommand to do something different.' c.option 'an_option', '-O', '--option', 'Some option' c.option 'another_options', '--pluginzzz', 'Set where the plugins should be found from' end end mercenary-0.3.6/examples/trace.rb0000644000004100000410000000073212705737440017011 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib }) require "mercenary" # This example sets the logging mode of mercenary to # debug. Logging messages from "p.logger.debug" will # be output to STDOUT. Mercenary.program(:trace) do |p| p.version "2.0.1" p.description 'An example of traces in Mercenary' p.syntax 'trace ' p.action do |_, _| raise ArgumentError.new("YOU DID SOMETHING TERRIBLE YOU BUFFOON") end end mercenary-0.3.6/script/0000755000004100000410000000000012705737440015052 5ustar www-datawww-datamercenary-0.3.6/script/examples0000755000004100000410000000061612705737440016621 0ustar www-datawww-data#! /bin/bash set -e function run () { echo "+ ruby ./examples/$@" ruby -e "puts '=' * 79" ruby ./examples/$@ ruby -e "puts '=' * 79" } run logging.rb run logging.rb -v run help_dialogue.rb -h run help_dialogue.rb some_subcommand -h run help_dialogue.rb another_subcommand -h run help_dialogue.rb some_subcommand yet_another_sub -h run help_dialogue.rb some_subcommand yet_another_sub -b mercenary-0.3.6/script/bootstrap0000755000004100000410000000011312705737440017010 0ustar www-datawww-data#! /bin/sh set -e echo "Time to get set up." bundle install echo "Boom." mercenary-0.3.6/script/cibuild0000755000004100000410000000007112705737440016411 0ustar www-datawww-data#! /bin/sh set -ex bundle exec rspec ./script/examples mercenary-0.3.6/script/console0000755000004100000410000000004712705737440016443 0ustar www-datawww-data#! /bin/bash irb -r./lib/mercenary.rb mercenary-0.3.6/.rspec0000644000004100000410000000003212705737440014656 0ustar www-datawww-data--color --format progress mercenary-0.3.6/LICENSE.txt0000644000004100000410000000206212705737440015371 0ustar www-datawww-dataCopyright (c) 2013-2014 Parker Moore MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mercenary-0.3.6/spec/0000755000004100000410000000000012705737440014500 5ustar www-datawww-datamercenary-0.3.6/spec/presenter_spec.rb0000644000004100000410000000332212705737440020046 0ustar www-datawww-datarequire 'spec_helper' describe(Mercenary::Presenter) do let(:supercommand) { Mercenary::Command.new(:script_name) } let(:command) { Mercenary::Command.new(:subcommand, supercommand) } let(:presenter) { described_class.new(command) } before(:each) do command.version '1.4.2' command.description 'Do all the things.' command.option 'one', '-1', '--one', 'The first option' command.option 'two', '-2', '--two', 'The second option' command.alias :cmd supercommand.commands[command.name] = command end it "knows how to present the command" do expect(presenter.command_presentation).to eql("script_name subcommand 1.4.2 -- Do all the things.\n\nUsage:\n\n script_name subcommand\n\nOptions:\n -1, --one The first option\n -2, --two The second option") end it "knows how to present the subcommands, without duplicates for aliases" do expect(described_class.new(supercommand).subcommands_presentation).to eql(" subcommand, cmd Do all the things.") end it "knows how to present the usage" do expect(presenter.usage_presentation).to eql(" script_name subcommand") end it "knows how to present the options" do expect(presenter.options_presentation).to eql(" -1, --one The first option\n -2, --two The second option") end it "allows you to say print_* instead of *_presentation" do expect(presenter.print_usage).to eql(presenter.usage_presentation) expect(presenter.print_subcommands).to eql(presenter.subcommands_presentation) expect(presenter.print_options).to eql(presenter.options_presentation) expect(presenter.print_command).to eql(presenter.command_presentation) end end mercenary-0.3.6/spec/spec_helper.rb0000644000004100000410000000073712705737440017325 0ustar www-datawww-datalib = File.expand_path('../../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'mercenary' RSpec.configure do |config| config.run_all_when_everything_filtered = true config.filter_run :focus # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = 'random' end mercenary-0.3.6/spec/option_spec.rb0000644000004100000410000000432212705737440017350 0ustar www-datawww-datarequire 'spec_helper' describe(Mercenary::Option) do let(:config_key) { "largo" } let(:description) { "This is a description" } let(:switches) { ['-l', '--largo'] } let(:option) { described_class.new(config_key, [switches, description].flatten.reject(&:nil?)) } it "knows its config key" do expect(option.config_key).to eql(config_key) end it "knows its description" do expect(option.description).to eql(description) end it "knows its switches" do expect(option.switches).to eql(switches) end it "knows how to present itself" do expect(option.to_s).to eql(" -l, --largo #{description}") end it "has an OptionParser representation" do expect(option.for_option_parser).to eql([switches, description].flatten) end it "compares itself with other options well" do new_option = described_class.new(config_key, ['-l', '--largo', description]) expect(option.eql?(new_option)).to be(true) expect(option.hash.eql?(new_option.hash)).to be(true) end it "has a custom #hash" do expect(option.hash.to_s).to match(/\d+/) end context "with just the long switch" do let(:switches) { ['--largo'] } it "adds an empty string in place of the short switch" do expect(option.switches).to eql(['', '--largo']) end it "sets its description properly" do expect(option.description).to eql(description) end it "knows how to present the switch" do expect(option.formatted_switches).to eql(" --largo ") end end context "with just the short switch" do let(:switches) { ['-l'] } it "adds an empty string in place of the long switch" do expect(option.switches).to eql(['-l', '']) end it "sets its description properly" do expect(option.description).to eql(description) end it "knows how to present the switch" do expect(option.formatted_switches).to eql(" -l ") end end context "without a description" do let(:description) { nil } it "knows there is no description" do expect(option.description).to be(nil) end it "knows both inputs are switches" do expect(option.switches).to eql(switches) end end end mercenary-0.3.6/spec/command_spec.rb0000644000004100000410000000534012705737440017457 0ustar www-datawww-datarequire "spec_helper" describe(Mercenary::Command) do context "a basic command" do let(:command) { Mercenary::Command.new(:my_name) } let(:parent) { Mercenary::Command.new(:my_parent) } let(:with_sub) do c = Mercenary::Command.new(:i_have_subcommand) add_sub.call(c) c end let(:command_with_parent) do Mercenary::Command.new( :i_have_parent, parent ) end let(:add_sub) do Proc.new do |c| c.command(:sub_command) { |p| } end end it "can be created with just a name" do expect(command.name).to eql(:my_name) end it "can hold a parent command" do expect(command_with_parent.parent).to eql(parent) end it "can create subcommands" do expect(add_sub.call(command)).to be_a(Mercenary::Command) expect(add_sub.call(command).parent).to eq(command) end it "can set its version" do version = "1.4.2" command.version version expect(command.version).to eq(version) end it "can set its syntax" do syntax_string = "my_name [options]" cmd = described_class.new(:my_name) cmd.syntax syntax_string expect(cmd.syntax).to eq(syntax_string) end it "can set its description" do desc = "run all the things" command.description desc expect(command.description).to eq(desc) end it "can set its options" do name = "show_drafts" opts = ['--drafts', 'Render posts in the _drafts folder'] option = Mercenary::Option.new(name, opts) command.option name, *opts expect(command.options).to eql([option]) expect(command.map.values).to include(name) end it "knows its full name" do expect(command_with_parent.full_name).to eql("my_parent i_have_parent") end it "knows its identity" do command_with_parent.version '1.8.7' expect(command_with_parent.identity).to eql("my_parent i_have_parent 1.8.7") end it "raises an ArgumentError if I specify a default_command that isn't there" do c = command # some weird NameError with the block below? expect { c.default_command(:nope) }.to raise_error(ArgumentError) end it "sets the default_command" do expect(with_sub.default_command(:sub_command).name).to eq(:sub_command) end context "with an alias" do before(:each) do command_with_parent.alias(:an_alias) end it "shows the alias in the summary" do expect(command_with_parent.summarize).to eql(" i_have_parent, an_alias ") end it "its names_and_aliases method reports both the name and alias" do expect(command_with_parent.names_and_aliases).to eql("i_have_parent, an_alias") end end end end mercenary-0.3.6/spec/program_spec.rb0000644000004100000410000000062112705737440017505 0ustar www-datawww-datarequire "spec_helper" describe(Mercenary::Program) do context "a basic program" do let(:program) { Mercenary::Program.new(:my_name) } it "can be created with just a name" do expect(program.name).to eql(:my_name) end it "can set its version" do version = Mercenary::VERSION program.version version expect(program.version).to eq(version) end end end mercenary-0.3.6/.travis.yml0000644000004100000410000000037012705737440015657 0ustar www-datawww-datalanguage: ruby rvm: - 1.9.3 - 2.0.0 - 2.1 sudo: false cache: bundler before_script: bundle update script: "./script/cibuild" notifications: email: recipients: - mercenary@jekyllrb.com on_success: change on_failure: change mercenary-0.3.6/lib/0000755000004100000410000000000012705737440014314 5ustar www-datawww-datamercenary-0.3.6/lib/mercenary.rb0000644000004100000410000000117112705737440016626 0ustar www-datawww-datarequire File.expand_path("../mercenary/version", __FILE__) require "optparse" require "logger" module Mercenary autoload :Command, File.expand_path("../mercenary/command", __FILE__) autoload :Option, File.expand_path("../mercenary/option", __FILE__) autoload :Presenter, File.expand_path("../mercenary/presenter", __FILE__) autoload :Program, File.expand_path("../mercenary/program", __FILE__) # Public: Instantiate a new program and execute. # # name - the name of your program # # Returns nothing. def self.program(name) program = Program.new(name) yield program program.go(ARGV) end end mercenary-0.3.6/lib/mercenary/0000755000004100000410000000000012705737440016301 5ustar www-datawww-datamercenary-0.3.6/lib/mercenary/presenter.rb0000644000004100000410000000550712705737440020644 0ustar www-datawww-datamodule Mercenary class Presenter attr_accessor :command # Public: Make a new Presenter # # command - a Mercenary::Command to present # # Returns nothing def initialize(command) @command = command end # Public: Builds a string representation of the command usage # # Returns the string representation of the command usage def usage_presentation " #{command.syntax}" end # Public: Builds a string representation of the options # # Returns the string representation of the options def options_presentation return nil unless command_options_presentation || parent_command_options_presentation [command_options_presentation, parent_command_options_presentation].compact.join("\n") end def command_options_presentation return nil unless command.options.size > 0 command.options.map(&:to_s).join("\n") end # Public: Builds a string representation of the options for parent # commands # # Returns the string representation of the options for parent commands def parent_command_options_presentation return nil unless command.parent Presenter.new(command.parent).options_presentation end # Public: Builds a string representation of the subcommands # # Returns the string representation of the subcommands def subcommands_presentation return nil unless command.commands.size > 0 command.commands.values.uniq.map(&:summarize).join("\n") end # Public: Builds the command header, including the command identity and description # # Returns the command header as a String def command_header header = "#{command.identity}" header << " -- #{command.description}" if command.description header end # Public: Builds a string representation of the whole command # # Returns the string representation of the whole command def command_presentation msg = [] msg << command_header msg << "Usage:" msg << usage_presentation if opts = options_presentation msg << "Options:\n#{opts}" end if subcommands = subcommands_presentation msg << "Subcommands:\n#{subcommands_presentation}" end msg.join("\n\n") end # Public: Turn a print_* into a *_presentation or freak out # # meth - the method being called # args - an array of arguments passed to the missing method # block - the block passed to the missing method # # Returns the value of whatever function is called def method_missing(meth, *args, &block) if meth.to_s =~ /^print_(.+)$/ send("#{$1.downcase}_presentation") else super # You *must* call super if you don't handle the method, # otherwise you'll mess up Ruby's method lookup. end end end end mercenary-0.3.6/lib/mercenary/command.rb0000644000004100000410000001761212705737440020253 0ustar www-datawww-datamodule Mercenary class Command attr_reader :name attr_reader :description attr_reader :syntax attr_accessor :options attr_accessor :commands attr_accessor :actions attr_reader :map attr_accessor :parent attr_reader :trace attr_reader :aliases # Public: Creates a new Command # # name - the name of the command # parent - (optional) the instancce of Mercenary::Command which you wish to # be the parent of this command # # Returns nothing def initialize(name, parent = nil) @name = name @options = [] @commands = {} @actions = [] @map = {} @parent = parent @trace = false @aliases = [] end # Public: Sets or gets the command version # # version - the command version (optional) # # Returns the version and sets it if an argument is non-nil def version(version = nil) @version = version if version @version end # Public: Sets or gets the syntax string # # syntax - the string which describes this command's usage syntax (optional) # # Returns the syntax string and sets it if an argument is present def syntax(syntax = nil) @syntax = syntax if syntax syntax_list = [] if parent syntax_list << parent.syntax.to_s.gsub(/<[\w\s-]+>/, '').gsub(/\[[\w\s-]+\]/, '').strip end syntax_list << (@syntax || name.to_s) syntax_list.join(" ") end # Public: Sets or gets the command description # # description - the description of what the command does (optional) # # Returns the description and sets it if an argument is present def description(desc = nil) @description = desc if desc @description end # Public: Sets the default command # # command_name - the command name to be executed in the event no args are # present # # Returns the default command if there is one, `nil` otherwise def default_command(command_name = nil) if command_name if commands.has_key?(command_name) @default_command = commands[command_name] if command_name @default_command else raise ArgumentError.new("'#{command_name}' couldn't be found in this command's list of commands.") end else @default_command end end # Public: Adds an option switch # # sym - the variable key which is used to identify the value of the switch # at runtime in the options hash # # Returns nothing def option(sym, *options) new_option = Option.new(sym, options) @options << new_option @map[new_option] = sym end # Public: Adds a subcommand # # cmd_name - the name of the command # block - a block accepting the new instance of Mercenary::Command to be # modified (optional) # # Returns nothing def command(cmd_name) cmd = Command.new(cmd_name, self) yield cmd @commands[cmd_name] = cmd end # Public: Add an alias for this command's name to be attached to the parent # # cmd_name - the name of the alias # # Returns nothing def alias(cmd_name) logger.debug "adding alias to parent for self: '#{cmd_name}'" aliases << cmd_name @parent.commands[cmd_name] = self end # Public: Add an action Proc to be executed at runtime # # block - the Proc to be executed at runtime # # Returns nothing def action(&block) @actions << block end # Public: Fetch a Logger (stdlib) # # level - the logger level (a Logger constant, see docs for more info) # # Returns the instance of Logger def logger(level = nil) unless @logger @logger = Logger.new(STDOUT) @logger.level = level || Logger::INFO @logger.formatter = proc do |severity, datetime, progname, msg| "#{identity} | " << "#{severity.downcase.capitalize}:".ljust(7) << " #{msg}\n" end end @logger.level = level unless level.nil? @logger end # Public: Run the command # # argv - an array of string args # opts - the instance of OptionParser # config - the output config hash # # Returns the command to be executed def go(argv, opts, config) opts.banner = "Usage: #{syntax}" process_options(opts, config) add_default_options(opts) if argv[0] && cmd = commands[argv[0].to_sym] logger.debug "Found subcommand '#{cmd.name}'" argv.shift cmd.go(argv, opts, config) else logger.debug "No additional command found, time to exec" self end end # Public: Add this command's options to OptionParser and set a default # action of setting the value of the option to the inputted hash # # opts - instance of OptionParser # config - the Hash in which the option values should be placed # # Returns nothing def process_options(opts, config) options.each do |option| opts.on(*option.for_option_parser) do |x| config[map[option]] = x end end end # Public: Add version and help options to the command # # opts - instance of OptionParser # # Returns nothing def add_default_options(opts) option 'show_help', '-h', '--help', 'Show this message' option 'show_version', '-v', '--version', 'Print the name and version' option 'show_backtrace', '-t', '--trace', 'Show the full backtrace when an error occurs' opts.on("-v", "--version", "Print the version") do puts "#{name} #{version}" exit(0) end opts.on('-t', '--trace', 'Show full backtrace if an error occurs') do @trace = true end opts.on_tail("-h", "--help", "Show this message") do puts self exit end end # Public: Execute all actions given the inputted args and options # # argv - (optional) command-line args (sans opts) # config - (optional) the Hash configuration of string key to value # # Returns nothing def execute(argv = [], config = {}) if actions.empty? && !default_command.nil? default_command.execute else actions.each { |a| a.call(argv, config) } end end # Public: Check if this command has a subcommand # # sub_command - the name of the subcommand # # Returns true if this command is the parent of a command of name # 'sub_command' and false otherwise def has_command?(sub_command) commands.keys.include?(sub_command) end # Public: Identify this command # # Returns a string which identifies this command def ident "" end # Public: Get the full identity (name & version) of this command # # Returns a string containing the name and version if it exists def identity "#{full_name} #{version if version}".strip end # Public: Get the name of the current command plus that of # its parent commands # # Returns the full name of the command def full_name the_name = [] the_name << parent.full_name if parent && parent.full_name the_name << name the_name.join(" ") end # Public: Return all the names and aliases for this command. # # Returns a comma-separated String list of the name followed by its aliases def names_and_aliases ([name.to_s] + aliases).compact.join(", ") end # Public: Build a string containing a summary of the command # # Returns a one-line summary of the command. def summarize " #{names_and_aliases.ljust(20)} #{description}" end # Public: Build a string containing the command name, options and any subcommands # # Returns the string identifying this command, its options and its subcommands def to_s Presenter.new(self).print_command end end end mercenary-0.3.6/lib/mercenary/program.rb0000644000004100000410000000220112705737440020270 0ustar www-datawww-datamodule Mercenary class Program < Command attr_reader :optparse attr_reader :config # Public: Creates a new Program # # name - the name of the program # # Returns nothing def initialize(name) @config = {} super(name) end # Public: Run the program # # argv - an array of string args (usually ARGV) # # Returns nothing def go(argv) logger.debug("Using args passed in: #{argv.inspect}") cmd = nil @optparse = OptionParser.new do |opts| cmd = super(argv, opts, @config) end begin @optparse.parse!(argv) rescue OptionParser::InvalidOption => e logger.error "Whoops, we can't understand your command." logger.error "#{e.message}" logger.error "Run your command again with the --help switch to see available options." abort end logger.debug("Parsed config: #{@config.inspect}") begin cmd.execute(argv, @config) rescue => e if cmd.trace raise e else logger.error e.message abort end end end end end mercenary-0.3.6/lib/mercenary/version.rb0000644000004100000410000000005112705737440020307 0ustar www-datawww-datamodule Mercenary VERSION = "0.3.6" end mercenary-0.3.6/lib/mercenary/option.rb0000644000004100000410000000505612705737440020144 0ustar www-datawww-datamodule Mercenary class Option attr_reader :config_key, :description, :short, :long, :return_type # Public: Create a new Option # # config_key - the key in the config hash to which the value of this option # will map # info - an array containing first the switches, then an optional # return type (e.g. Array), then a description of the option # # Returns nothing def initialize(config_key, info) @config_key = config_key while arg = info.shift begin @return_type = Object.const_get("#{arg}") next rescue NameError end if arg.start_with?("-") if arg.start_with?("--") @long = arg else @short = arg end next end @description = arg end end # Public: Fetch the array containing the info OptionParser is interested in # # Returns the array which OptionParser#on wants def for_option_parser [short, long, return_type, description].flatten.reject{ |o| o.to_s.empty? } end # Public: Build a string representation of this option including the # switches and description # # Returns a string representation of this option def to_s "#{formatted_switches} #{description}" end # Public: Build a beautifully-formatted string representation of the switches # # Returns a formatted string representation of the switches def formatted_switches [ switches.first.rjust(10), switches.last.ljust(13) ].join(", ").gsub(/ , /, ' ').gsub(/, /, ' ') end # Public: Hash based on the hash value of instance variables # # Returns a Fixnum which is unique to this Option based on the instance variables def hash instance_variables.map do |var| instance_variable_get(var).hash end.reduce(:^) end # Public: Check equivalence of two Options based on equivalence of their # instance variables # # Returns true if all the instance variables are equal, false otherwise def eql?(other) return false unless self.class.eql?(other.class) instance_variables.map do |var| instance_variable_get(var).eql?(other.instance_variable_get(var)) end.all? end # Public: Fetch an array of switches, including the short and long versions # # Returns an array of two strings. An empty string represents no switch in # that position. def switches [short, long].map(&:to_s) end end end mercenary-0.3.6/History.markdown0000644000004100000410000000442712705737440016762 0ustar www-datawww-data## 0.3.6 / 2016-04-07 ### Bug Fixes * Presenter: Options should include those from parent command (#42) ## 0.3.5 / 2014-11-12 ### Bug Fixes * Capture `OptionsParser::InvalidOption` and show a nice error message (#38) * Absolute paths for requires and autoloads (#39) ### Development Fixes * Bump to RSpec 3 (#40) ## 0.3.4 / 2014-07-11 ### Bug Fixes * Use option object as key in the command's `@map` hash (#35) ## 0.3.3 / 2014-05-07 ### Bug Fixes * The `--version` flag should not exit with code 1, but instead code 0. (#33) ## 0.3.2 / 2014-03-18 ### Bug Fixes * Remove duplicate commands from help output; show aliases w/command names (#29) ## 0.3.1 / 2014-02-21 ### Minor Enhancements * Add `-t/--trace` to list of options in help message (#19) ### Bug Fixes * `Mercenary::Option` now accepts return values in the form of Class constants (#22) ## 0.3.0 / 2014-02-20 ### Major Enhancements * Officially drop 1.8.7 support (#14) * Allow Commands to set their own versions (#17) * Show subcommands, options and usage in help and attach to all commands (#18) * Add `-t, --trace` to allow full exception backtrace to print, otherwise print just the error message (#19) ### Minor Enhancements * Logging state is maintained throughout process (#12) * Tidy up Command#logger output (#21) ### Development Fixes * Added specs for `Program` (#13) ## 0.2.1 / 2013-12-25 ### Bug Fixes * Added missing comma to fix '-v' and '--version' options (#9) ## 0.2.0 / 2013-11-30 ### Major Enhancements * Add `Command#default_command` to specify a default command if none is given by the user at runtime (#7) ### Minor Enhancements * Add `Command#execute` to execute the actions of a command (#6) ### Development Fixes * Add standard GitHub bootstrap and cibuild scripts to `script/` (#2) ## 0.1.0 / 2013-11-08 ### Major Enhancements * It works! ### Minor Enhancements * Add a logger to `Command` * Add `--version` switch to all programs ### Bug Fixes * Fix `Command#syntax` and `Command#description`'s handing of setting vs getting * Fix load path problem in `lib/mercenary.rb` ### Development Fixes * Add TomDoc to everything * Add a couple starter specs * Add TravisCI badge * Add Travis configuration ## 0.0.1 / 2013-11-06 * Birthday! mercenary-0.3.6/.gitignore0000644000004100000410000000023212705737440015533 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp mercenary-0.3.6/mercenary.gemspec0000644000004100000410000000202512705737440017077 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'mercenary/version' Gem::Specification.new do |spec| spec.name = "mercenary" spec.version = Mercenary::VERSION spec.authors = ["Tom Preston-Werner", "Parker Moore"] spec.email = ["tom@mojombo.com", "parkrmoore@gmail.com"] spec.description = %q{Lightweight and flexible library for writing command-line apps in Ruby.} spec.summary = %q{Lightweight and flexible library for writing command-line apps in Ruby.} spec.homepage = "https://github.com/jekyll/mercenary" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 3.0" end mercenary-0.3.6/README.md0000644000004100000410000001613512705737440015033 0ustar www-datawww-data# Mercenary Lightweight and flexible library for writing command-line apps in Ruby. [![Build Status](https://secure.travis-ci.org/jekyll/mercenary.png)](https://travis-ci.org/jekyll/mercenary) ## Installation Add this line to your application's Gemfile: gem 'mercenary' And then execute: $ bundle Or install it yourself as: $ gem install mercenary **Note: Mercenary may not work with Ruby < 1.9.3.** ## Usage Creating programs and commands with Mercenary is easy: ```ruby Mercenary.program(:jekyll) do |p| p.version Jekyll::VERSION p.description 'Jekyll is a blog-aware, static site generator in Ruby' p.syntax "jekyll [options]" p.command(:new) do |c| c.syntax "new PATH" # do not include the program name or super commands c.description "Creates a new Jekyll site scaffold in PATH" c.option 'blank', '--blank', 'Initialize the new site without any content.' c.action do |args, options| Jekyll::Commands::New.process(args, blank: options['blank']) end end p.command(:build) do |c| c.syntax "build [options]" c.description "Builds your Jekyll site" c.option 'safe', '--safe', 'Run in safe mode' c.option 'source', '--source DIR', 'From where to collect the source files' c.option 'destination', '--dest DIR', 'To where the compiled files should be written' c.action do |_, options| Jekyll::Commands::Build.process(options) end end # Bring in command bundled in external gem begin require "jekyll-import" JekyllImport.init_with_program(p) rescue LoadError end p.default_command(:build) end ``` All commands have the following default options: - `-h/--help` - show a help message - `-v/--version` - show the program version - `-t/--trace` - show the full backtrace when an error occurs ## API ### `Mercenary` #### `.program` Creates and executes a program. Accepts two arguments: - `name` - program name as a Symbol - `block` - the specification for the program, passed the program instance as an argument. Example is above, under the heading [Usage](#usage). ### `Program` `Program` is a subclass of `Command`, so it has all of the methods documented below as well as those for `Command`. #### `#config` Fetches the program configuration hash. ### `Command` #### `#new` Create a new command. Accepts two arguments: - `name` - the name of your command, as a symbol - `parent` - (optional) the parent Command #### `#version` Sets or gets the version of the command. Accepts an optional argument: - `version` - (optional) the version to set for the command. If present, this becomes the new version for the command and persists. #### `#syntax` Sets or gets the syntax of the command. Built on parent syntaxes if a parent exists. Accepts one optional argument: - `syntax` - (optional) the syntax to set for the command. Will inherit from the parent commands or program. Usually in the form of `"command_name [OPTIONS]"` When a parent command exists, say `supercommand`, with syntax set as `supercommand [OPTIONS]`, the syntax of the command in question will be `supercommand command_name [OPTIONS]` with both `` and `[OPTIONS]` stripped out. Any text between `<` and `>` or between `[` and `]` will be stripped from parent command syntaxes. The purpose of this chaining is to reduce redundancy. #### `#description` Sets or gets the description of the command. Accepts one optional argument: - `desc` - (optional) the description to set for the command. If provided, will override any previous description set for the command. #### `#default_command` Sets or gets the default subcommand of the command to execute in the event no subcommand is passed during execution. Accepts one optional argument: - `command_name` - (optional) the `Symbol` name of the subcommand to be executed. Raises an `ArgumentError` if the subcommand doesn't exist. Overwrites previously-set default commands. #### `#option` Adds a new option to the command. Accepts many arguments: - `config_key` - the configuration key that the value of this option maps to. - `*options` - all the options, globbed, to be passed to `OptionParser`, namely the switches and the option description. Usually in the format `"-s", "--switch", "Sets the 'switch' flag"`. Valid option calls: ```ruby cmd.option 'config_key', '-c', 'Sets the "config" flag' cmd.option 'config_key', '--config', 'Sets the "config" flag' cmd.option 'config_key', '-c', '--config', 'Sets the "config" flag.' cmd.option 'config_key', '-c FILE', '--config FILE', 'The config file.' cmd.option 'config_key', '-c FILE1[,FILE2[,FILE3...]]', '--config FILE1[,FILE2[,FILE3...]]', Array, 'The config files.' ``` Notice that you can specify either a short switch, a long switch, or both. If you want to accept an argument, you have to specify it in the switch strings. The class of the argument defaults to `String`, but you can optionally set a different class to create, e.g. `Array`, if you are expecting a particular class in your code from this option's value. The description is also optional, but it's highly recommended to include a description. #### `#alias` Specifies an alias for this command such that the alias may be used in place of the command during execution. Accepts one argument: - `cmd_name` - the alias name for this command as a `Symbol` Example: ```ruby cmd.alias(:my_alias) # Now `cmd` is now also executable via "my_alias" ``` #### `#action` Specifies a block to be executed in the event the command is specified at runtime. The block is given two arguments: - `args` - the non-switch arguments given from the command-line - `options` - the options hash built via the switches passed **Note that actions are additive**, meaning any new call to `#action` will result in another action to be executed at runtime. Actions will be executed in the order they are specified in. Example: ```ruby cmd.action do |args, options| # do something! end ``` #### `#logger` Access the logger for this command. Useful for outputting information to STDOUT. Accepts one optional argument: - `level` - (optional) the severity threshold at which to begin logging. Uses Ruby's built-in [`Logger`](http://www.ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html) levels. Log level defaults to `Logger::INFO`. Examples: ```ruby cmd.logger(Logger::DEBUG) cmd.logger.debug "My debug message." cmd.logger.info "My informative message." cmd.logger.warn "ACHTUNG!!" cmd.logger.error "Something terrible has happened." cmd.logger.fatal "I can't continue doing what I'm doing." ``` #### `#command` Creates a new subcommand for the current command. Accepts two arguments: - `cmd_name` - the command name, as a Symbol - `block` - the specification of the subcommand in a block Example: ```ruby my_command.command(:my_subcommand) do |subcmd| subcmd.description 'My subcommand' subcmd.syntax 'my_subcommand [OPTIONS]' # ... end ``` ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request