module Mixlib::CLI

Mixlib::CLI

Adds a DSL for defining command line options and methods for parsing those options to the including class.

Mixlib::CLI does some setup in initialize, so the including class must call `super()` if it defines a custom initializer.

DSL

When included, Mixlib::CLI also extends the including class with its ClassMethods, which define the DSL. The primary methods of the DSL are ClassMethods#option, which defines a command line option; ClassMethods#banner, which defines the “usage” banner; and ClassMethods#deprecated_option, which defines a deprecated command-line option.

Parsing

Command line options are parsed by calling the instance method parse_options. After calling this method, the attribute config will contain a hash of `:option_name => value` pairs.

Constants

VERSION

Attributes

banner[RW]

Banner for the option parser. If the option parser is printed, e.g., by `puts opt_parser`, this string will be used as the first line.

cli_arguments[RW]

Any arguments which were not parsed and placed in “config”–the leftovers.

config[RW]

A Hash containing the values supplied by command line options.

The behavior and contents of this Hash vary depending on whether ClassMethods#use_separate_default_options is enabled.

use_separate_default_options disabled

After initialization, config will contain any default values defined via the mixlib-config DSL. When parse_options is called, user-supplied values (from ARGV) will be merged in.

use_separate_default_options enabled

After initialization, this will be an empty hash. When parse_options is called, config is populated only with user-supplied values.

default_config[RW]

If ClassMethods#use_separate_default_options is enabled, this will be a Hash containing key value pairs of `:option_name => default_value` (populated during object initialization).

If use_separate_default_options is disabled, it will always be an empty hash.

options[RW]

Gives the command line options definition as configured in the DSL. These are used by parse_options to generate the option parsing code. To get the values supplied by the user, see config.

Public Class Methods

included(receiver) click to toggle source
# File lib/mixlib/cli.rb, line 445
def self.included(receiver)
  receiver.extend(Mixlib::CLI::ClassMethods)
  receiver.extend(Mixlib::CLI::InheritMethods)
end
new(*args) click to toggle source

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 :)

Calls superclass method
# File lib/mixlib/cli.rb, line 258
def initialize(*args)
  @options = Hash.new
  @config  = Hash.new
  @default_config = Hash.new
  @opt_parser = nil

  # 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 }

  # If use_separate_defaults? is on, default values go in @default_config
  defaults_container = if self.class.use_separate_defaults?
                         @default_config
                       else
                         @config
                       end

  # 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
    config_opts[:in] ||= nil
    if config_opts.key?(:default)
      defaults_container[config_key] = config_opts[:default]
    end
  end

  super(*args)
end

Public Instance Methods

build_option_arguments(opt_setting) click to toggle source
# File lib/mixlib/cli.rb, line 429
def build_option_arguments(opt_setting)
  arguments = Array.new

  arguments << opt_setting[:short] if opt_setting[:short]
  arguments << opt_setting[:long] if opt_setting[:long]
  if opt_setting.key?(:description)
    description = opt_setting[:description].dup
    description << " (required)" if opt_setting[:required]
    description << " (valid options: #{Formatter.friendly_opt_list(opt_setting[:in])})" if opt_setting[:in]
    opt_setting[:description] = description
    arguments << description
  end

  arguments
end
handle_deprecated_options(show_deprecations) click to toggle source

Iterates through options declared as deprecated, maps values to their replacement options, and prints deprecation warnings.

@return NilClass

# File lib/mixlib/cli.rb, line 391
def handle_deprecated_options(show_deprecations)
  merge_in_values = {}
  config.each_key do |opt_key|
    opt_cfg = options[opt_key]

    # Deprecated entries do not have defaults so no matter what
    # separate_default_options are set, if we see a 'config'
    # entry that contains a deprecated indicator, then the option was
    # explicitly provided by the caller.
    #
    # opt_cfg may not exist if an inheriting application
    # has directly inserted values info config.
    next unless opt_cfg && opt_cfg[:deprecated]

    replacement_key = opt_cfg[:replacement]
    if replacement_key
      # This is the value passed into the deprecated flag. We'll use
      # the declared value mapper (defaults to return the same value if caller hasn't
      # provided a mapper).
      deprecated_val = config[opt_key]

      # We can't modify 'config' since we're iterating it, apply updates
      # at the end.
      merge_in_values[replacement_key] = opt_cfg[:value_mapper].call(deprecated_val)
      config.delete(opt_key) unless opt_cfg[:keep]
    end

    # Warn about the deprecation.
    if show_deprecations
      # Description is also the deprecation message.
      display_name = CLI::Formatter.combined_option_display_name(opt_cfg[:short], opt_cfg[:long])
      puts "#{display_name}: #{opt_cfg[:description]}"
    end
  end
  config.merge!(merge_in_values)
  nil
end
opt_parser() click to toggle source

The option parser generated from the mixlib-cli DSL. opt_parser can be used to print a help message including the banner and any CLI options via `puts opt_parser`.

Returns

opt_parser<OptionParser>

The option parser object.

# File lib/mixlib/cli.rb, line 341
def opt_parser
  @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 =
        Proc.new() do |c|
          config[opt_key] = if opt_val[:proc]
                              if opt_val[:proc].arity == 2
                                # New hotness to allow for reducer-style procs.
                                opt_val[:proc].call(c, config[opt_key])
                              else
                                # Older single-argument proc.
                                opt_val[:proc].call(c)
                              end
                            else
                              # No proc.
                              c
                            end
          puts opts if opt_val[:show_options]
          exit opt_val[:exit] if opt_val[:exit]
        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
end
parse_options(argv = ARGV, show_deprecations: true) click to toggle source

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.

# File lib/mixlib/cli.rb, line 302
def parse_options(argv = ARGV, show_deprecations: true)
  argv = argv.dup
  opt_parser.parse!(argv)
  # Do this before our custom validations, so that custom
  # validations apply to any converted deprecation values;
  # but after parse! so that everything is populated.
  handle_deprecated_options(show_deprecations)

  # Deal with any required values
  options.each do |opt_key, opt_config|
    if opt_config[:required] && !config.key?(opt_key)
      reqarg = opt_config[:short] || opt_config[:long]
      puts "You must supply #{reqarg}!"
      puts @opt_parser
      exit 2
    end
    if opt_config[:in]
      unless opt_config[:in].kind_of?(Array)
        raise(ArgumentError, "Options config key :in must receive an Array")
      end
      if config[opt_key] && !opt_config[:in].include?(config[opt_key])
        reqarg = Formatter.combined_option_display_name(opt_config[:short], opt_config[:long])
        puts "#{reqarg}: #{config[opt_key]} is not one of the allowed values: #{Formatter.friendly_opt_list(opt_config[:in])}"
        # TODO - get rid of this. nobody wants to be spammed with a  ton of information, particularly since we just told them the exact problem and how to fix it.
        puts @opt_parser
        exit 2
      end
    end
  end

  @cli_arguments = argv
  argv
end