classThorclassOptions<Arguments#:nodoc:LONG_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}$/i# 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.definitialize(hash_options={},defaults={})options=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|@shorts[short.to_s]||=option.switch_nameendendenddefremaining@extraenddefparse(args)@pile=args.dupwhilepeekmatch,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)elsifmatch@extra<<shifted@extra<<shiftwhilepeek&&peek!~/^-/else@extra<<shiftedendendcheck_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=~/^--?(?:(?!--).)*$/}raiseUnknownArgumentError,"Unknown switches '#{unknown.join(', ')}'"unlessunknown.empty?endprotected# Returns true if the current value in peek 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_REtrueelsefalseendenddefswitch?(arg)switch_option(normalize_switch(arg))enddefswitch_option(arg)ifmatch=no_or_skip?(arg)@switches[arg]||@switches["--#{match}"]else@switches[arg]endend# Check if the given argument is actually a shortcut.#defnormalize_switch(arg)(@shorts[arg]||arg).tr('_','-')end# 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)ifcurrent_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_defaultelseraiseMalformattedArgumentError,"No value provided for option '#{switch}'"endend@non_assigned_required.delete(option)send(:"parse_#{option.type}",switch)endendend