optimist-3.0.0/0000755000175000017500000000000013616055261013313 5ustar utkarshutkarshoptimist-3.0.0/.travis.yml0000644000175000017500000000022513616055261015423 0ustar utkarshutkarshlanguage: ruby sudo: false rvm: - "2.2" - "2.3.4" - "2.5.1" - jruby-head matrix: allow_failures: - rvm: jruby-head fast_finish: true optimist-3.0.0/History.txt0000644000175000017500000001455513616055261015527 0ustar utkarshutkarsh== [3.0.0] / 2018-08-24 * The gem has been officially renamed to optimist == [2.1.3] / 2018-07-05 * Refactor each option type into subclasses of Option. Define a registry for the registration of each option. This makes the code more modular and facilitates extension by allowing additional Option subclasses. (thanks @clxy) * Fixed implementation of ignore_invalid_options. (thanks @metcalf) * Various warning cleanup for ruby 2.1, 2.3, etc. (thanks @nanobowers) * Optimist.die can now accept an error code. * fixed default (thanks @nanobowers) * Change from ruby license to MIT license in the code. == [2.1.2] / 2015-03-10 * loosen mime-types requirements (for better ruby 1.8.7 support) * use io/console gem instead of curses (for better jruby support) * fix parsing bug when chronic gem is not available * allow default array to be empty if a type is specified * better specified license and better spec coverage == [2.1.1] / 2015-01-03 * Remove curses as a hard dependency. It is optional. This can leverage the gem if it is present. * Fix ruby -w warnings == 2.1.0 / 2015-01-02 * Integer parser now supports underscore separator. * Add Parser#usage and Parser#synopsis commands for creating a standard banner message. Using Parser#banner directly will override both of those. * Add Parser#ignore_invalid_options to prevent erroring on unknown options. * Allow flags to act as switches if they have defaults set and no value is passed on the commandline * Parser#opt learned to accept a block or a :callback option which it will call after parsing the option. * Add Optimist::educate which displays the help message and dies. * Reformat help message to be more GNUish. * Fix command name in help message when script has no extension. * Fix handling of newlines inside descriptions * Documentation and other fixes. == 2.0 / 2012-08-11 * Change flag logic: --no-X will always be false, and --X will always be true, regardless of default. * For flags that default to true, display --no-X instead of --X in the help menu. Accept both versions on the commandline. * Fix a spurious warning * Update Rakefile to 1.9 * Minor documentation fixes == 1.16.2 / 2010-04-06 * Bugfix in Optimist::options. Thanks to Brian C. Thomas for pointing it out. == 1.16.1 / 2010-04-05 * Bugfix in Optimist::die method introduced in last release. == 1.16 / 2010-04-01 * Add Optimist::with_standard_exception_handling method for easing the use of Parser directly. * Handle scientific notation in float arguments, thanks to Will Fitzgerald. * Drop hoe dependency. == 1.15 / 2009-09-30 * Don't raise an exception when out of short arguments (thanks to Rafael Sevilla for pointing out how dumb this behavior was). == 1.14 / 2009-06-19 * Make :multi arguments default to [], not nil, when not set on the commandline. * Minor commenting and error message improvements == 1.13 / 2009-03-16 * Fix parsing of "--longarg=". == 1.12 / 2009-01-30 * Fix some unit test failures in the last release. Should be more careful. * Make default short options only be assigned *after* all user-specified short options. Now there's a little less juggling to do when you just want to specify a few short options. == 1.11 / 2009-01-29 * Set _given keys in the results hash for options that were specified on the commandline. == 1.10.2 / 2008-10-23 * No longer try `stty size` for screen size detection. Just use curses, and screen users will have to deal with the screen clearing. == 1.10.1 / 2008-10-22 * Options hash now responds to method calls as well as standard hash lookup. * Default values for multi-occurrence parameters now autoboxed. * The relationship between multi-value, multi-occurrence, and default values improved and explained. * Documentation improvements. == 1.10 / 2008-10-21 * Added :io type for parameters that point to IO streams (filenames, URIs, etc). * For screen size detection, first try `stty size` before loading Curses. * Improved documentation. == 1.9 / 2008-08-20 * Added 'stop_on_unknown' command to stop parsing on any unknown argument. This is useful for handling sub-commands when you don't know the entire set of commands up front. (E.g. if the initial arguments can change it.) * Added a :multi option for parameters, signifying that they can be specified multiple times. * Added :ints, :strings, :doubles, and :floats option types, which can take multiple arguments. == 1.8.2 / 2008-06-25 * Bugfix for #conflicts and #depends error messages == 1.8.1 / 2008-06-24 * Bugfix for short option autocreation * More aggressive documentation == 1.8 / 2008-06-16 * Sub-command support via Parser#stop_on == 1.7.2 / 2008-01-16 * Ruby 1.9-ify. Apparently this means replacing :'s with ;'s. == 1.7.1 / 2008-01-07 * Documentation improvements == 1.7 / 2007-06-17 * Fix incorrect error message for multiple missing required arguments (thanks to Neill Zero) == 1.6 / 2007-04-01 * Don't attempt curses screen-width magic unless running on a terminal. == 1.5 / 2007-03-31 * --help and --version do the right thing even if the rest of the command line is incorrect. * Added #conflicts and #depends to model dependencies and exclusivity between arguments. * Minor bugfixes. == 1.4 / 2007-03-26 * Disable short options with :short => :none. * Minor bugfixes and error message improvements. == 1.3 / 2007-01-31 * Wrap at (screen width - 1) instead of screen width. * User can override --help and --version. * Bugfix in handling of -v and -h. * More tests to confirm the above. == 1.2 / 2007-01-31 * Minor documentation tweaks. * Removed hoe dependency. == 1.1 / 2007-01-30 * Optimist::options now passes any arguments as block arguments. Since instance variables are not properly captured by the block, this makes it slightly less noisy to pass them in as local variables. (A real-life use for _why's cloaker!) * Help display now preserves original argument order. * Optimist::die now also has a single string form in case death is not due to a single argument. * Parser#text now an alias for Parser#banner, and can be called multiple times, with the output being placed in the right position in the help text. * Slightly more indicative formatting for parameterized arguments. == 1.0 / 2007-01-29 * Initial release. [2.1.3]: https://github.com/ManageIQ/optimist/compare/v2.1.2...v2.1.3 [2.1.2]: https://github.com/ManageIQ/optimist/compare/v2.1.1...v2.1.2 [2.1.1]: https://github.com/ManageIQ/optimist/compare/v2.1.0...v2.1.1 optimist-3.0.0/Rakefile0000644000175000017500000000036213616055261014761 0ustar utkarshutkarshrequire 'bundler/gem_tasks' require 'rake/testtask' task :default => :test Rake::TestTask.new do |t| t.libs << 'test' t.pattern = "test/**/*_test.rb" end begin require 'coveralls/rake/task' Coveralls::RakeTask.new rescue LoadError end optimist-3.0.0/FAQ.txt0000644000175000017500000001020013616055261014454 0ustar utkarshutkarshOptimist FAQ ----------- Q: Why should I use Optimist? A: Because it will take you fewer lines of code to parse commandline arguments than anything else out there. Like this: opts = Optimist::options do opt :monkey, "Use monkey mode" opt :goat, "Use goat mode", :default => true opt :num_limbs, "Set number of limbs", :default => 4 end That's it. 'opts' will be a hash and you can do whatever you want with it. You don't have to mix processing code with the declarations. You don't have to make a class for every option (what is this, Java?). You don't have to write more than 1 line of code per option. Plus, you get a beautiful help screen that detects your terminal width and wraps appropriately. Q: What is the philosophy behind Optimist? A: Optimist does the parsing and gives you a hash table of options. You then write whatever fancy constraint logic you need as regular Ruby code operating on that hash table. (Optimist does support limited constraints (see #conflicts and #depends), but any non-trivial program will probably need to get fancier.) Then if you need to abort and tell the user to fix their command line at any point, you can call #die and Optimist will do that for you in a pretty way. Q: What happens to the other stuff on the commandline? A: Anything Optimist doesn't recognize as an option or as an option parameter is left in ARGV for you to process. Q: Does Optimist support multiple-value arguments? A: Yes. If you set the :type of an option to something plural, like ":ints", ":strings", ":doubles", ":floats", ":ios", it will accept multiple arguments on the commandline, and the value will be an array of the parameters. Q: Does Optimist support arguments that can be given multiple times? A: Yes. If you set :multi to true, then the argument can appear multiple times on the commandline, and the value will be an array of the parameters. Q: Does Optimist support subcommands? A: Yes: you can direct Optimist to stop processing when it encounters certain tokens. Then you can re-call Optimist with the subcommand-specific configuration to process the rest of the commandline. See the third example on the webpage. (And if you don't know the subcommands ahead of time, you can call #stop_on_unknown, which will cause Optimist to stop when it encounters any unknown token. This might be more trouble than its worth if you're also passing filenames on the commandline.) Q: Why does Optimist disallow numeric short argument names, like '-1' and '-9'? A: Because it's ambiguous whether these are arguments or negative integer or floating-point parameters to arguments. E.g., is "-f -3" a negative floating point parameter to -f, or two separate arguments? Q: What was the big change in version 2.0? A: The big change was boolean parameter (aka flag) handling. In pre-2.0, not specifying a flag on the commandline would result in the option being set to its default value; specifying it on the commandline would result in the option being set to the opposite of its default value. This was weird for options with a default of true: opt :magic, "Use magic", default: true Using --magic with the above configuration would result in a :magic => false value in the options hash. In 2.0, we introduce the GNU-style notion of a --no-x parameter. Now, specifying --x will always set the option :x to true, regardless of its default value, and specifying --no-x will always set the option :x to false, regardless of its default value. The default value only comes into play when neither form is given on the commandline. E.g.: opt :magic, "Use magic", :default => true Using --magic will result in :magic => true, and --no-magic will result in :magic => false, and neither will result in :magic => true. There is one exception: if the option itself starts with a "no_", then you'll get the opposite behavior: opt :no_magic, "Don't use magic", :default => true Using --magic will result in :no_magic => false, and --no-magic will result in :no_magic => true, and neither will result in :no_magic => true. optimist-3.0.0/test/0000755000175000017500000000000013616055261014272 5ustar utkarshutkarshoptimist-3.0.0/test/test_helper.rb0000644000175000017500000000055113616055261017136 0ustar utkarshutkarsh$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) unless ENV['MUTANT'] begin require "coveralls" Coveralls.wear! do add_filter '/test/' end rescue LoadError end end begin require "pry" rescue LoadError end require 'minitest/autorun' Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f } require 'optimist' optimist-3.0.0/test/optimist/0000755000175000017500000000000013616055261016142 5ustar utkarshutkarshoptimist-3.0.0/test/optimist/help_needed_test.rb0000644000175000017500000000046413616055261021766 0ustar utkarshutkarshrequire 'test_helper' module Optimist class HelpNeededTest < ::MiniTest::Test def test_class assert_kind_of Exception, hn("message") end def test_message assert "message", hn("message").message end private def hn(*args) HelpNeeded.new(*args) end end end optimist-3.0.0/test/optimist/parser_test.rb0000644000175000017500000011323113616055261021023 0ustar utkarshutkarshrequire 'stringio' require 'test_helper' module Optimist class ParserTest < ::MiniTest::Test def setup @p = Parser.new end def parser @p ||= Parser.new end # initialize # cloaker def test_version assert_nil parser.version assert_equal "optimist 5.2.3", parser.version("optimist 5.2.3") assert_equal "optimist 5.2.3", parser.version end def test_usage assert_nil parser.usage assert_equal "usage string", parser.usage("usage string") assert_equal "usage string", parser.usage end def test_synopsis assert_nil parser.synopsis assert_equal "synopsis string", parser.synopsis("synopsis string") assert_equal "synopsis string", parser.synopsis end # def test_depends # def test_conflicts # def test_stop_on # def test_stop_on_unknown # die # def test_die_educate_on_error def test_unknown_arguments assert_raises(CommandlineError) { @p.parse(%w(--arg)) } @p.opt "arg" @p.parse(%w(--arg)) assert_raises(CommandlineError) { @p.parse(%w(--arg2)) } end def test_syntax_check @p.opt "arg" @p.parse(%w(--arg)) @p.parse(%w(arg)) assert_raises(CommandlineError) { @p.parse(%w(---arg)) } assert_raises(CommandlineError) { @p.parse(%w(-arg)) } end def test_required_flags_are_required @p.opt "arg", "desc", :required => true @p.opt "arg2", "desc", :required => false @p.opt "arg3", "desc", :required => false @p.parse(%w(--arg)) @p.parse(%w(--arg --arg2)) assert_raises(CommandlineError) { @p.parse(%w(--arg2)) } assert_raises(CommandlineError) { @p.parse(%w(--arg2 --arg3)) } end ## flags that take an argument error unless given one def test_argflags_demand_args @p.opt "goodarg", "desc", :type => String @p.opt "goodarg2", "desc", :type => String @p.parse(%w(--goodarg goat)) assert_raises(CommandlineError) { @p.parse(%w(--goodarg --goodarg2 goat)) } assert_raises(CommandlineError) { @p.parse(%w(--goodarg)) } end ## flags that don't take arguments ignore them def test_arglessflags_refuse_args @p.opt "goodarg" @p.opt "goodarg2" @p.parse(%w(--goodarg)) @p.parse(%w(--goodarg --goodarg2)) opts = @p.parse %w(--goodarg a) assert_equal true, opts["goodarg"] assert_equal ["a"], @p.leftovers end ## flags that require args of a specific type refuse args of other ## types def test_typed_args_refuse_args_of_other_types @p.opt "goodarg", "desc", :type => :int assert_raises(ArgumentError) { @p.opt "badarg", "desc", :type => :asdf } @p.parse(%w(--goodarg 3)) assert_raises(CommandlineError) { @p.parse(%w(--goodarg 4.2)) } assert_raises(CommandlineError) { @p.parse(%w(--goodarg hello)) } end ## type is correctly derived from :default def test_type_correctly_derived_from_default assert_raises(ArgumentError) { @p.opt "badarg", "desc", :default => [] } assert_raises(ArgumentError) { @p.opt "badarg3", "desc", :default => [{1 => 2}] } assert_raises(ArgumentError) { @p.opt "badarg4", "desc", :default => Hash.new } # single arg: int @p.opt "argsi", "desc", :default => 0 opts = @p.parse(%w(--)) assert_equal 0, opts["argsi"] opts = @p.parse(%w(--argsi 4)) assert_equal 4, opts["argsi"] opts = @p.parse(%w(--argsi=4)) assert_equal 4, opts["argsi"] opts = @p.parse(%w(--argsi=-4)) assert_equal( -4, opts["argsi"]) assert_raises(CommandlineError) { @p.parse(%w(--argsi 4.2)) } assert_raises(CommandlineError) { @p.parse(%w(--argsi hello)) } # single arg: float @p.opt "argsf", "desc", :default => 3.14 opts = @p.parse(%w(--)) assert_equal 3.14, opts["argsf"] opts = @p.parse(%w(--argsf 2.41)) assert_equal 2.41, opts["argsf"] opts = @p.parse(%w(--argsf 2)) assert_equal 2, opts["argsf"] opts = @p.parse(%w(--argsf 1.0e-2)) assert_equal 1.0e-2, opts["argsf"] assert_raises(CommandlineError) { @p.parse(%w(--argsf hello)) } # single arg: date date = Date.today @p.opt "argsd", "desc", :default => date opts = @p.parse(%w(--)) assert_equal Date.today, opts["argsd"] opts = @p.parse(['--argsd', 'Jan 4, 2007']) assert_equal Date.civil(2007, 1, 4), opts["argsd"] assert_raises(CommandlineError) { @p.parse(%w(--argsd hello)) } # single arg: string @p.opt "argss", "desc", :default => "foobar" opts = @p.parse(%w(--)) assert_equal "foobar", opts["argss"] opts = @p.parse(%w(--argss 2.41)) assert_equal "2.41", opts["argss"] opts = @p.parse(%w(--argss hello)) assert_equal "hello", opts["argss"] # multi args: ints @p.opt "argmi", "desc", :default => [3, 5] opts = @p.parse(%w(--)) assert_equal [3, 5], opts["argmi"] opts = @p.parse(%w(--argmi 4)) assert_equal [4], opts["argmi"] assert_raises(CommandlineError) { @p.parse(%w(--argmi 4.2)) } assert_raises(CommandlineError) { @p.parse(%w(--argmi hello)) } # multi args: floats @p.opt "argmf", "desc", :default => [3.34, 5.21] opts = @p.parse(%w(--)) assert_equal [3.34, 5.21], opts["argmf"] opts = @p.parse(%w(--argmf 2)) assert_equal [2], opts["argmf"] opts = @p.parse(%w(--argmf 4.0)) assert_equal [4.0], opts["argmf"] assert_raises(CommandlineError) { @p.parse(%w(--argmf hello)) } # multi args: dates dates = [Date.today, Date.civil(2007, 1, 4)] @p.opt "argmd", "desc", :default => dates opts = @p.parse(%w(--)) assert_equal dates, opts["argmd"] opts = @p.parse(['--argmd', 'Jan 4, 2007']) assert_equal [Date.civil(2007, 1, 4)], opts["argmd"] assert_raises(CommandlineError) { @p.parse(%w(--argmd hello)) } # multi args: strings @p.opt "argmst", "desc", :default => %w(hello world) opts = @p.parse(%w(--)) assert_equal %w(hello world), opts["argmst"] opts = @p.parse(%w(--argmst 3.4)) assert_equal ["3.4"], opts["argmst"] opts = @p.parse(%w(--argmst goodbye)) assert_equal ["goodbye"], opts["argmst"] end ## :type and :default must match if both are specified def test_type_and_default_must_match assert_raises(ArgumentError) { @p.opt "badarg", "desc", :type => :int, :default => "hello" } assert_raises(ArgumentError) { @p.opt "badarg2", "desc", :type => :String, :default => 4 } assert_raises(ArgumentError) { @p.opt "badarg2", "desc", :type => :String, :default => ["hi"] } assert_raises(ArgumentError) { @p.opt "badarg2", "desc", :type => :ints, :default => [3.14] } @p.opt "argsi", "desc", :type => :int, :default => 4 @p.opt "argsf", "desc", :type => :float, :default => 3.14 @p.opt "argsd", "desc", :type => :date, :default => Date.today @p.opt "argss", "desc", :type => :string, :default => "yo" @p.opt "argmi", "desc", :type => :ints, :default => [4] @p.opt "argmf", "desc", :type => :floats, :default => [3.14] @p.opt "argmd", "desc", :type => :dates, :default => [Date.today] @p.opt "argmst", "desc", :type => :strings, :default => ["yo"] end ## def test_flags_with_defaults_and_no_args_act_as_switches @p.opt :argd, "desc", :default => "default_string" opts = @p.parse(%w(--)) assert !opts[:argd_given] assert_equal "default_string", opts[:argd] opts = @p.parse(%w( --argd )) assert opts[:argd_given] assert_equal "default_string", opts[:argd] opts = @p.parse(%w(--argd different_string)) assert opts[:argd_given] assert_equal "different_string", opts[:argd] end def test_flag_with_no_defaults_and_no_args_act_as_switches_array opts = nil @p.opt :argd, "desc", :type => :strings, :default => ["default_string"] opts = @p.parse(%w(--argd)) assert_equal ["default_string"], opts[:argd] end def test_type_and_empty_array @p.opt "argmi", "desc", :type => :ints, :default => [] @p.opt "argmf", "desc", :type => :floats, :default => [] @p.opt "argmd", "desc", :type => :dates, :default => [] @p.opt "argms", "desc", :type => :strings, :default => [] assert_raises(ArgumentError) { @p.opt "badi", "desc", :type => :int, :default => [] } assert_raises(ArgumentError) { @p.opt "badf", "desc", :type => :float, :default => [] } assert_raises(ArgumentError) { @p.opt "badd", "desc", :type => :date, :default => [] } assert_raises(ArgumentError) { @p.opt "bads", "desc", :type => :string, :default => [] } opts = @p.parse([]) assert_equal(opts["argmi"], []) assert_equal(opts["argmf"], []) assert_equal(opts["argmd"], []) assert_equal(opts["argms"], []) end def test_long_detects_bad_names @p.opt "goodarg", "desc", :long => "none" @p.opt "goodarg2", "desc", :long => "--two" assert_raises(ArgumentError) { @p.opt "badarg", "desc", :long => "" } assert_raises(ArgumentError) { @p.opt "badarg2", "desc", :long => "--" } assert_raises(ArgumentError) { @p.opt "badarg3", "desc", :long => "-one" } assert_raises(ArgumentError) { @p.opt "badarg4", "desc", :long => "---toomany" } end def test_short_detects_bad_names @p.opt "goodarg", "desc", :short => "a" @p.opt "goodarg2", "desc", :short => "-b" assert_raises(ArgumentError) { @p.opt "badarg", "desc", :short => "" } assert_raises(ArgumentError) { @p.opt "badarg2", "desc", :short => "-ab" } assert_raises(ArgumentError) { @p.opt "badarg3", "desc", :short => "--t" } end def test_short_names_created_automatically @p.opt "arg" @p.opt "arg2" @p.opt "arg3" opts = @p.parse %w(-a -g) assert_equal true, opts["arg"] assert_equal false, opts["arg2"] assert_equal true, opts["arg3"] end def test_short_autocreation_skips_dashes_and_numbers @p.opt :arg # auto: a @p.opt :arg_potato # auto: r @p.opt :arg_muffin # auto: g @p.opt :arg_daisy # auto: d (not _)! @p.opt :arg_r2d2f # auto: f (not 2)! opts = @p.parse %w(-f -d) assert_equal true, opts[:arg_daisy] assert_equal true, opts[:arg_r2d2f] assert_equal false, opts[:arg] assert_equal false, opts[:arg_potato] assert_equal false, opts[:arg_muffin] end def test_short_autocreation_is_ok_with_running_out_of_chars @p.opt :arg1 # auto: a @p.opt :arg2 # auto: r @p.opt :arg3 # auto: g @p.opt :arg4 # auto: uh oh! @p.parse [] end def test_short_can_be_nothing @p.opt "arg", "desc", :short => :none @p.parse [] sio = StringIO.new "w" @p.educate sio assert sio.string =~ /--arg\s+desc/ assert_raises(CommandlineError) { @p.parse %w(-a) } end ## two args can't have the same name def test_conflicting_names_are_detected @p.opt "goodarg" assert_raises(ArgumentError) { @p.opt "goodarg" } end ## two args can't have the same :long def test_conflicting_longs_detected @p.opt "goodarg", "desc", :long => "--goodarg" assert_raises(ArgumentError) { @p.opt "badarg", "desc", :long => "--goodarg" } end ## two args can't have the same :short def test_conflicting_shorts_detected @p.opt "goodarg", "desc", :short => "-g" assert_raises(ArgumentError) { @p.opt "badarg", "desc", :short => "-g" } end ## note: this behavior has changed in optimist 2.0! def test_flag_parameters @p.opt :defaultnone, "desc" @p.opt :defaultfalse, "desc", :default => false @p.opt :defaulttrue, "desc", :default => true ## default state opts = @p.parse [] assert_equal false, opts[:defaultnone] assert_equal false, opts[:defaultfalse] assert_equal true, opts[:defaulttrue] ## specifying turns them on, regardless of default opts = @p.parse %w(--defaultfalse --defaulttrue --defaultnone) assert_equal true, opts[:defaultnone] assert_equal true, opts[:defaultfalse] assert_equal true, opts[:defaulttrue] ## using --no- form turns them off, regardless of default opts = @p.parse %w(--no-defaultfalse --no-defaulttrue --no-defaultnone) assert_equal false, opts[:defaultnone] assert_equal false, opts[:defaultfalse] assert_equal false, opts[:defaulttrue] end ## note: this behavior has changed in optimist 2.0! def test_flag_parameters_for_inverted_flags @p.opt :no_default_none, "desc" @p.opt :no_default_false, "desc", :default => false @p.opt :no_default_true, "desc", :default => true ## default state opts = @p.parse [] assert_equal false, opts[:no_default_none] assert_equal false, opts[:no_default_false] assert_equal true, opts[:no_default_true] ## specifying turns them all on, regardless of default opts = @p.parse %w(--no-default-false --no-default-true --no-default-none) assert_equal true, opts[:no_default_none] assert_equal true, opts[:no_default_false] assert_equal true, opts[:no_default_true] ## using dropped-no form turns them all off, regardless of default opts = @p.parse %w(--default-false --default-true --default-none) assert_equal false, opts[:no_default_none] assert_equal false, opts[:no_default_false] assert_equal false, opts[:no_default_true] ## disallow double negatives for reasons of sanity preservation assert_raises(CommandlineError) { @p.parse %w(--no-no-default-true) } end def test_short_options_combine @p.opt :arg1, "desc", :short => "a" @p.opt :arg2, "desc", :short => "b" @p.opt :arg3, "desc", :short => "c", :type => :int opts = @p.parse %w(-a -b) assert_equal true, opts[:arg1] assert_equal true, opts[:arg2] assert_equal nil, opts[:arg3] opts = @p.parse %w(-ab) assert_equal true, opts[:arg1] assert_equal true, opts[:arg2] assert_equal nil, opts[:arg3] opts = @p.parse %w(-ac 4 -b) assert_equal true, opts[:arg1] assert_equal true, opts[:arg2] assert_equal 4, opts[:arg3] assert_raises(CommandlineError) { @p.parse %w(-cab 4) } assert_raises(CommandlineError) { @p.parse %w(-cba 4) } end def test_doubledash_ends_option_processing @p.opt :arg1, "desc", :short => "a", :default => 0 @p.opt :arg2, "desc", :short => "b", :default => 0 opts = @p.parse %w(-- -a 3 -b 2) assert_equal opts[:arg1], 0 assert_equal opts[:arg2], 0 assert_equal %w(-a 3 -b 2), @p.leftovers opts = @p.parse %w(-a 3 -- -b 2) assert_equal opts[:arg1], 3 assert_equal opts[:arg2], 0 assert_equal %w(-b 2), @p.leftovers opts = @p.parse %w(-a 3 -b 2 --) assert_equal opts[:arg1], 3 assert_equal opts[:arg2], 2 assert_equal %w(), @p.leftovers end def test_wrap assert_equal [""], @p.wrap("") assert_equal ["a"], @p.wrap("a") assert_equal ["one two", "three"], @p.wrap("one two three", :width => 8) assert_equal ["one two three"], @p.wrap("one two three", :width => 80) assert_equal ["one", "two", "three"], @p.wrap("one two three", :width => 3) assert_equal ["onetwothree"], @p.wrap("onetwothree", :width => 3) assert_equal [ "Test is an awesome program that does something very, very important.", "", "Usage:", " test [options] +", "where [options] are:"], @p.wrap(< 100) Test is an awesome program that does something very, very important. Usage: test [options] + where [options] are: EOM end def test_multi_line_description out = StringIO.new @p.opt :arg, <<-EOM, :type => :int This is an arg with a multi-line description EOM @p.educate(out) assert_equal <<-EOM, out.string Options: --arg= This is an arg with a multi-line description EOM end def test_integer_formatting @p.opt :arg, "desc", :type => :integer, :short => "i" opts = @p.parse %w(-i 5) assert_equal 5, opts[:arg] end def test_integer_formatting @p.opt :arg, "desc", :type => :integer, :short => "i", :default => 3 opts = @p.parse %w(-i) assert_equal 3, opts[:arg] end def test_floating_point_formatting @p.opt :arg, "desc", :type => :float, :short => "f" opts = @p.parse %w(-f 1) assert_equal 1.0, opts[:arg] opts = @p.parse %w(-f 1.0) assert_equal 1.0, opts[:arg] opts = @p.parse %w(-f 0.1) assert_equal 0.1, opts[:arg] opts = @p.parse %w(-f .1) assert_equal 0.1, opts[:arg] opts = @p.parse %w(-f .99999999999999999999) assert_equal 1.0, opts[:arg] opts = @p.parse %w(-f -1) assert_equal(-1.0, opts[:arg]) opts = @p.parse %w(-f -1.0) assert_equal(-1.0, opts[:arg]) opts = @p.parse %w(-f -0.1) assert_equal(-0.1, opts[:arg]) opts = @p.parse %w(-f -.1) assert_equal(-0.1, opts[:arg]) assert_raises(CommandlineError) { @p.parse %w(-f a) } assert_raises(CommandlineError) { @p.parse %w(-f 1a) } assert_raises(CommandlineError) { @p.parse %w(-f 1.a) } assert_raises(CommandlineError) { @p.parse %w(-f a.1) } assert_raises(CommandlineError) { @p.parse %w(-f 1.0.0) } assert_raises(CommandlineError) { @p.parse %w(-f .) } assert_raises(CommandlineError) { @p.parse %w(-f -.) } end def test_floating_point_formatting_default @p.opt :arg, "desc", :type => :float, :short => "f", :default => 5.5 opts = @p.parse %w(-f) assert_equal 5.5, opts[:arg] end def test_date_formatting @p.opt :arg, "desc", :type => :date, :short => 'd' opts = @p.parse(['-d', 'Jan 4, 2007']) assert_equal Date.civil(2007, 1, 4), opts[:arg] opts = @p.parse(['-d', 'today']) assert_equal Date.today, opts[:arg] end def test_short_options_cant_be_numeric assert_raises(ArgumentError) { @p.opt :arg, "desc", :short => "-1" } @p.opt :a1b, "desc" @p.opt :a2b, "desc" assert @p.specs[:a2b].short.to_i == 0 end def test_short_options_can_be_weird @p.opt :arg1, "desc", :short => "#" @p.opt :arg2, "desc", :short => "." assert_raises(ArgumentError) { @p.opt :arg3, "desc", :short => "-" } end def test_options_cant_be_set_multiple_times_if_not_specified @p.opt :arg, "desc", :short => "-x" @p.parse %w(-x) assert_raises(CommandlineError) { @p.parse %w(-x -x) } assert_raises(CommandlineError) { @p.parse %w(-xx) } end def test_options_can_be_set_multiple_times_if_specified @p.opt :arg, "desc", :short => "-x", :multi => true @p.parse %w(-x) @p.parse %w(-x -x) @p.parse %w(-xx) end def test_short_options_with_multiple_options @p.opt :xarg, "desc", :short => "-x", :type => String, :multi => true opts = @p.parse %w(-x a -x b) assert_equal %w(a b), opts[:xarg] assert_equal [], @p.leftovers end def test_short_options_with_multiple_options_does_not_affect_flags_type @p.opt :xarg, "desc", :short => "-x", :type => :flag, :multi => true opts = @p.parse %w(-x a) assert_equal true, opts[:xarg] assert_equal %w(a), @p.leftovers opts = @p.parse %w(-x a -x b) assert_equal true, opts[:xarg] assert_equal %w(a b), @p.leftovers opts = @p.parse %w(-xx a -x b) assert_equal true, opts[:xarg] assert_equal %w(a b), @p.leftovers end def test_short_options_with_multiple_arguments @p.opt :xarg, "desc", :type => :ints opts = @p.parse %w(-x 3 4 0) assert_equal [3, 4, 0], opts[:xarg] assert_equal [], @p.leftovers @p.opt :yarg, "desc", :type => :floats opts = @p.parse %w(-y 3.14 4.21 0.66) assert_equal [3.14, 4.21, 0.66], opts[:yarg] assert_equal [], @p.leftovers @p.opt :zarg, "desc", :type => :strings opts = @p.parse %w(-z a b c) assert_equal %w(a b c), opts[:zarg] assert_equal [], @p.leftovers end def test_short_options_with_multiple_options_and_arguments @p.opt :xarg, "desc", :type => :ints, :multi => true opts = @p.parse %w(-x 3 4 5 -x 6 7) assert_equal [[3, 4, 5], [6, 7]], opts[:xarg] assert_equal [], @p.leftovers @p.opt :yarg, "desc", :type => :floats, :multi => true opts = @p.parse %w(-y 3.14 4.21 5.66 -y 6.99 7.01) assert_equal [[3.14, 4.21, 5.66], [6.99, 7.01]], opts[:yarg] assert_equal [], @p.leftovers @p.opt :zarg, "desc", :type => :strings, :multi => true opts = @p.parse %w(-z a b c -z d e) assert_equal [%w(a b c), %w(d e)], opts[:zarg] assert_equal [], @p.leftovers end def test_combined_short_options_with_multiple_arguments @p.opt :arg1, "desc", :short => "a" @p.opt :arg2, "desc", :short => "b" @p.opt :arg3, "desc", :short => "c", :type => :ints @p.opt :arg4, "desc", :short => "d", :type => :floats opts = @p.parse %w(-abc 4 6 9) assert_equal true, opts[:arg1] assert_equal true, opts[:arg2] assert_equal [4, 6, 9], opts[:arg3] opts = @p.parse %w(-ac 4 6 9 -bd 3.14 2.41) assert_equal true, opts[:arg1] assert_equal true, opts[:arg2] assert_equal [4, 6, 9], opts[:arg3] assert_equal [3.14, 2.41], opts[:arg4] assert_raises(CommandlineError) { opts = @p.parse %w(-abcd 3.14 2.41) } end def test_long_options_with_multiple_options @p.opt :xarg, "desc", :type => String, :multi => true opts = @p.parse %w(--xarg=a --xarg=b) assert_equal %w(a b), opts[:xarg] assert_equal [], @p.leftovers opts = @p.parse %w(--xarg a --xarg b) assert_equal %w(a b), opts[:xarg] assert_equal [], @p.leftovers end def test_long_options_with_multiple_arguments @p.opt :xarg, "desc", :type => :ints opts = @p.parse %w(--xarg 3 2 5) assert_equal [3, 2, 5], opts[:xarg] assert_equal [], @p.leftovers opts = @p.parse %w(--xarg=3) assert_equal [3], opts[:xarg] assert_equal [], @p.leftovers @p.opt :yarg, "desc", :type => :floats opts = @p.parse %w(--yarg 3.14 2.41 5.66) assert_equal [3.14, 2.41, 5.66], opts[:yarg] assert_equal [], @p.leftovers opts = @p.parse %w(--yarg=3.14) assert_equal [3.14], opts[:yarg] assert_equal [], @p.leftovers @p.opt :zarg, "desc", :type => :strings opts = @p.parse %w(--zarg a b c) assert_equal %w(a b c), opts[:zarg] assert_equal [], @p.leftovers opts = @p.parse %w(--zarg=a) assert_equal %w(a), opts[:zarg] assert_equal [], @p.leftovers end def test_long_options_with_multiple_options_and_arguments @p.opt :xarg, "desc", :type => :ints, :multi => true opts = @p.parse %w(--xarg 3 2 5 --xarg 2 1) assert_equal [[3, 2, 5], [2, 1]], opts[:xarg] assert_equal [], @p.leftovers opts = @p.parse %w(--xarg=3 --xarg=2) assert_equal [[3], [2]], opts[:xarg] assert_equal [], @p.leftovers @p.opt :yarg, "desc", :type => :floats, :multi => true opts = @p.parse %w(--yarg 3.14 2.72 5 --yarg 2.41 1.41) assert_equal [[3.14, 2.72, 5], [2.41, 1.41]], opts[:yarg] assert_equal [], @p.leftovers opts = @p.parse %w(--yarg=3.14 --yarg=2.41) assert_equal [[3.14], [2.41]], opts[:yarg] assert_equal [], @p.leftovers @p.opt :zarg, "desc", :type => :strings, :multi => true opts = @p.parse %w(--zarg a b c --zarg d e) assert_equal [%w(a b c), %w(d e)], opts[:zarg] assert_equal [], @p.leftovers opts = @p.parse %w(--zarg=a --zarg=d) assert_equal [%w(a), %w(d)], opts[:zarg] assert_equal [], @p.leftovers end def test_long_options_also_take_equals @p.opt :arg, "desc", :long => "arg", :type => String, :default => "hello" opts = @p.parse %w() assert_equal "hello", opts[:arg] opts = @p.parse %w(--arg goat) assert_equal "goat", opts[:arg] opts = @p.parse %w(--arg=goat) assert_equal "goat", opts[:arg] ## actually, this next one is valid. empty string for --arg, and goat as a ## leftover. ## assert_raises(CommandlineError) { opts = @p.parse %w(--arg= goat) } end def test_auto_generated_long_names_convert_underscores_to_hyphens @p.opt :hello_there assert_equal "hello-there", @p.specs[:hello_there].long end def test_arguments_passed_through_block @goat = 3 boat = 4 Parser.new(@goat) do |goat| boat = goat end assert_equal @goat, boat end def test_version_and_help_override_errors @p.opt :asdf, "desc", :type => String @p.version "version" @p.parse %w(--asdf goat) assert_raises(CommandlineError) { @p.parse %w(--asdf) } assert_raises(HelpNeeded) { @p.parse %w(--asdf --help) } assert_raises(VersionNeeded) { @p.parse %w(--asdf --version) } end def test_conflicts @p.opt :one assert_raises(ArgumentError) { @p.conflicts :one, :two } @p.opt :two @p.conflicts :one, :two @p.parse %w(--one) @p.parse %w(--two) assert_raises(CommandlineError) { @p.parse %w(--one --two) } @p.opt :hello @p.opt :yellow @p.opt :mellow @p.opt :jello @p.conflicts :hello, :yellow, :mellow, :jello assert_raises(CommandlineError) { @p.parse %w(--hello --yellow --mellow --jello) } assert_raises(CommandlineError) { @p.parse %w(--hello --mellow --jello) } assert_raises(CommandlineError) { @p.parse %w(--hello --jello) } @p.parse %w(--hello) @p.parse %w(--jello) @p.parse %w(--yellow) @p.parse %w(--mellow) @p.parse %w(--mellow --one) @p.parse %w(--mellow --two) assert_raises(CommandlineError) { @p.parse %w(--mellow --two --jello) } assert_raises(CommandlineError) { @p.parse %w(--one --mellow --two --jello) } end def test_conflict_error_messages @p.opt :one @p.opt "two" @p.conflicts :one, "two" assert_raises(CommandlineError, /--one.*--two/) { @p.parse %w(--one --two) } end def test_depends @p.opt :one assert_raises(ArgumentError) { @p.depends :one, :two } @p.opt :two @p.depends :one, :two @p.parse %w(--one --two) assert_raises(CommandlineError) { @p.parse %w(--one) } assert_raises(CommandlineError) { @p.parse %w(--two) } @p.opt :hello @p.opt :yellow @p.opt :mellow @p.opt :jello @p.depends :hello, :yellow, :mellow, :jello @p.parse %w(--hello --yellow --mellow --jello) assert_raises(CommandlineError) { @p.parse %w(--hello --mellow --jello) } assert_raises(CommandlineError) { @p.parse %w(--hello --jello) } assert_raises(CommandlineError) { @p.parse %w(--hello) } assert_raises(CommandlineError) { @p.parse %w(--mellow) } @p.parse %w(--hello --yellow --mellow --jello --one --two) @p.parse %w(--hello --yellow --mellow --jello --one --two a b c) assert_raises(CommandlineError) { @p.parse %w(--mellow --two --jello --one) } end def test_depend_error_messages @p.opt :one @p.opt "two" @p.depends :one, "two" @p.parse %w(--one --two) assert_raises(CommandlineError, /--one.*--two/) { @p.parse %w(--one) } assert_raises(CommandlineError, /--one.*--two/) { @p.parse %w(--two) } end ## courtesy neill zero def test_two_required_one_missing_accuses_correctly @p.opt "arg1", "desc1", :required => true @p.opt "arg2", "desc2", :required => true assert_raises(CommandlineError, /arg2/) { @p.parse(%w(--arg1)) } assert_raises(CommandlineError, /arg1/) { @p.parse(%w(--arg2)) } @p.parse(%w(--arg1 --arg2)) end def test_stopwords_mixed @p.opt "arg1", :default => false @p.opt "arg2", :default => false @p.stop_on %w(happy sad) opts = @p.parse %w(--arg1 happy --arg2) assert_equal true, opts["arg1"] assert_equal false, opts["arg2"] ## restart parsing @p.leftovers.shift opts = @p.parse @p.leftovers assert_equal false, opts["arg1"] assert_equal true, opts["arg2"] end def test_stopwords_no_stopwords @p.opt "arg1", :default => false @p.opt "arg2", :default => false @p.stop_on %w(happy sad) opts = @p.parse %w(--arg1 --arg2) assert_equal true, opts["arg1"] assert_equal true, opts["arg2"] ## restart parsing @p.leftovers.shift opts = @p.parse @p.leftovers assert_equal false, opts["arg1"] assert_equal false, opts["arg2"] end def test_stopwords_multiple_stopwords @p.opt "arg1", :default => false @p.opt "arg2", :default => false @p.stop_on %w(happy sad) opts = @p.parse %w(happy sad --arg1 --arg2) assert_equal false, opts["arg1"] assert_equal false, opts["arg2"] ## restart parsing @p.leftovers.shift opts = @p.parse @p.leftovers assert_equal false, opts["arg1"] assert_equal false, opts["arg2"] ## restart parsing again @p.leftovers.shift opts = @p.parse @p.leftovers assert_equal true, opts["arg1"] assert_equal true, opts["arg2"] end def test_stopwords_with_short_args @p.opt :global_option, "This is a global option", :short => "-g" @p.stop_on %w(sub-command-1 sub-command-2) global_opts = @p.parse %w(-g sub-command-1 -c) cmd = @p.leftovers.shift @q = Parser.new @q.opt :cmd_option, "This is an option only for the subcommand", :short => "-c" cmd_opts = @q.parse @p.leftovers assert_equal true, global_opts[:global_option] assert_nil global_opts[:cmd_option] assert_equal true, cmd_opts[:cmd_option] assert_nil cmd_opts[:global_option] assert_equal cmd, "sub-command-1" assert_equal @q.leftovers, [] end def test_unknown_subcommand @p.opt :global_flag, "Global flag", :short => "-g", :type => :flag @p.opt :global_param, "Global parameter", :short => "-p", :default => 5 @p.stop_on_unknown expected_opts = { :global_flag => true, :help => false, :global_param => 5, :global_flag_given => true } expected_leftovers = [ "my_subcommand", "-c" ] assert_parses_correctly @p, %w(--global-flag my_subcommand -c), \ expected_opts, expected_leftovers assert_parses_correctly @p, %w(-g my_subcommand -c), \ expected_opts, expected_leftovers expected_opts = { :global_flag => false, :help => false, :global_param => 5, :global_param_given => true } expected_leftovers = [ "my_subcommand", "-c" ] assert_parses_correctly @p, %w(-p 5 my_subcommand -c), \ expected_opts, expected_leftovers assert_parses_correctly @p, %w(--global-param 5 my_subcommand -c), \ expected_opts, expected_leftovers end def test_alternate_args args = %w(-a -b -c) opts = ::Optimist.options(args) do opt :alpher, "Ralph Alpher", :short => "-a" opt :bethe, "Hans Bethe", :short => "-b" opt :gamow, "George Gamow", :short => "-c" end physicists_with_humor = [:alpher, :bethe, :gamow] physicists_with_humor.each do |physicist| assert_equal true, opts[physicist] end end def test_date_arg_type temp = Date.new @p.opt :arg, 'desc', :type => :date @p.opt :arg2, 'desc', :type => Date @p.opt :arg3, 'desc', :default => temp opts = @p.parse [] assert_equal temp, opts[:arg3] opts = @p.parse %w(--arg 5/1/2010) assert_kind_of Date, opts[:arg] assert_equal Date.new(2010, 5, 1), opts[:arg] opts = @p.parse %w(--arg2 5/1/2010) assert_kind_of Date, opts[:arg2] assert_equal Date.new(2010, 5, 1), opts[:arg2] opts = @p.parse %w(--arg3) assert_equal temp, opts[:arg3] end def test_unknown_arg_class_type assert_raises ArgumentError do @p.opt :arg, 'desc', :type => Hash end end def test_io_arg_type @p.opt :arg, "desc", :type => :io @p.opt :arg2, "desc", :type => IO @p.opt :arg3, "desc", :default => $stdout opts = @p.parse [] assert_equal $stdout, opts[:arg3] opts = @p.parse %w(--arg /dev/null) assert_kind_of File, opts[:arg] assert_equal "/dev/null", opts[:arg].path #TODO: move to mocks #opts = @p.parse %w(--arg2 http://google.com/) #assert_kind_of StringIO, opts[:arg2] opts = @p.parse %w(--arg3 stdin) assert_equal $stdin, opts[:arg3] assert_raises(CommandlineError) { opts = @p.parse %w(--arg /fdasfasef/fessafef/asdfasdfa/fesasf) } end def test_openstruct_style_access @p.opt "arg1", "desc", :type => :int @p.opt :arg2, "desc", :type => :int opts = @p.parse(%w(--arg1 3 --arg2 4)) opts.arg1 opts.arg2 assert_equal 3, opts.arg1 assert_equal 4, opts.arg2 end def test_multi_args_autobox_defaults @p.opt :arg1, "desc", :default => "hello", :multi => true @p.opt :arg2, "desc", :default => ["hello"], :multi => true opts = @p.parse [] assert_equal ["hello"], opts[:arg1] assert_equal ["hello"], opts[:arg2] opts = @p.parse %w(--arg1 hello) assert_equal ["hello"], opts[:arg1] assert_equal ["hello"], opts[:arg2] opts = @p.parse %w(--arg1 hello --arg1 there) assert_equal ["hello", "there"], opts[:arg1] end def test_ambigious_multi_plus_array_default_resolved_as_specified_by_documentation @p.opt :arg1, "desc", :default => ["potato"], :multi => true @p.opt :arg2, "desc", :default => ["potato"], :multi => true, :type => :strings @p.opt :arg3, "desc", :default => ["potato"] @p.opt :arg4, "desc", :default => ["potato", "rhubarb"], :short => :none, :multi => true ## arg1 should be multi-occurring but not multi-valued opts = @p.parse %w(--arg1 one two) assert_equal ["one"], opts[:arg1] assert_equal ["two"], @p.leftovers opts = @p.parse %w(--arg1 one --arg1 two) assert_equal ["one", "two"], opts[:arg1] assert_equal [], @p.leftovers ## arg2 should be multi-valued and multi-occurring opts = @p.parse %w(--arg2 one two) assert_equal [["one", "two"]], opts[:arg2] assert_equal [], @p.leftovers ## arg3 should be multi-valued but not multi-occurring opts = @p.parse %w(--arg3 one two) assert_equal ["one", "two"], opts[:arg3] assert_equal [], @p.leftovers ## arg4 should be multi-valued but not multi-occurring opts = @p.parse %w() assert_equal ["potato", "rhubarb"], opts[:arg4] end def test_given_keys @p.opt :arg1 @p.opt :arg2 opts = @p.parse %w(--arg1) assert opts[:arg1_given] assert !opts[:arg2_given] opts = @p.parse %w(--arg2) assert !opts[:arg1_given] assert opts[:arg2_given] opts = @p.parse [] assert !opts[:arg1_given] assert !opts[:arg2_given] opts = @p.parse %w(--arg1 --arg2) assert opts[:arg1_given] assert opts[:arg2_given] end def test_default_shorts_assigned_only_after_user_shorts @p.opt :aab, "aaa" # should be assigned to -b @p.opt :ccd, "bbb" # should be assigned to -d @p.opt :user1, "user1", :short => 'a' @p.opt :user2, "user2", :short => 'c' opts = @p.parse %w(-a -b) assert opts[:user1] assert !opts[:user2] assert opts[:aab] assert !opts[:ccd] opts = @p.parse %w(-c -d) assert !opts[:user1] assert opts[:user2] assert !opts[:aab] assert opts[:ccd] end def test_accepts_arguments_with_spaces @p.opt :arg1, "arg", :type => String @p.opt :arg2, "arg2", :type => String opts = @p.parse ["--arg1", "hello there", "--arg2=hello there"] assert_equal "hello there", opts[:arg1] assert_equal "hello there", opts[:arg2] assert_equal 0, @p.leftovers.size end def test_multi_args_default_to_empty_array @p.opt :arg1, "arg", :multi => true opts = @p.parse [] assert_equal [], opts[:arg1] end def test_simple_interface_handles_help assert_stdout(/Options:/) do assert_raises(SystemExit) do ::Optimist::options(%w(-h)) do opt :potato end end end # ensure regular status is returned assert_stdout do begin ::Optimist::options(%w(-h)) do opt :potato end rescue SystemExit => e assert_equal 0, e.status end end end def test_simple_interface_handles_version assert_stdout(/1.2/) do assert_raises(SystemExit) do ::Optimist::options(%w(-v)) do version "1.2" opt :potato end end end end def test_simple_interface_handles_regular_usage opts = ::Optimist::options(%w(--potato)) do opt :potato end assert opts[:potato] end def test_simple_interface_handles_die assert_stderr do ::Optimist::options(%w(--potato)) do opt :potato end assert_raises(SystemExit) { ::Optimist::die :potato, "is invalid" } end end def test_simple_interface_handles_die_without_message assert_stderr(/Error:/) do ::Optimist::options(%w(--potato)) do opt :potato end assert_raises(SystemExit) { ::Optimist::die :potato } end end def test_invalid_option_with_simple_interface assert_stderr do assert_raises(SystemExit) do ::Optimist.options(%w(--potato)) end end assert_stderr do begin ::Optimist.options(%w(--potato)) rescue SystemExit => e assert_equal(-1, e.status) end end end def test_supports_callback_inline assert_raises(RuntimeError, "good") do @p.opt :cb1 do |vals| raise "good" end @p.parse(%w(--cb1)) end end def test_supports_callback_param assert_raises(RuntimeError, "good") do @p.opt :cb1, "with callback", :callback => lambda { |vals| raise "good" } @p.parse(%w(--cb1)) end end def test_ignore_invalid_options @p.opt :arg1, "desc", :type => String @p.opt :b, "desc", :type => String @p.opt :c, "desc", :type => :flag @p.opt :d, "desc", :type => :flag @p.ignore_invalid_options = true opts = @p.parse %w{unknown -S param --arg1 potato -fb daisy --foo -ecg --bar baz -z} assert_equal "potato", opts[:arg1] assert_equal "daisy", opts[:b] assert opts[:c] refute opts[:d] assert_equal %w{unknown -S param -f --foo -eg --bar baz -z}, @p.leftovers end def test_ignore_invalid_options_stop_on_unknown_long @p.opt :arg1, "desc", :type => String @p.ignore_invalid_options = true @p.stop_on_unknown opts = @p.parse %w{--unk --arg1 potato} refute opts[:arg1] assert_equal %w{--unk --arg1 potato}, @p.leftovers end def test_ignore_invalid_options_stop_on_unknown_short @p.opt :arg1, "desc", :type => String @p.ignore_invalid_options = true @p.stop_on_unknown opts = @p.parse %w{-ua potato} refute opts[:arg1] assert_equal %w{-ua potato}, @p.leftovers end def test_ignore_invalid_options_stop_on_unknown_partial_end_short @p.opt :arg1, "desc", :type => :flag @p.ignore_invalid_options = true @p.stop_on_unknown opts = @p.parse %w{-au potato} assert opts[:arg1] assert_equal %w{-u potato}, @p.leftovers end def test_ignore_invalid_options_stop_on_unknown_partial_mid_short @p.opt :arg1, "desc", :type => :flag @p.ignore_invalid_options = true @p.stop_on_unknown opts = @p.parse %w{-abu potato} assert opts[:arg1] assert_equal %w{-bu potato}, @p.leftovers end end end optimist-3.0.0/test/optimist/parser_parse_test.rb0000644000175000017500000000361113616055261022215 0ustar utkarshutkarshrequire 'stringio' require 'test_helper' module Optimist class ParserParseTest < ::MiniTest::Test # TODO: parse # resolve_default_short_options! # parse_date_parameter # parse_integer_parameter(param, arg) # parse_float_parameter(param, arg) # parse_io_parameter(param, arg) # each_arg # collect_argument_parameters def test_help_needed parser.opt "arg" assert_raises(HelpNeeded) { parser.parse %w(-h) } assert_raises(HelpNeeded) { parser.parse %w(--help) } end def test_help_overridden parser.opt :arg1, "desc", :long => "help" assert parser.parse(%w(-h))[:arg1] assert parser.parse(%w(--help))[:arg1] end def test_help_with_other_args parser.opt :arg1 assert_raises(HelpNeeded) { @p.parse %w(--arg1 --help) } end def test_help_with_arg_error parser.opt :arg1, :type => String assert_raises(HelpNeeded) { @p.parse %w(--arg1 --help) } end def test_version_needed_unset parser.opt "arg" assert_raises(CommandlineError) { parser.parse %w(-v) } end def test_version_needed parser.version "optimist 5.2.3" assert_raises(VersionNeeded) { parser.parse %w(-v) } assert_raises(VersionNeeded) { parser.parse %w(--version) } end def test_version_overridden parser.opt "version" assert parser.parse(%w(-v))["version"] assert parser.parse(%w(-v))[:version_given] end def test_version_only_appears_if_set parser.opt "arg" assert_raises(CommandlineError) { parser.parse %w(-v) } end def test_version_with_other_args parser.opt :arg1 parser.version "1.1" assert_raises(VersionNeeded) { parser.parse %w(--arg1 --version) } end def test_version_with_arg_error parser.opt :arg1, :type => String parser.version "1.1" assert_raises(VersionNeeded) { parser.parse %w(--arg1 --version) } end private def parser @p ||= Parser.new end end end optimist-3.0.0/test/optimist/parser_educate_test.rb0000644000175000017500000001041313616055261022513 0ustar utkarshutkarshrequire 'stringio' require 'test_helper' module Optimist class ParserEduateTest < ::MiniTest::Test def setup end def test_no_arguments_to_stdout assert_stdout(/Options:/) do parser.educate end end def test_argument_to_stringio assert_educates(/Options:/) end def test_no_headers assert_educates(/^Options:/) end def test_usage parser.usage("usage string") assert_educates(/^Usage: \w* usage string\n\nOptions:/) end def test_usage_synopsis_version end # def test_banner # def test_text # width, legacy_width # wrap # wrap_lines ############ # convert these into multiple tests # pulled out of optimist_test for now def test_help_has_default_banner @p = Parser.new sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /options/i assert_equal 2, help.length # options, then -h @p = Parser.new @p.version "my version" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /my version/i assert_equal 4, help.length # version, options, -h, -v @p = Parser.new @p.banner "my own banner" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /my own banner/i assert_equal 2, help.length # banner, -h @p = Parser.new @p.text "my own text banner" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /my own text banner/i assert_equal 2, help.length # banner, -h end def test_help_has_optional_usage @p = Parser.new @p.usage "OPTIONS FILES" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /OPTIONS FILES/i assert_equal 4, help.length # line break, options, then -h end def test_help_has_optional_synopsis @p = Parser.new @p.synopsis "About this program" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /About this program/i assert_equal 4, help.length # line break, options, then -h end def test_help_has_specific_order_for_usage_and_synopsis @p = Parser.new @p.usage "OPTIONS FILES" @p.synopsis "About this program" sio = StringIO.new "w" @p.parse [] @p.educate sio help = sio.string.split "\n" assert help[0] =~ /OPTIONS FILES/i assert help[1] =~ /About this program/i assert_equal 5, help.length # line break, options, then -h end def test_help_preserves_positions parser.opt :zzz, "zzz" parser.opt :aaa, "aaa" sio = StringIO.new "w" parser.educate sio help = sio.string.split "\n" assert help[1] =~ /zzz/ assert help[2] =~ /aaa/ end def test_help_includes_option_types parser.opt :arg1, 'arg', :type => :int parser.opt :arg2, 'arg', :type => :ints parser.opt :arg3, 'arg', :type => :string parser.opt :arg4, 'arg', :type => :strings parser.opt :arg5, 'arg', :type => :float parser.opt :arg6, 'arg', :type => :floats parser.opt :arg7, 'arg', :type => :io parser.opt :arg8, 'arg', :type => :ios parser.opt :arg9, 'arg', :type => :date parser.opt :arg10, 'arg', :type => :dates sio = StringIO.new "w" parser.educate sio help = sio.string.split "\n" assert help[1] =~ // assert help[2] =~ // assert help[3] =~ // assert help[4] =~ // assert help[5] =~ // assert help[6] =~ // assert help[7] =~ // assert help[8] =~ // assert help[9] =~ // assert help[10] =~ // end def test_help_has_grammatical_default_text parser.opt :arg1, 'description with period.', :default => 'hello' parser.opt :arg2, 'description without period', :default => 'world' sio = StringIO.new 'w' parser.educate sio help = sio.string.split "\n" assert help[1] =~ /Default/ assert help[2] =~ /default/ end ############ private def parser @p ||= Parser.new end def assert_educates(output) str = StringIO.new parser.educate str assert_match output, str.string end end end optimist-3.0.0/test/optimist/parser_opt_test.rb0000644000175000017500000000025313616055261021704 0ustar utkarshutkarshrequire 'stringio' require 'test_helper' module Optimist class ParserOptTest < ::MiniTest::Test private def parser @p ||= Parser.new end end end optimist-3.0.0/test/optimist/command_line_error_test.rb0000644000175000017500000000076513616055261023374 0ustar utkarshutkarshrequire 'test_helper' module Optimist class CommandlineErrorTest < ::MiniTest::Test def test_class assert_kind_of Exception, cle("message") end def test_message assert "message", cle("message").message end def test_error_code_default assert_nil cle("message").error_code end def test_error_code_custom assert_equal(-3, cle("message", -3).error_code) end private def cle(*args) CommandlineError.new(*args) end end end optimist-3.0.0/test/optimist/version_needed_test.rb0000644000175000017500000000047213616055261022522 0ustar utkarshutkarshrequire 'test_helper' module Optimist class VersionNeededTest < ::MiniTest::Test def test_class assert_kind_of Exception, vn("message") end def test_message assert "message", vn("message").message end private def vn(*args) VersionNeeded.new(*args) end end end optimist-3.0.0/test/support/0000755000175000017500000000000013616055261016006 5ustar utkarshutkarshoptimist-3.0.0/test/support/assert_helpers.rb0000644000175000017500000000233113616055261021355 0ustar utkarshutkarshmodule Minitest::Assertions def assert_parses_correctly(parser, commandline, expected_opts, expected_leftovers) opts = parser.parse commandline assert_equal expected_opts, opts assert_equal expected_leftovers, parser.leftovers end def assert_stderr(str = nil, msg = nil) msg = "#{msg}.\n" if msg old_stderr, $stderr = $stderr, StringIO.new('') yield assert_match str, $stderr.string, msg if str ensure $stderr = old_stderr end def assert_stdout(str = nil, msg = nil) msg = "#{msg}.\n" if msg old_stdout, $stdout = $stdout, StringIO.new('') yield assert_match str, $stdout.string, msg if str ensure $stdout = old_stdout end # like assert raises, but if it does raise, it checks status # NOTE: this does not ensure the exception is raised def assert_system_exit *exp msg = "#{exp.pop}.\n" if String === exp.last status = exp.first begin yield rescue SystemExit => e assert_equal status, e.status { exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not") } if status return true end flunk "#{msg}#{mu_pp(exp)} SystemExit expected but nothing was raised." end end optimist-3.0.0/test/optimist_test.rb0000644000175000017500000001015413616055261017527 0ustar utkarshutkarshrequire 'test_helper' class OptimistTest < MiniTest::Test def setup Optimist.send(:instance_variable_set, "@last_parser", nil) end def parser(&block) Optimist::Parser.new(&block) end def test_options opts = Optimist.options %w(-f) do opt :f end assert_equal true, opts[:f] end def test_options_die_default assert_stderr(/Error: unknown argument.*Try --help/m) do assert_system_exit(-1) do Optimist.options %w(-f) do opt :x end end end end def test_options_die_educate_on_error assert_stderr(/Error: unknown argument.*Options/m) do assert_system_exit(-1) do Optimist.options %w(-f) do opt :x educate_on_error end end end end def test_die_without_options_ever_run assert_raises(ArgumentError) { Optimist.die 'hello' } end def test_die assert_stderr(/Error: issue with parsing/) do assert_system_exit(-1) do Optimist.options [] Optimist.die "issue with parsing" end end end def test_die_custom_error_code assert_stderr(/Error: issue with parsing/) do assert_system_exit(5) do Optimist.options [] Optimist.die "issue with parsing", nil, 5 end end end def test_die_custom_error_code_two_args assert_stderr(/Error: issue with parsing/) do assert_system_exit(5) do Optimist.options [] Optimist.die "issue with parsing", 5 end end end def test_educate_without_options_ever_run assert_raises(ArgumentError) { Optimist.educate } end def test_educate assert_stdout(/Show this message/) do assert_system_exit(0) do Optimist.options [] Optimist.educate end end end def test_with_standard_exception_options p = parser do opt :f end opts = Optimist::with_standard_exception_handling p do p.parse %w(-f) end assert_equal true, opts[:f] end def test_with_standard_exception_version_exception p = parser do version "5.5" end assert_stdout(/5\.5/) do assert_system_exit(0) do Optimist::with_standard_exception_handling p do raise Optimist::VersionNeeded end end end end def test_with_standard_exception_version_flag p = parser do version "5.5" end assert_stdout(/5\.5/) do assert_system_exit(0) do Optimist::with_standard_exception_handling p do p.parse %w(-v) end end end end def test_with_standard_exception_die_exception assert_stderr(/Error: cl error/) do assert_system_exit(-1) do p = parser Optimist.with_standard_exception_handling(p) do raise ::Optimist::CommandlineError.new('cl error') end end end end def test_with_standard_exception_die_exception_custom_error assert_stderr(/Error: cl error/) do assert_system_exit(5) do p = parser Optimist.with_standard_exception_handling(p) do raise ::Optimist::CommandlineError.new('cl error', 5) end end end end def test_with_standard_exception_die assert_stderr(/Error: cl error/) do assert_system_exit(-1) do p = parser Optimist.with_standard_exception_handling(p) do p.die 'cl error' end end end end def test_with_standard_exception_die_custom_error assert_stderr(/Error: cl error/) do assert_system_exit(3) do p = parser Optimist.with_standard_exception_handling(p) do p.die 'cl error', nil, 3 end end end end def test_with_standard_exception_help_needed assert_stdout(/Options/) do assert_system_exit(0) do p = parser Optimist.with_standard_exception_handling(p) do raise Optimist::HelpNeeded end end end end def test_with_standard_exception_help_needed_flag assert_stdout(/Options/) do assert_system_exit(0) do p = parser Optimist.with_standard_exception_handling(p) do p.parse(%w(-h)) end end end end end optimist-3.0.0/.gitignore0000644000175000017500000000017413616055261015305 0ustar utkarshutkarsh/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ *.bundle *.so *.swp *.o *.a mkmf.log optimist-3.0.0/Gemfile0000644000175000017500000000012413616055261014603 0ustar utkarshutkarshsource 'https://rubygems.org' # Specify your gem's dependencies in gemspec gemspec optimist-3.0.0/README.md0000644000175000017500000000415113616055261014573 0ustar utkarshutkarsh# optimist http://manageiq.github.io/optimist/ [![Gem Version](https://badge.fury.io/rb/optimist.svg)](http://badge.fury.io/rb/optimist) [![Build Status](https://travis-ci.org/ManageIQ/optimist.svg)](https://travis-ci.org/ManageIQ/optimist) [![Code Climate](https://codeclimate.com/github/ManageIQ/optimist/badges/gpa.svg)](https://codeclimate.com/github/ManageIQ/optimist) [![Coverage Status](http://img.shields.io/coveralls/ManageIQ/optimist.svg)](https://coveralls.io/r/ManageIQ/optimist) [![Dependency Status](https://gemnasium.com/ManageIQ/optimist.svg)](https://gemnasium.com/ManageIQ/optimist) ## Documentation - Quickstart: See `Optimist.options` and then `Optimist::Parser#opt`. - Examples: http://manageiq.github.io/optimist/. - Wiki: http://github.com/ManageIQ/optimist/wiki ## Description Optimist is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, and sensible defaults for everything you don't specify. ## Features - Dirt-simple usage. - Single file. Throw it in lib/ if you don't want to make it a Rubygem dependency. - Sensible defaults. No tweaking necessary, much tweaking possible. - Support for long options, short options, subcommands, and automatic type validation and conversion. - Automatic help message generation, wrapped to current screen width. ## Requirements * A burning desire to write less code. ## Install * gem install optimist ## Synopsis ```ruby require 'optimist' opts = Optimist::options do opt :monkey, "Use monkey mode" # flag --monkey, default false opt :name, "Monkey name", :type => :string # string --name , default nil opt :num_limbs, "Number of limbs", :default => 4 # integer --num-limbs , default to 4 end p opts # a hash: { :monkey=>false, :name=>nil, :num_limbs=>4, :help=>false } ``` ## License Copyright © 2008-2014 [William Morgan](http://masanjin.net/). Copyright © 2014 Red Hat, Inc. Optimist is released under the [MIT License](http://www.opensource.org/licenses/MIT). optimist-3.0.0/optimist.gemspec0000644000175000017500000000275713616055261016543 0ustar utkarshutkarsh# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'optimist' Gem::Specification.new do |spec| spec.name = "optimist" spec.version = Optimist::VERSION spec.authors = ["William Morgan", "Keenan Brock", "Jason Frey"] spec.email = "keenan@thebrocks.net" spec.summary = "Optimist is a commandline option parser for Ruby that just gets out of your way." spec.description = "Optimist is a commandline option parser for Ruby that just gets out of your way. One line of code per option is all you need to write. For that, you get a nice automatically-generated help page, robust option parsing, command subcompletion, and sensible defaults for everything you don't specify." spec.homepage = "http://manageiq.github.io/optimist/" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.metadata = { "changelog_uri" => "https://github.com/ManageIQ/optimist/blob/master/History.txt", "source_code_uri" => "https://github.com/ManageIQ/optimist/", "bug_tracker_uri" => "https://github.com/ManageIQ/optimist/issues", } spec.require_paths = ["lib"] spec.add_development_dependency "minitest", "~> 5.4.3" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "chronic" end optimist-3.0.0/lib/0000755000175000017500000000000013616055261014061 5ustar utkarshutkarshoptimist-3.0.0/lib/optimist.rb0000644000175000017500000010146213616055261016262 0ustar utkarshutkarsh# lib/optimist.rb -- optimist command-line processing library # Copyright (c) 2008-2014 William Morgan. # Copyright (c) 2014 Red Hat, Inc. # optimist is licensed under the MIT license. require 'date' module Optimist # note: this is duplicated in gemspec # please change over there too VERSION = "3.0.0" ## Thrown by Parser in the event of a commandline error. Not needed if ## you're using the Optimist::options entry. class CommandlineError < StandardError attr_reader :error_code def initialize(msg, error_code = nil) super(msg) @error_code = error_code end end ## Thrown by Parser if the user passes in '-h' or '--help'. Handled ## automatically by Optimist#options. class HelpNeeded < StandardError end ## Thrown by Parser if the user passes in '-v' or '--version'. Handled ## automatically by Optimist#options. class VersionNeeded < StandardError end ## Regex for floating point numbers FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?[\d]+)?$/ ## Regex for parameters PARAM_RE = /^-(-|\.$|[^\d\.])/ ## The commandline parser. In typical usage, the methods in this class ## will be handled internally by Optimist::options. In this case, only the ## #opt, #banner and #version, #depends, and #conflicts methods will ## typically be called. ## ## If you want to instantiate this class yourself (for more complicated ## argument-parsing logic), call #parse to actually produce the output hash, ## and consider calling it from within ## Optimist::with_standard_exception_handling. class Parser ## The registry is a class-instance-variable map of option aliases to their subclassed Option class. @registry = {} ## The Option subclasses are responsible for registering themselves using this function. def self.register(lookup, klass) @registry[lookup.to_sym] = klass end ## Gets the class from the registry. ## Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer def self.registry_getopttype(type) return nil unless type if type.respond_to?(:name) type = type.name lookup = type.downcase.to_sym else lookup = type.to_sym end raise ArgumentError, "Unsupported argument type '#{type}', registry lookup '#{lookup}'" unless @registry.has_key?(lookup) return @registry[lookup].new end INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc: ## The values from the commandline that were not interpreted by #parse. attr_reader :leftovers ## The complete configuration hashes for each option. (Mainly useful ## for testing.) attr_reader :specs ## A flag that determines whether or not to raise an error if the parser is passed one or more ## options that were not registered ahead of time. If 'true', then the parser will simply ## ignore options that it does not recognize. attr_accessor :ignore_invalid_options ## Initializes the parser, and instance-evaluates any block given. def initialize(*a, &b) @version = nil @leftovers = [] @specs = {} @long = {} @short = {} @order = [] @constraints = [] @stop_words = [] @stop_on_unknown = false @educate_on_error = false @synopsis = nil @usage = nil # instance_eval(&b) if b # can't take arguments cloaker(&b).bind(self).call(*a) if b end ## Define an option. +name+ is the option name, a unique identifier ## for the option that you will use internally, which should be a ## symbol or a string. +desc+ is a string description which will be ## displayed in help messages. ## ## Takes the following optional arguments: ## ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s. ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value. ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given. ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Optimist::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+. ## [+:required+] If set to +true+, the argument must be provided on the commandline. ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.) ## ## Note that there are two types of argument multiplicity: an argument ## can take multiple values, e.g. "--arg 1 2 3". An argument can also ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2". ## ## Arguments that take multiple values should have a +:type+ parameter ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+ ## value of an array of the correct type (e.g. [String]). The ## value of this argument will be an array of the parameters on the ## commandline. ## ## Arguments that can occur multiple times should be marked with ## +:multi+ => +true+. The value of this argument will also be an array. ## In contrast with regular non-multi options, if not specified on ## the commandline, the default value will be [], not nil. ## ## These two attributes can be combined (e.g. +:type+ => +:strings+, ## +:multi+ => +true+), in which case the value of the argument will be ## an array of arrays. ## ## There's one ambiguous case to be aware of: when +:multi+: is true and a ## +:default+ is set to an array (of something), it's ambiguous whether this ## is a multi-value argument as well as a multi-occurrence argument. ## In thise case, Optimist assumes that it's not a multi-value argument. ## If you want a multi-value, multi-occurrence argument with a default ## value, you must specify +:type+ as well. def opt(name, desc = "", opts = {}, &b) opts[:callback] ||= b if block_given? opts[:desc] ||= desc o = Option.create(name, desc, opts) raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? o.name raise ArgumentError, "long option name #{o.long.inspect} is already taken; please specify a (different) :long" if @long[o.long] raise ArgumentError, "short option name #{o.short.inspect} is already taken; please specify a (different) :short" if @short[o.short] @long[o.long] = o.name @short[o.short] = o.name if o.short? @specs[o.name] = o @order << [:opt, o.name] end ## Sets the version string. If set, the user can request the version ## on the commandline. Should probably be of the form " ## ". def version(s = nil) s ? @version = s : @version end ## Sets the usage string. If set the message will be printed as the ## first line in the help (educate) output and ending in two new ## lines. def usage(s = nil) s ? @usage = s : @usage end ## Adds a synopsis (command summary description) right below the ## usage line, or as the first line if usage isn't specified. def synopsis(s = nil) s ? @synopsis = s : @synopsis end ## Adds text to the help display. Can be interspersed with calls to ## #opt to build a multi-section help page. def banner(s) @order << [:text, s] end alias_method :text, :banner ## Marks two (or more!) options as requiring each other. Only handles ## undirected (i.e., mutual) dependencies. Directed dependencies are ## better modeled with Optimist::die. def depends(*syms) syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] } @constraints << [:depends, syms] end ## Marks two (or more!) options as conflicting. def conflicts(*syms) syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] } @constraints << [:conflicts, syms] end ## Defines a set of words which cause parsing to terminate when ## encountered, such that any options to the left of the word are ## parsed as usual, and options to the right of the word are left ## intact. ## ## A typical use case would be for subcommand support, where these ## would be set to the list of subcommands. A subsequent Optimist ## invocation would then be used to parse subcommand options, after ## shifting the subcommand off of ARGV. def stop_on(*words) @stop_words = [*words].flatten end ## Similar to #stop_on, but stops on any unknown word when encountered ## (unless it is a parameter for an argument). This is useful for ## cases where you don't know the set of subcommands ahead of time, ## i.e., without first parsing the global options. def stop_on_unknown @stop_on_unknown = true end ## Instead of displaying "Try --help for help." on an error ## display the usage (via educate) def educate_on_error @educate_on_error = true end ## Parses the commandline. Typically called by Optimist::options, ## but you can call it directly if you need more control. ## ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions. def parse(cmdline = ARGV) vals = {} required = {} opt :version, "Print version and exit" if @version && ! (@specs[:version] || @long["version"]) opt :help, "Show this message" unless @specs[:help] || @long["help"] @specs.each do |sym, opts| required[sym] = true if opts.required? vals[sym] = opts.default vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil end resolve_default_short_options! ## resolve symbols given_args = {} @leftovers = each_arg cmdline do |arg, params| ## handle --no- forms arg, negative_given = if arg =~ /^--no-([^-]\S*)$/ ["--#{$1}", true] else [arg, false] end sym = case arg when /^-([^-])$/ then @short[$1] when /^--([^-]\S*)$/ then @long[$1] || @long["no-#{$1}"] else raise CommandlineError, "invalid argument syntax: '#{arg}'" end sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments next nil if ignore_invalid_options && !sym raise CommandlineError, "unknown argument '#{arg}'" unless sym if given_args.include?(sym) && !@specs[sym].multi? raise CommandlineError, "option '#{arg}' specified multiple times" end given_args[sym] ||= {} given_args[sym][:arg] = arg given_args[sym][:negative_given] = negative_given given_args[sym][:params] ||= [] # The block returns the number of parameters taken. num_params_taken = 0 unless params.empty? if @specs[sym].single_arg? given_args[sym][:params] << params[0, 1] # take the first parameter num_params_taken = 1 elsif @specs[sym].multi_arg? given_args[sym][:params] << params # take all the parameters num_params_taken = params.size end end num_params_taken end ## check for version and help args raise VersionNeeded if given_args.include? :version raise HelpNeeded if given_args.include? :help ## check constraint satisfaction @constraints.each do |type, syms| constraint_sym = syms.find { |sym| given_args[sym] } next unless constraint_sym case type when :depends syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}" unless given_args.include? sym } when :conflicts syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}" if given_args.include?(sym) && (sym != constraint_sym) } end end required.each do |sym, val| raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym end ## parse parameters given_args.each do |sym, given_data| arg, params, negative_given = given_data.values_at :arg, :params, :negative_given opts = @specs[sym] if params.empty? && !opts.flag? raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default params << (opts.array_default? ? opts.default.clone : [opts.default]) end vals["#{sym}_given".intern] = true # mark argument as specified on the commandline vals[sym] = opts.parse(params, negative_given) if opts.single_arg? if opts.multi? # multiple options, each with a single parameter vals[sym] = vals[sym].map { |p| p[0] } else # single parameter vals[sym] = vals[sym][0][0] end elsif opts.multi_arg? && !opts.multi? vals[sym] = vals[sym][0] # single option, with multiple parameters end # else: multiple options, with multiple parameters opts.callback.call(vals[sym]) if opts.callback end ## modify input in place with only those ## arguments we didn't process cmdline.clear @leftovers.each { |l| cmdline << l } ## allow openstruct-style accessors class << vals def method_missing(m, *_args) self[m] || self[m.to_s] end end vals end ## Print the help message to +stream+. def educate(stream = $stdout) width # hack: calculate it now; otherwise we have to be careful not to # call this unless the cursor's at the beginning of a line. left = {} @specs.each { |name, spec| left[name] = spec.educate } leftcol_width = left.values.map(&:length).max || 0 rightcol_start = leftcol_width + 6 # spaces unless @order.size > 0 && @order.first.first == :text command_name = File.basename($0).gsub(/\.[^.]+$/, '') stream.puts "Usage: #{command_name} #{@usage}\n" if @usage stream.puts "#{@synopsis}\n" if @synopsis stream.puts if @usage || @synopsis stream.puts "#{@version}\n" if @version stream.puts "Options:" end @order.each do |what, opt| if what == :text stream.puts wrap(opt) next end spec = @specs[opt] stream.printf " %-#{leftcol_width}s ", left[opt] desc = spec.description_with_default stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start) end end def width #:nodoc: @width ||= if $stdout.tty? begin require 'io/console' w = IO.console.winsize.last w.to_i > 0 ? w : 80 rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL legacy_width end else 80 end end def legacy_width # Support for older Rubies where io/console is not available `tput cols`.to_i rescue Errno::ENOENT 80 end private :legacy_width def wrap(str, opts = {}) # :nodoc: if str == "" [""] else inner = false str.split("\n").map do |s| line = wrap_line s, opts.merge(:inner => inner) inner = true line end.flatten end end ## The per-parser version of Optimist::die (see that for documentation). def die(arg, msg = nil, error_code = nil) msg, error_code = nil, msg if msg.kind_of?(Integer) if msg $stderr.puts "Error: argument --#{@specs[arg].long} #{msg}." else $stderr.puts "Error: #{arg}." end if @educate_on_error $stderr.puts educate $stderr else $stderr.puts "Try --help for help." end exit(error_code || -1) end private ## yield successive arg, parameter pairs def each_arg(args) remains = [] i = 0 until i >= args.length return remains += args[i..-1] if @stop_words.member? args[i] case args[i] when /^--$/ # arg terminator return remains += args[(i + 1)..-1] when /^--(\S+?)=(.*)$/ # long argument with equals num_params_taken = yield "--#{$1}", [$2] if num_params_taken.nil? remains << args[i] if @stop_on_unknown return remains += args[i + 1..-1] end end i += 1 when /^--(\S+)$/ # long argument params = collect_argument_parameters(args, i + 1) num_params_taken = yield args[i], params if num_params_taken.nil? remains << args[i] if @stop_on_unknown return remains += args[i + 1..-1] end else i += num_params_taken end i += 1 when /^-(\S+)$/ # one or more short arguments short_remaining = "" shortargs = $1.split(//) shortargs.each_with_index do |a, j| if j == (shortargs.length - 1) params = collect_argument_parameters(args, i + 1) num_params_taken = yield "-#{a}", params unless num_params_taken short_remaining << a if @stop_on_unknown remains << "-#{short_remaining}" return remains += args[i + 1..-1] end else i += num_params_taken end else unless yield "-#{a}", [] short_remaining << a if @stop_on_unknown short_remaining += shortargs[j + 1..-1].join remains << "-#{short_remaining}" return remains += args[i + 1..-1] end end end end unless short_remaining.empty? remains << "-#{short_remaining}" end i += 1 else if @stop_on_unknown return remains += args[i..-1] else remains << args[i] i += 1 end end end remains end def collect_argument_parameters(args, start_at) params = [] pos = start_at while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do params << args[pos] pos += 1 end params end def resolve_default_short_options! @order.each do |type, name| opts = @specs[name] next if type != :opt || opts.short c = opts.long.split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) } if c # found a character to use opts.short = c @short[c] = name end end end def wrap_line(str, opts = {}) prefix = opts[:prefix] || 0 width = opts[:width] || (self.width - 1) start = 0 ret = [] until start > str.length nextt = if start + width >= str.length str.length else x = str.rindex(/\s/, start + width) x = str.index(/\s/, start) if x && x < start x || str.length end ret << ((ret.empty? && !opts[:inner]) ? "" : " " * prefix) + str[start...nextt] start = nextt + 1 end ret end ## instance_eval but with ability to handle block arguments ## thanks to _why: http://redhanded.hobix.com/inspect/aBlockCostume.html def cloaker(&b) (class << self; self; end).class_eval do define_method :cloaker_, &b meth = instance_method :cloaker_ remove_method :cloaker_ meth end end end class Option attr_accessor :name, :short, :long, :default attr_writer :multi_given def initialize @long = nil @short = nil @name = nil @multi_given = false @hidden = false @default = nil @optshash = Hash.new() end def opts (key) @optshash[key] end def opts= (o) @optshash = o end ## Indicates a flag option, which is an option without an argument def flag? ; false ; end def single_arg? !self.multi_arg? && !self.flag? end def multi ; @multi_given ; end alias multi? multi ## Indicates that this is a multivalued (Array type) argument def multi_arg? ; false ; end ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options. def array_default? ; self.default.kind_of?(Array) ; end def short? ; short && short != :none ; end def callback ; opts(:callback) ; end def desc ; opts(:desc) ; end def required? ; opts(:required) ; end def parse (_paramlist, _neg_given) raise NotImplementedError, "parse must be overridden for newly registered type" end # provide type-format string. default to empty, but user should probably override it def type_format ; "" ; end def educate (short? ? "-#{short}, " : "") + "--#{long}" + type_format + (flag? && default ? ", --no-#{long}" : "") end ## Format the educate-line description including the default-value(s) def description_with_default return desc unless default default_s = case default when $stdout then '' when $stdin then '' when $stderr then '' when Array default.join(', ') else default.to_s end defword = desc.end_with?('.') ? 'Default' : 'default' return "#{desc} (#{defword}: #{default_s})" end ## Provide a way to register symbol aliases to the Parser def self.register_alias(*alias_keys) alias_keys.each do |alias_key| # pass in the alias-key and the class Parser.register(alias_key, self) end end ## Factory class methods ... # Determines which type of object to create based on arguments passed # to +Optimist::opt+. This is trickier in Optimist, than other cmdline # parsers (e.g. Slop) because we allow the +default:+ to be able to # set the option's type. def self.create(name, desc="", opts={}, settings={}) opttype = Optimist::Parser.registry_getopttype(opts[:type]) opttype_from_default = get_klass_from_default(opts, opttype) raise ArgumentError, ":type specification and default type don't match (default type is #{opttype_from_default.class})" if opttype && opttype_from_default && (opttype.class != opttype_from_default.class) opt_inst = (opttype || opttype_from_default || Optimist::BooleanOption.new) ## fill in :long opt_inst.long = handle_long_opt(opts[:long], name) ## fill in :short opt_inst.short = handle_short_opt(opts[:short]) ## fill in :multi multi_given = opts[:multi] || false opt_inst.multi_given = multi_given ## fill in :default for flags defvalue = opts[:default] || opt_inst.default ## autobox :default for :multi (multi-occurrence) arguments defvalue = [defvalue] if defvalue && multi_given && !defvalue.kind_of?(Array) opt_inst.default = defvalue opt_inst.name = name opt_inst.opts = opts opt_inst end private def self.get_type_from_disdef(optdef, opttype, disambiguated_default) if disambiguated_default.is_a? Array return(optdef.first.class.name.downcase + "s") if !optdef.empty? if opttype raise ArgumentError, "multiple argument type must be plural" unless opttype.multi_arg? return nil else raise ArgumentError, "multiple argument type cannot be deduced from an empty array" end end return disambiguated_default.class.name.downcase end def self.get_klass_from_default(opts, opttype) ## for options with :multi => true, an array default doesn't imply ## a multi-valued argument. for that you have to specify a :type ## as well. (this is how we disambiguate an ambiguous situation; ## see the docs for Parser#opt for details.) disambiguated_default = if opts[:multi] && opts[:default].is_a?(Array) && opttype.nil? opts[:default].first else opts[:default] end return nil if disambiguated_default.nil? type_from_default = get_type_from_disdef(opts[:default], opttype, disambiguated_default) return Optimist::Parser.registry_getopttype(type_from_default) end def self.handle_long_opt(lopt, name) lopt = lopt ? lopt.to_s : name.to_s.gsub("_", "-") lopt = case lopt when /^--([^-].*)$/ then $1 when /^[^-]/ then lopt else raise ArgumentError, "invalid long option name #{lopt.inspect}" end end def self.handle_short_opt(sopt) sopt = sopt.to_s if sopt && sopt != :none sopt = case sopt when /^-(.)$/ then $1 when nil, :none, /^.$/ then sopt else raise ArgumentError, "invalid short option name '#{sopt.inspect}'" end if sopt raise ArgumentError, "a short option name can't be a number or a dash" if sopt =~ ::Optimist::Parser::INVALID_SHORT_ARG_REGEX end return sopt end end # Flag option. Has no arguments. Can be negated with "no-". class BooleanOption < Option register_alias :flag, :bool, :boolean, :trueclass, :falseclass def initialize super() @default = false end def flag? ; true ; end def parse(_paramlist, neg_given) return(self.name.to_s =~ /^no_/ ? neg_given : !neg_given) end end # Floating point number option class. class FloatOption < Option register_alias :float, :double def type_format ; "=" ; end def parse(paramlist, _neg_given) paramlist.map do |pg| pg.map do |param| raise CommandlineError, "option '#{self.name}' needs a floating-point number" unless param.is_a?(Numeric) || param =~ FLOAT_RE param.to_f end end end end # Integer number option class. class IntegerOption < Option register_alias :int, :integer, :fixnum def type_format ; "=" ; end def parse(paramlist, _neg_given) paramlist.map do |pg| pg.map do |param| raise CommandlineError, "option '#{self.name}' needs an integer" unless param.is_a?(Numeric) || param =~ /^-?[\d_]+$/ param.to_i end end end end # Option class for handling IO objects and URLs. # Note that this will return the file-handle, not the file-name # in the case of file-paths given to it. class IOOption < Option register_alias :io def type_format ; "=" ; end def parse(paramlist, _neg_given) paramlist.map do |pg| pg.map do |param| if param =~ /^(stdin|-)$/i $stdin else require 'open-uri' begin open param rescue SystemCallError => e raise CommandlineError, "file or url for option '#{self.name}' cannot be opened: #{e.message}" end end end end end end # Option class for handling Strings. class StringOption < Option register_alias :string def type_format ; "=" ; end def parse(paramlist, _neg_given) paramlist.map { |pg| pg.map(&:to_s) } end end # Option for dates. Uses Chronic if it exists. class DateOption < Option register_alias :date def type_format ; "=" ; end def parse(paramlist, _neg_given) paramlist.map do |pg| pg.map do |param| next param if param.is_a?(Date) begin begin require 'chronic' time = Chronic.parse(param) rescue LoadError # chronic is not available end time ? Date.new(time.year, time.month, time.day) : Date.parse(param) rescue ArgumentError raise CommandlineError, "option '#{self.name}' needs a date" end end end end end ### MULTI_OPT_TYPES : ## The set of values that indicate a multiple-parameter option (i.e., that ## takes multiple space-separated values on the commandline) when passed as ## the +:type+ parameter of #opt. # Option class for handling multiple Integers class IntegerArrayOption < IntegerOption register_alias :fixnums, :ints, :integers def type_format ; "=" ; end def multi_arg? ; true ; end end # Option class for handling multiple Floats class FloatArrayOption < FloatOption register_alias :doubles, :floats def type_format ; "=" ; end def multi_arg? ; true ; end end # Option class for handling multiple Strings class StringArrayOption < StringOption register_alias :strings def type_format ; "=" ; end def multi_arg? ; true ; end end # Option class for handling multiple dates class DateArrayOption < DateOption register_alias :dates def type_format ; "=" ; end def multi_arg? ; true ; end end # Option class for handling Files/URLs via 'open' class IOArrayOption < IOOption register_alias :ios def type_format ; "=" ; end def multi_arg? ; true ; end end ## The easy, syntactic-sugary entry method into Optimist. Creates a Parser, ## passes the block to it, then parses +args+ with it, handling any errors or ## requests for help or version information appropriately (and then exiting). ## Modifies +args+ in place. Returns a hash of option values. ## ## The block passed in should contain zero or more calls to +opt+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and ## probably a call to +version+ (Parser#version). ## ## The returned block contains a value for every option specified with ## +opt+. The value will be the value given on the commandline, or the ## default value if the option was not specified on the commandline. For ## every option specified on the commandline, a key "