diff options
author | Adam Jacob <adam@hjksolutions.com> | 2009-03-12 17:40:24 -0700 |
---|---|---|
committer | Adam Jacob <adam@hjksolutions.com> | 2009-03-12 17:40:24 -0700 |
commit | e5fdd8ab3aa158c9264ebc3fb5e61df1e99bc874 (patch) | |
tree | 74123d029657c25edfc2fa41412f34218913e04b | |
download | mixlib-cli-e5fdd8ab3aa158c9264ebc3fb5e61df1e99bc874.tar.gz |
Initial commit
-rw-r--r-- | LICENSE | 201 | ||||
-rw-r--r-- | NOTICE | 27 | ||||
-rw-r--r-- | README.rdoc | 75 | ||||
-rw-r--r-- | Rakefile | 60 | ||||
-rw-r--r-- | lib/mixlib/cli.rb | 196 | ||||
-rwxr-xr-x | script/destroy | 14 | ||||
-rwxr-xr-x | script/generate | 14 | ||||
-rw-r--r-- | spec/mixlib/cli_spec.rb | 197 | ||||
-rw-r--r-- | spec/spec_helper.rb | 9 |
9 files changed, 793 insertions, 0 deletions
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. @@ -0,0 +1,27 @@ +Mixin::CLI NOTICE +================= + +Developed at Opscode (http://www.opscode.com). + + * Copyright 2009, Opscode, Inc. <legal@opscode.com> + +Mixin::CLI incorporates code from Chef. The Chef notice file follows: + +Chef NOTICE +=========== + +Developed at Opscode (http://www.opscode.com). + +Contributors and Copyright holders: + + * Copyright 2008, Adam Jacob <adam@opscode.com> + * Copyright 2008, Arjuna Christensen <aj@hjksolutions.com> + * Copyright 2008, Bryan McLellan <btm@loftninjas.org> + * Copyright 2008, Ezra Zygmuntowicz <ezra@engineyard.com> + * Copyright 2009, Sean Cribbs <seancribbs@gmail.com> + * Copyright 2009, Christopher Brown <cb@opscode.com> + * Copyright 2009, Thom May <thom@clearairturbulence.org> + +Chef incorporates code modified from Open4 (http://www.codeforpeople.com/lib/ruby/open4/), which was written by Ara T. Howard. + +Chef incorporates code modified from Merb (http://www.merbivore.com), which is Copyright (c) 2008 Engine Yard. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..6acb9d8 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,75 @@ +== Mixlib::CLI + +Mixlib::CLI provides a class-based command line option parsing object, like the one used in Chef, Ohai and Relish. To use in your project: + + require 'rubygems' + require 'mixlib/cli' + + class MyCLI + include Mixlib::CLI + + option :config_file, + :short => "-c CONFIG", + :long => "--config CONFIG", + :default => 'config.rb', + :description => "The configuration file to use" + + option :log_level, + :short => "-l LEVEL", + :long => "--log_level LEVEL", + :description => "Set the log level (debug, info, warn, error, fatal)", + :required => true, + :proc => Proc.new { |l| l.to_sym } + + option :help, + :short => "-h", + :long => "--help", + :description => "Show this message", + :on => :tail, + :boolean => true, + :show_options => true, + :exit => 0 + + end + + # ARGV = [ '-c', 'foo.rb', '-l', 'debug' ] + cli = MyCLI.new + cli.parse_options + cli.config[:config_file] # 'foo.rb' + cli.config[:log_level] # :debug + +If you are using this in conjunction with Mixlib::Config, you can do something like this (building on the above definition): + + class MyConfig + extend(Mixlib::Config) + + log_level :info + config_file "default.rb" + end + + class MyCLI + def run(argv=ARGV) + parse_options(argv) + MyConfig.merge!(config) + end + end + + c = MyCLI.new + # ARGV = [ '-l', 'debug' ] + c.run + MyConfig[:log_level] # :debug + +Available arguments to 'option': + +:short:: The short option, just like from optparse. Example: "-l LEVEL" +:long:: The long option, just like from optparse. Example: "--level LEVEL" +:description:: The description for this item, just like from optparse. +:default:: A default value for this option +:required:: Prints a message informing the user of the missing requirement, and exits. Default is false. +:on:: Set to :tail to appear at the end, or :head to appear at the top. +:boolean:: If this option takes no arguments, set this to true. +:show_options:: If you want the option list printed when this option is called, set this to true. +:exit:: Exit your program with the exit code when this option is specified. Example: 0 +:proc:: If set, the configuration value will be set to the return value of this proc. + +Have fun! diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..058ed05 --- /dev/null +++ b/Rakefile @@ -0,0 +1,60 @@ +require 'rubygems' +require 'rake/gempackagetask' +require 'rubygems/specification' +require 'date' +require 'spec/rake/spectask' +require 'cucumber/rake/task' + +GEM = "mixlib-cli" +GEM_VERSION = "1.0.0" +AUTHOR = "Opscode, Inc." +EMAIL = "info@opscode.com" +HOMEPAGE = "http://www.opscode.com" +SUMMARY = "A simple mixin for CLI interfaces, including option parsing" + +spec = Gem::Specification.new do |s| + s.name = GEM + s.version = GEM_VERSION + s.platform = Gem::Platform::RUBY + s.has_rdoc = true + s.extra_rdoc_files = ["README.rdoc", "LICENSE", "NOTIFY" ] + s.summary = SUMMARY + s.description = s.summary + s.author = AUTHOR + s.email = EMAIL + s.homepage = HOMEPAGE + + # Uncomment this to add a dependency + # s.add_dependency "foo" + + s.require_path = 'lib' + s.autorequire = GEM + s.files = %w(LICENSE README.rdoc Rakefile NOTIFY) + Dir.glob("{lib,spec}/**/*") +end + +task :default => :test + +desc "Run specs" +Spec::Rake::SpecTask.new do |t| + t.spec_files = FileList['spec/**/*_spec.rb'] + t.spec_opts = %w(-fs --color) +end + +desc "Run the spec" +task :test => [ :spec ] + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + +desc "install the gem locally" +task :install => [:package] do + sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}} +end + +desc "create a gemspec file" +task :make_spec do + File.open("#{GEM}.gemspec", "w") do |file| + file.puts spec.to_ruby + end +end
\ No newline at end of file diff --git a/lib/mixlib/cli.rb b/lib/mixlib/cli.rb new file mode 100644 index 0000000..8c15e08 --- /dev/null +++ b/lib/mixlib/cli.rb @@ -0,0 +1,196 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'optparse' + +module Mixlib + module CLI + module ClassMethods + # Add a command line option. + # + # === Parameters + # name<Symbol>:: The name of the option to add + # args<Hash>:: A hash of arguments for the option, specifying how it should be parsed. + # === Returns + # true:: Always returns true. + def option(name, args) + @options ||= {} + raise(ArgumentError, "Option name must be a symbol") unless name.kind_of?(Symbol) + @options[name.to_sym] = args + end + + # Get the hash of current options. + # + # === Returns + # @options<Hash>:: The current options hash. + def options + @options ||= {} + @options + end + + # Set the current options hash + # + # === Parameters + # val<Hash>:: The hash to set the options to + # + # === Returns + # @options<Hash>:: The current options hash. + def options=(val) + raise(ArgumentError, "Options must recieve a hash") unless val.kind_of?(Hash) + @options = val + end + + # Change the banner. Defaults to: + # Usage: #{0} (options) + # + # === Parameters + # bstring<String>:: The string to set the banner to + # + # === Returns + # @banner<String>:: The current banner + def banner(bstring=nil) + if bstring + @banner = bstring + else + @banner ||= "Usage: #{$0} (options)" + @banner + end + end + end + + attr_accessor :options, :config, :banner, :opt_parser + + # Create a new Mixlib::CLI class. If you override this, make sure you call super! + # + # === Parameters + # *args<Array>:: The array of arguments passed to the initializer + # + # === Returns + # object<Mixlib::Config>:: Returns an instance of whatever you wanted :) + def initialize(*args) + @options = Hash.new + @config = Hash.new + + # Set the banner + @banner = self.class.banner + + # Dupe the class options for this instance + klass_options = self.class.options + klass_options.keys.inject(@options) { |memo, key| memo[key] = klass_options[key].dup; memo } + + # Set the default configuration values for this instance + @options.each do |config_key, config_opts| + config_opts[:on] ||= :on + config_opts[:boolean] ||= false + config_opts[:required] ||= false + config_opts[:proc] ||= nil + config_opts[:show_options] ||= false + config_opts[:exit] ||= nil + + if config_opts.has_key?(:default) + @config[config_key] = config_opts[:default] + end + end + + super(*args) + end + + # Parses an array, by default ARGV, for command line options (as configured at + # the class level). + # === Parameters + # argv<Array>:: The array of arguments to parse; defaults to ARGV + # + # === Returns + # argv<Array>:: Returns any un-parsed elements. + def parse_options(argv=ARGV) + @opt_parser = OptionParser.new do |opts| + # Set the banner + opts.banner = banner + + # Create new options + options.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |opt_key, opt_val| + opt_args = build_option_arguments(opt_val) + + opt_method = case opt_val[:on] + when :on + :on + when :tail + :on_tail + when :head + :on_head + else + raise ArgumentError, "You must pass :on, :tail, or :head to :on" + end + + parse_block = case opt_val[:boolean] + when true + Proc.new() do + config[opt_key] = (opt_val[:proc] && opt_val[:proc].call(true)) || true + puts opts if opt_val[:show_options] + exit opt_val[:exit] if opt_val[:exit] + end + when false + Proc.new() do |c| + config[opt_key] = (opt_val[:proc] && opt_val[:proc].call(c)) || c + puts opts if opt_val[:show_options] + exit opt_val[:exit] if opt_val[:exit] + end + end + + full_opt = [ opt_method ] + opt_args.inject(full_opt) { |memo, arg| memo << arg; memo } + full_opt << parse_block + opts.send(*full_opt) + end + end + @opt_parser.parse!(argv) + + # Deal with any required values + options.each do |opt_key, opt_value| + if opt_value[:required] + reqarg = opt_value[:short] || opt_value[:long] + puts "You must supply #{reqarg}!" + puts @opt_parser + exit 2 + end + end + + argv + end + + def build_option_arguments(opt_setting) + arguments = Array.new + + arguments << opt_setting[:short] if opt_setting.has_key?(:short) + arguments << opt_setting[:long] if opt_setting.has_key?(:long) + + if opt_setting.has_key?(:description) + description = opt_setting[:description] + description << " (required)" if opt_setting[:required] + arguments << description + end + + arguments + end + + def self.included(receiver) + receiver.extend(Mixlib::CLI::ClassMethods) + end + + end +end diff --git a/script/destroy b/script/destroy new file mode 100755 index 0000000..40901a8 --- /dev/null +++ b/script/destroy @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit] +RubiGen::Scripts::Destroy.new.run(ARGV) diff --git a/script/generate b/script/generate new file mode 100755 index 0000000..5c8ed01 --- /dev/null +++ b/script/generate @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit] +RubiGen::Scripts::Generate.new.run(ARGV) diff --git a/spec/mixlib/cli_spec.rb b/spec/mixlib/cli_spec.rb new file mode 100644 index 0000000..45bf340 --- /dev/null +++ b/spec/mixlib/cli_spec.rb @@ -0,0 +1,197 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) + +describe Mixlib::CLI do + after(:each) do + TestCLI.options = {} + TestCLI.banner("Usage: #{$0} (options)") + end + + describe "class method" do + describe "option" do + it "should allow you to set a config option with a hash" do + TestCLI.option(:config_file, :short => '-c CONFIG').should == { :short => '-c CONFIG' } + end + end + + describe "options" do + it "should return the current options hash" do + TestCLI.option(:config_file, :short => '-c CONFIG') + TestCLI.options.should == { :config_file => { :short => '-c CONFIG' } } + end + end + + describe "options=" do + it "should allow you to set the full options with a single hash" do + TestCLI.options = { :config_file => { :short => '-c CONFIG' } } + TestCLI.options.should == { :config_file => { :short => '-c CONFIG' } } + end + end + + describe "banner" do + it "should have a default value" do + TestCLI.banner.should =~ /^Usage: (.+) \(options\)$/ + end + + it "should allow you to set the banner" do + TestCLI.banner("Usage: foo") + TestCLI.banner.should == "Usage: foo" + end + end + end + + describe "instance methods" do + + before(:each) do + @cli = TestCLI.new + end + + describe "initialize" do + it "should set the banner to the class defined banner" do + @cli.banner.should == TestCLI.banner + end + + it "should set the options to the class defined options, plus defaults" do + TestCLI.option(:config_file, :short => "-l LOG") + cli = TestCLI.new + cli.options.should == { + :config_file => { + :short => "-l LOG", + :on => :on, + :boolean => false, + :required => false, + :proc => nil, + :show_options => false, + :exit => nil + } + } + end + + it "should set the default config value for any options that include it" do + TestCLI.option(:config_file, :short => "-l LOG", :default => :debug) + @cli = TestCLI.new + @cli.config[:config_file].should == :debug + end + end + + describe "parse_options" do + it "should set the banner in opt_parse" do + @cli.parse_options([]) + @cli.opt_parser.banner.should == @cli.banner + end + + it "should present the arguments in the banner" do + TestCLI.option(:config_file, :short => "-l LOG") + @cli = TestCLI.new + @cli.parse_options([]) + @cli.opt_parser.to_s.should =~ /-l LOG/ + end + + it "should honor :on => :tail options in the banner" do + TestCLI.option(:config_file, :short => "-l LOG") + TestCLI.option(:help, :short => "-h", :boolean => true, :on => :tail) + @cli = TestCLI.new + @cli.parse_options([]) + @cli.opt_parser.to_s.split("\n").last.should =~ /-h/ + end + + it "should honor :on => :head options in the banner" do + TestCLI.option(:config_file, :short => "-l LOG") + TestCLI.option(:help, :short => "-h", :boolean => true, :on => :head) + @cli = TestCLI.new + @cli.parse_options([]) + @cli.opt_parser.to_s.split("\n")[1].should =~ /-h/ + end + + it "should present the arguments in alphabetical order in the banner" do + TestCLI.option(:alpha, :short => "-a ALPHA") + TestCLI.option(:beta, :short => "-b BETA") + TestCLI.option(:zeta, :short => "-z ZETA") + @cli = TestCLI.new + @cli.parse_options([]) + output_lines = @cli.opt_parser.to_s.split("\n") + output_lines[1].should =~ /-a ALPHA/ + output_lines[2].should =~ /-b BETA/ + output_lines[3].should =~ /-z ZETA/ + end + + it "should set the corresponding config value for non-boolean arguments" do + TestCLI.option(:config_file, :short => "-c CONFIG") + @cli = TestCLI.new + @cli.parse_options([ '-c', 'foo.rb' ]) + @cli.config[:config_file].should == 'foo.rb' + end + + it "should set the corresponding config value according to a supplied proc" do + TestCLI.option(:number, + :short => "-n NUMBER", + :proc => Proc.new { |config| config.to_i + 2 } + ) + @cli = TestCLI.new + @cli.parse_options([ "-n", "2" ]) + @cli.config[:number].should == 4 + end + + it "should set the corresponding config value to true for boolean arguments" do + TestCLI.option(:i_am_boolean, :short => "-i", :boolean => true) + @cli = TestCLI.new + @cli.parse_options([ '-i' ]) + @cli.config[:i_am_boolean].should == true + end + + it "should exit if a config option has :exit set" do + TestCLI.option(:i_am_exit, :short => "-x", :boolean => true, :exit => 0) + @cli = TestCLI.new + lambda { @cli.parse_options(["-x"]) }.should raise_error(SystemExit) + end + + it "should exit if a required option is missing" do + TestCLI.option(:require_me, :short => "-r", :boolean => true, :required => true) + @cli = TestCLI.new + lambda { @cli.parse_options([]) }.should raise_error(SystemExit) + end + + end + end + +end + + +# option :config_file, +# :short => "-c CONFIG", +# :long => "--config CONFIG", +# :default => 'config.rb', +# :description => "The configuration file to use" +# +# option :log_level, +# :short => "-l LEVEL", +# :long => "--log_level LEVEL", +# :description => "Set the log level (debug, info, warn, error, fatal)", +# :required => true, +# :proc => nil +# +# option :help, +# :short => "-h", +# :long => "--help", +# :description => "Show this message", +# :on => :tail, +# :boolean => true, +# :show_options => true, +# :exit => 0
\ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..29936df --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,9 @@ +$TESTING=true +$:.push File.join(File.dirname(__FILE__), '..', 'lib') + +require 'mixlib/cli' + +class TestCLI + include Mixlib::CLI +end + |