def parse_options(parser_configuration = {})
      raise_on_invalid_option = parser_configuration.has_key?(:raise_on_invalid_option) ? parser_configuration[:raise_on_invalid_option] : true
      parse_base_options = parser_configuration.has_key?(:parse_base_options) ? parser_configuration[:parse_base_options] : true
      logger.debug "parsing args: #{@args.inspect}, raise_on_invalid_option: #{raise_on_invalid_option}, parse_base_options: #{parse_base_options}"

      @option_parser ||= OptionParser.new

      option_parser.banner = help + "\n\nOptions:"

      if parse_base_options
        option_parser.on("--template [NAME]", "Use a template to render output. (default=default.slim)") do |t|
          options[:template] = t.nil? ? "default.slim" : t
          @template = options[:template]
        end

        option_parser.on("--output FILENAME", "Render output directly to a file") do |f|
          options[:output] = f
          @output = options[:output]
        end

        option_parser.on("--force", "Overwrite file output without prompting") do |f|
          options[:force] = f
        end

        option_parser.on("-r", "--repos a1,a2,a3", "--asset a1,a2,a3", "--filter a1,a2,a3", Array, "List of regex asset name filters") do |list|
          options[:filter] = list
        end

        # NOTE: OptionParser will add short options, there is no way to stop '-m' from being the same as '--match'
        option_parser.on("--match [MODE]", "Asset filter match mode.  MODE=ALL (default), FIRST, EXACT, or ONE (fails if more than 1 match)") do |m|
          options[:match] = m || "ALL"
          options[:match].upcase!
          unless ["ALL", "FIRST", "EXACT", "ONE"].include?(options[:match])
            puts "invalid match mode option: #{options[:match]}"
            exit 1
          end
        end
      end

      # allow decendants to add options
      yield option_parser if block_given?

      # reprocess args for known options, see binary wrapper for first pass
      # (first pass doesn't know about action specific options), find all
      # action options that may come after the action/subcommand (options
      # before subcommand have already been processed) and its args
      logger.debug "args before reprocessing: #{@args.inspect}"
      begin
        option_parser.order!(@args)
      rescue OptionParser::InvalidOption => e
        if raise_on_invalid_option
          puts "option error: #{e}"
          puts option_parser
          exit 1
        else
          # parse and consume until we hit an unknown option (not arg), put it back so it
          # can be shifted into the new array
          e.recover(@args)
        end
      end
      logger.debug "args before unknown collection: #{@args.inspect}"

      unknown_args = []
      while unknown_arg = @args.shift
        logger.debug "unknown_arg: #{unknown_arg.inspect}"
        unknown_args << unknown_arg
        begin
          # consume options and stop at an arg
          option_parser.order!(@args)
        rescue OptionParser::InvalidOption => e
          if raise_on_invalid_option
            puts "option error: #{e}"
            puts option_parser
            exit 1
          else
            # parse and consume until we hit an unknown option (not arg), put it back so it
            # can be shifted into the new array
            e.recover(@args)
          end
        end
      end
      logger.debug "args after unknown collection: #{@args.inspect}"

      @args = unknown_args.dup
      logger.debug "args after reprocessing: #{@args.inspect}"
      logger.debug "configuration after reprocessing: #{@configuration.inspect}"
      logger.debug "options after reprocessing: #{@options.inspect}"

      option_parser
    end
def parse_options(parser_configuration = {})
      raise_on_invalid_option = parser_configuration.has_key?(:raise_on_invalid_option) ? parser_configuration[:raise_on_invalid_option] : true
      parse_base_options = parser_configuration.has_key?(:parse_base_options) ? parser_configuration[:parse_base_options] : true
      logger.debug "parsing args: #{@args.inspect}, raise_on_invalid_option: #{raise_on_invalid_option}, parse_base_options: #{parse_base_options}"

      @option_parser ||= OptionParser.new

      option_parser.banner = help + "\n\nOptions:"

      if parse_base_options
        option_parser.on("--template [NAME]", "Use a template to render output. (default=default.slim)") do |t|
          options[:template] = t.nil? ? "default.slim" : t
          @template = options[:template]
        end

        option_parser.on("--output FILENAME", "Render output directly to a file") do |f|
          options[:output] = f
          @output = options[:output]
        end

        option_parser.on("--force", "Overwrite file output without prompting") do |f|
          options[:force] = f
        end

        option_parser.on("-r", "--repos a1,a2,a3", "--asset a1,a2,a3", "--filter a1,a2,a3", Array, "List of regex asset name filters") do |list|
          options[:filter] = list
        end

        # NOTE: OptionParser will add short options, there is no way to stop '-m' from being the same as '--match'
        option_parser.on("--match [MODE]", "Asset filter match mode.  MODE=ALL (default), FIRST, EXACT, or ONE (fails if more than 1 match)") do |m|
          options[:match] = m || "ALL"
          options[:match].upcase!
          unless ["ALL", "FIRST", "EXACT", "ONE"].include?(options[:match])
            puts "invalid match mode option: #{options[:match]}"
            exit 1
          end
        end
      end

      # allow decendants to add options
      yield option_parser if block_given?

      # reprocess args for known options, see binary wrapper for first pass
      # (first pass doesn't know about action specific options), find all
      # action options that may come after the action/subcommand (options
      # before subcommand have already been processed) and its args
      logger.debug "args before reprocessing: #{@args.inspect}"
      begin
        option_parser.order!(@args)
      rescue OptionParser::InvalidOption => e
        if raise_on_invalid_option
          puts "option error: #{e}"
          puts option_parser
          exit 1
        else
          # parse and consume until we hit an unknown option (not arg), put it back so it
          # can be shifted into the new array
          e.recover(@args)
        end
      end
      logger.debug "args before unknown collection: #{@args.inspect}"

      unknown_args = []
      while unknown_arg = @args.shift
        logger.debug "unknown_arg: #{unknown_arg.inspect}"
        unknown_args << unknown_arg
        begin
          # consume options and stop at an arg
          option_parser.order!(@args)
        rescue OptionParser::InvalidOption => e
          if raise_on_invalid_option
            puts "option error: #{e}"
            puts option_parser
            exit 1
          else
            # parse and consume until we hit an unknown option (not arg), put it back so it
            # can be shifted into the new array
            e.recover(@args)
          end
        end
      end
      logger.debug "args after unknown collection: #{@args.inspect}"

      @args = unknown_args.dup
      logger.debug "args after reprocessing: #{@args.inspect}"
      logger.debug "configuration after reprocessing: #{@configuration.inspect}"
      logger.debug "options after reprocessing: #{@options.inspect}"

      option_parser
    end
Parse generic action options for all decendant actions

 @return [OptionParser] for use by decendant actions