1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
module Slop
class Parser
# Our Options instance.
attr_reader :options
# A Hash of configuration options.
attr_reader :config
# Returns an Array of String arguments that were not parsed.
attr_reader :arguments
def initialize(options, **config)
@options = options
@config = config
reset
end
# Reset the parser, useful to use the same instance to parse a second
# time without duplicating state.
def reset
@arguments = []
@options.each(&:reset)
self
end
# Traverse `strings` and process options one by one. Anything after
# `--` is ignored. If a flag includes a equals (=) it will be split
# so that `flag, argument = s.split('=')`.
#
# The `call` method will be executed immediately for each option found.
# Once all options have been executed, any found options will have
# the `finish` method called on them.
#
# Returns a Slop::Result.
def parse(strings)
reset # reset before every parse
pairs = strings.each_cons(2).to_a
# this ensures we still support the last string being a flag,
# otherwise it'll only be used as an argument.
pairs << [strings.last, nil]
@arguments = strings.dup
pairs.each do |flag, arg|
break if !flag
# ignore everything after '--', flag or not
if flag == '--'
arguments.delete(flag)
break
end
# support `foo=bar`
if flag.include?("=")
flag, arg = flag.split("=")
end
if opt = try_process(flag, arg)
# since the option was parsed, we remove it from our
# arguments (plus the arg if necessary)
arguments.delete(flag)
arguments.delete(arg) if opt.expects_argument?
end
end
Result.new(self).tap do |result|
used_options.each { |o| o.finish(result) }
end
end
# Returns an Array of Option instances that were used.
def used_options
options.select { |o| o.count > 0 }
end
# Returns an Array of Option instances that were not used.
def unused_options
options.to_a - used_options
end
private
# We've found an option, process and return it
def process(option, arg)
option.ensure_call(arg)
option
end
# Try and find an option to process
def try_process(flag, arg)
if option = matching_option(flag)
process(option, arg)
elsif flag.start_with?("--no-") && option = matching_option(flag.sub("no-", ""))
process(option, false)
elsif flag =~ /\A-[^-]{2,}/
# try and process as a set of grouped short flags. drop(1) removes
# the prefixed -, then we add them back to each flag separately.
flags = flag.split("").drop(1).map { |f| "-#{f}" }
last = flags.pop
flags.each { |f| try_process(f, nil) }
try_process(last, arg) # send the argument to the last flag
else
if flag.start_with?("-") && !suppress_errors?
raise UnknownOption.new("unknown option `#{flag}'", "#{flag}")
end
end
end
def suppress_errors?
config[:suppress_errors]
end
def matching_option(flag)
options.find { |o| o.flags.include?(flag) }
end
end
end
|