classThorclassOptions<Arguments#:nodoc: # rubocop:disable ClassLengthLONG_RE=/^(--\w+(?:-\w+)*)$/SHORT_RE=/^(-[a-z])$/iEQ_RE=/^(--\w+(?:-\w+)*|-[a-z])=(.*)$/iSHORT_SQ_RE=/^-([a-z]{2,})$/i# Allow either -x -v or -xv style for single char argsSHORT_NUM=/^(-[a-z])#{NUMERIC}$/iOPTS_END='--'.freeze# Receives a hash and makes it switches.defself.to_switches(options)options.mapdo|key,value|casevaluewhentrue"--#{key}"whenArray"--#{key}#{value.map{|v|v.inspect}.join(' ')}"whenHash"--#{key}#{value.map{|k,v|"#{k}:#{v}"}.join(' ')}"whennil,false''else"--#{key}#{value.inspect}"endend.join(' ')end# Takes a hash of Thor::Option and a hash with defaults.## If +stop_on_unknown+ is true, #parse will stop as soon as it encounters# an unknown option or a regular argument.definitialize(hash_options={},defaults={},stop_on_unknown=false)@stop_on_unknown=stop_on_unknownoptions=hash_options.valuessuper(options)# Add defaultsdefaults.eachdo|key,value|@assigns[key.to_s]=value@non_assigned_required.delete(hash_options[key])end@shorts,@switches,@extra={},{},[]options.eachdo|option|@switches[option.switch_name]=optionoption.aliases.eachdo|short|name=short.to_s.sub(/^(?!\-)/,'-')@shorts[name]||=option.switch_nameendendenddefremaining# rubocop:disable TrivialAccessors@extraenddefpeekreturnsuperunless@parsing_optionsresult=superifresult==OPTS_ENDshift@parsing_options=falsesuperelseresultendenddefparse(args)# rubocop:disable MethodLength@pile=args.dup@parsing_options=truewhilepeekifparsing_options?match,is_switch=current_is_switch?shifted=shiftifis_switchcaseshiftedwhenSHORT_SQ_REunshift($1.split('').map{|f|"-#{f}"})nextwhenEQ_RE,SHORT_NUMunshift($2)switch=$1whenLONG_RE,SHORT_REswitch=$1endswitch=normalize_switch(switch)option=switch_option(switch)@assigns[option.human_name]=parse_peek(switch,option)elsif@stop_on_unknown@parsing_options=false@extra<<shifted@extra<<shiftwhilepeekbreakelsifmatch@extra<<shifted@extra<<shiftwhilepeek&&peek!~/^-/else@extra<<shiftedendelse@extra<<shiftendendcheck_requirement!assigns=Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)assigns.freezeassignsenddefcheck_unknown!# an unknown option starts with - or -- and has no more --'s afterward.unknown=@extra.select{|str|str=~/^--?(?:(?!--).)*$/}failUnknownArgumentError,"Unknown switches '#{unknown.join(', ')}'"unlessunknown.empty?endprotected# Check if the current value in peek is a registered switch.## Two booleans are returned. The first is true if the current value# starts with a hyphen; the second is true if it is a registered switch.defcurrent_is_switch?casepeekwhenLONG_RE,SHORT_RE,EQ_RE,SHORT_NUM[true,switch?($1)]whenSHORT_SQ_RE[true,$1.split('').any?{|f|switch?("-#{f}")}]else[false,false]endenddefcurrent_is_switch_formatted?casepeekwhenLONG_RE,SHORT_RE,EQ_RE,SHORT_NUM,SHORT_SQ_REtrueelsefalseendenddefcurrent_is_value?peek&&(!parsing_options?||super)enddefswitch?(arg)switch_option(normalize_switch(arg))enddefswitch_option(arg)ifmatch=no_or_skip?(arg)# rubocop:disable AssignmentInCondition@switches[arg]||@switches["--#{match}"]else@switches[arg]endend# Check if the given argument is actually a shortcut.#defnormalize_switch(arg)(@shorts[arg]||arg).tr('_','-')enddefparsing_options?peek@parsing_optionsend# Parse boolean values which can be given as --foo=true, --foo or --no-foo.#defparse_boolean(switch)ifcurrent_is_value?if['true','TRUE','t','T',true].include?(peek)shifttrueelsif['false','FALSE','f','F',false].include?(peek)shiftfalseelsetrueendelse@switches.key?(switch)||!no_or_skip?(switch)endend# Parse the value at the peek analyzing if it requires an input or not.#defparse_peek(switch,option)ifparsing_options?&&(current_is_switch_formatted?||last?)ifoption.boolean?# No problem for boolean typeselsifno_or_skip?(switch)returnnil# User set value to nilelsifoption.string?&&!option.required?# Return the default if there is one, else the human namereturnoption.lazy_default||option.default||option.human_nameelsifoption.lazy_defaultreturnoption.lazy_defaultelsefailMalformattedArgumentError,"No value provided for option '#{switch}'"endend@non_assigned_required.delete(option)send(:"parse_#{option.type}",switch)endendend