class QB::Options

Constants

QB_DEFAULTS

Default initial values for {#qb}.

@return [Hash]

SPACER

Appended on the end of an `opts.on` call to create a newline after the option (making the help output a bit easier to read)

You might think the empty string would be reasonable, but OptionParser blows up if you do that.

@return [String]

Attributes

ansible[R]

@!attribute [r] ansible

@return [Hash<String, String>]
  options to pass through to ansible-playbook.
qb[R]

@!attribute [r] qb

@return [Hash<String, *>]
  common qb-level options.
role_options[R]

@!attribute [r] role_options

@return [Hash<String, QB::Options::Option>]
  options to pass through to ansible-playbook.

Public Class Methods

add(opts, options, role, include_path = []) click to toggle source

Add the options from a role to the OptionParser

@param [OptionParser] opts

The option parser to add options to.
# File lib/qb/options.rb, line 137
def self.add opts, options, role, include_path = []
  QB.debug "adding options", "role" => role
  
  role.option_metas.each do |option_meta|
    if option_meta.key? 'include'
      include_role opts, options, option_meta, include_path
      
    else
      # create an option
      option = Option.new role, option_meta, include_path
      
      option.option_parser_add opts, included: !include_path.empty?
      
      options[option.cli_name] = option
    end
  end # each var
end
cli_ize_name(option_name) click to toggle source

turn a name into a “command line” version by replacing underscores with dashes.

@param [String] option_name

the input option name.

@return [String]

the CLI-ized name.

@example

QB::Options.cli_ize_name "my_var" # => "my-var"
# File lib/qb/options.rb, line 86
def self.cli_ize_name option_name
  option_name.gsub '_', '-'
end
include_role(opts, options, include_meta, include_path) click to toggle source
# File lib/qb/options.rb, line 106
def self.include_role opts, options, include_meta, include_path
  role_name = include_meta['include']
  role = QB::Role.require role_name
  new_include_path = if include_meta.key? 'as'
    case include_meta['as']
    when nil, false
      # include it in with the parent role's options
      include_path
    when String
      include_path + [include_meta['as']]
    else
      raise QB::Role::MetadataError.new,
        "bad 'as' value: #{ include_meta.inspect }"
    end
  else
    include_path + [role.namespaceless]
  end
  
  QB.debug "including #{ role.name } as #{ new_include_path.join('-') }"
  
  opts.separator "Options for included #{ role.name } role:"
  
  add opts, options, role, new_include_path
end
new(role, argv) click to toggle source

@param [Role] role

the role to parse the args for.
# File lib/qb/options.rb, line 187
def initialize role, argv
  @role = role
  @argv = argv
  @qb = QB_DEFAULTS.dup
  
  parse!
end
parse!(role, argv) click to toggle source

Destructively removes options from `@argv` and populates ansible, role, and qb option hashes.

@param [QB::Role] role

the role to parse the options for.

@param [Array<String>] args

CLI args -- `ARGV` with the role arg shifted off.

@return [Array<Hash<String, Option|Object>>]

a two-element array:

1.  the options for the role, hash of Option#cli_name to Option
    instances.

2.  the general qb options, hash of String key to option values.

@raise if bad options are found.

# File lib/qb/options.rb, line 175
def self.parse! role, argv
  options = self.new role, argv
  [options.role_options, options.qb]
end
var_ize_name(option_name) click to toggle source

turn a name into a “ruby / ansible variable” version by replacing dashes with underscores.

@param [String] option_name

the input option name.

@return [String]

the ruby / ansible var-ized name.

@example

QB::Options.cli_ize_name "my-var" # => "my_var"
# File lib/qb/options.rb, line 102
def self.var_ize_name option_name
  option_name.gsub '-', '_'
end

Public Instance Methods

accept_false?() click to toggle source

Does the option accept `false` as value?

If it does, and is not a boolean option, we also accept a `–no-<name>` option format to set the value to `false`.

This is useful to explicitly tell QB “no, I don't want this”, since we treat `nil`/`null` as the same as absent, which will cause a default value to be used (if available).

This feature does not apply to {#boolean?} options themselves, only options that accept other values (though this method will of course return `true` for {#boolean?} options, since they do accept `false`).

@return [Boolean]

# File lib/qb/options/option.rb, line 305
def accept_false?
  return true if meta[:accept_false]
  
  return false if type.is_a?( Class ) && type < NRSER::Props
  
  type.test?( false )
end
ask?() click to toggle source

@todo Document ask? method.

@param [type] arg_name

@todo Add name param description.

@return [return_type]

@todo Document return value.
# File lib/qb/options.rb, line 204
def ask?
  @qb['ask']
end
boolean?() click to toggle source
# File lib/qb/options/option.rb, line 258
def boolean?
  type == t.bool
end
description() click to toggle source

Description of the option.

@return [String]

# File lib/qb/options/option.rb, line 250
def description
  meta(
    :description,
    default: "Set the #{ @var_name } role variable"
  ).to_s
end
examples() click to toggle source

get an array of examples for the option. returns `[]` if no examples are defined.

@return [Array<String>]

# File lib/qb/options/option.rb, line 285
def examples
  Array meta( *EXAMPLES_KEYS, type: (t.nil | t.str | t.array( t.str )) )
end
has_examples?() click to toggle source

test if the option has any examples.

@return [Boolean]

# File lib/qb/options/option.rb, line 276
def has_examples?
  meta? *EXAMPLES_KEYS
end
meta?(*keys) click to toggle source
# File lib/qb/options/option.rb, line 214
def meta? *keys
  keys.any? { |key| @meta.key? key }
end
required?() click to toggle source

Is the option is required in the CLI?

@return [Boolean]

# File lib/qb/options/option.rb, line 232
def required?
  meta :required, :require, type: t.bool, default: false
end
save?() click to toggle source

Should we save the option value in `./.qb-options.yml`?

@return [Boolean]

# File lib/qb/options/option.rb, line 241
def save?
  meta :save, type: t.bool, default: true
end
usage() click to toggle source
# File lib/qb/options/option.rb, line 263
def usage
  if boolean?
    "--[no-]#{ cli_name }"
  else
    "--#{ cli_name }=#{ meta_name.upcase }"
  end
end
value_data() click to toggle source
# File lib/qb/options/option.rb, line 219
def value_data
  if value.respond_to? :to_data
    value.to_data
  else
    value
  end
end

Protected Instance Methods

parse_ansible!() click to toggle source

Pull options that start with

  1. `–ANSIBLE_`

  2. `–ANSIBLE-`

  3. `—`

out of `@argv` and stick them in `@ansible`.

@return [nil]

**Mutates** `@argv`.
# File lib/qb/options.rb, line 368
def parse_ansible!
  logger.debug "Parsing Ansible options...",
    argv: @argv.dup
  
  @ansible = @role.default_ansible_options.clone
  
  reg_exs = [
    /\A\-\-ANSIBLE[\-\_]/,
    /\A\-\-\-/,
  ]
  
  @argv.reject! {|shellword|
    if re = reg_exs.find {|re| re =~ shellword}
      name = shellword.sub re, ''
      
      value = true
      
      if name.include? '='
        name, value = name.split('=', 2)
      end
      
      @ansible[name] = value
      
      true
    end
  }
  
  nil
end

Private Instance Methods

parse!() click to toggle source

destructively removes options from `@argv` and populates ansible, role, and qb option hashes.

# File lib/qb/options.rb, line 215
  def parse!
    parse_ansible!
    
    @role_options = {}
    
    if @role.meta['default_user']
      @qb['user'] = @role.meta['default_user']
    end
    
    opt_parser = OptionParser.new do |opts|
      opts.accept(QB::Package::Version) do |string|
        QB::Package::Version.from( string ).to_h
      end
      
      opts.banner = @role.banner
      
      opts.separator "Common options:"
      
      opts.on(
        '-H',
        '--HOSTS=HOSTS',
        Array,
        "set playbook host",
        "DEFAULT: localhost",
        SPACER
      ) do |value|
        @qb['hosts'] = value
      end
      
      opts.on(
        '-I',
        '--INVENTORY=FILEPATH',
        String,
        "set inventory file",
        SPACER
      ) do |value|
        @qb['inventory'] = value
      end
      
      opts.on(
        '-U',
        '--USER=USER',
        String,
        "ansible become user for the playbook",
        SPACER
      ) do |value|
        @qb['user'] = value
      end
      
      opts.on(
        '-T',
        '--TAGS=TAGS',
        Array,
        "playbook tags",
        SPACER
      ) do |value|
        @qb['tags'] = value
      end
      
      opts.on(
        '-V[LEVEL]',
        "run playbook in verbose mode. use like -VVV or -V3.",
        SPACER
      ) do |value|
        # QB.debug "verbose", value: value
        
        @qb['verbose'] = if value.nil?
          1
        else
          case value
          when '0'
            false
          when /^[1-4]$/
            value.to_i
          when /^[V]{1,3}$/i
            value.length + 1
          else
            raise "bad verbose value: #{ value.inspect }"
          end
        end
      end
      
      opts.on(
        '--NO-FACTS',
        "don't gather facts",
        SPACER
      ) do |value|
        @qb['facts'] = false
      end
      
      opts.on(
        '--PRINT=FLAGS',
        Array,
        "set what to print before running: options, env, cmd, playbook",
        SPACER
      ) do |value|
        @qb['print'] = value
      end
      
      opts.on(
        '--NO-RUN',
        "don't run the playbook (useful to just print stuff)",
        SPACER
      ) do |value|
        @qb['run'] = false
      end
      
      opts.on(
        '-A',
        '--ASK',
        "interactively ask for argument and option values",
        SPACER
      ) do |value|
        if value && !$stdin.isatty
          raise ArgumentError.squished <<-END
            Interactive args & options only works with TTY $stdin.
          END
        end
        
        @qb['ask'] = value
      end
      
      opts.separator "Role options:"
      
      self.class.add opts, @role_options, @role
      
      opts.on_tail("-h", "--help", "Show this message") do
        puts opts
        
        @role.puts_examples
        
        exit
      end
    end
    
    opt_parser.parse! @argv
  end