Class: Aargs
- Inherits:
-
Object
show all
- Defined in:
- lib/aargs.rb
Overview
Constant Summary
collapse
- DEFAULT =
Object.new
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(prologue: DEFAULT, flag_config: DEFAULT, flag_configs: nil, epilogue: DEFAULT, aliases: {}, program: nil) ⇒ Aargs
Returns a new instance of Aargs.
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
# File 'lib/aargs.rb', line 165
def initialize(
prologue: DEFAULT,
flag_config: DEFAULT,
flag_configs: nil,
epilogue: DEFAULT,
aliases: {},
program: nil)
@program = program || begin
%r{^(?:.*/)?(?<file>[^/]+):\d+:in} =~ caller.first
file
end
@aliases = aliases.freeze
prologue_set = prologue && prologue != DEFAULT
flag_configs_set = flag_configs && flag_configs != DEFAULT
epilogue_set = epilogue && epilogue != DEFAULT
prologue = epilogue_set || flag_configs_set ? false : true if prologue == DEFAULT
initialize_prologue(prologue)
flag_config = flag_configs_set ? false : true if flag_config == DEFAULT
@flag_configs = Hash.new(flag_config).merge(flag_configs || {}).freeze
epilogue = prologue_set || flag_configs_set ? false : true if epilogue == DEFAULT
initialize_epilogue(epilogue)
@valid = false
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *_) ⇒ Object
359
360
361
362
363
364
365
366
367
368
369
370
|
# File 'lib/aargs.rb', line 359
def method_missing(sym, *_)
return super unless @parsed
/^(?<key>.*?)(?:(?<boolean>\?))?$/ =~ sym
key = key.to_sym
return super unless api_key?(key)
value = @values[key]
return !(!value) if boolean
value
end
|
Instance Attribute Details
#aliases ⇒ Object
152
153
154
|
# File 'lib/aargs.rb', line 152
def aliases
@aliases
end
|
#epilogue_key ⇒ Object
Returns the value of attribute epilogue_key.
161
162
163
|
# File 'lib/aargs.rb', line 161
def epilogue_key
@epilogue_key
end
|
#flag_configs ⇒ Object
Returns the value of attribute flag_configs.
158
159
160
|
# File 'lib/aargs.rb', line 158
def flag_configs
@flag_configs
end
|
#optional_epilogue ⇒ Object
Returns the value of attribute optional_epilogue.
160
161
162
|
# File 'lib/aargs.rb', line 160
def optional_epilogue
@optional_epilogue
end
|
#optional_prologue ⇒ Object
Returns the value of attribute optional_prologue.
156
157
158
|
# File 'lib/aargs.rb', line 156
def optional_prologue
@optional_prologue
end
|
#prologue_key ⇒ Object
Returns the value of attribute prologue_key.
157
158
159
|
# File 'lib/aargs.rb', line 157
def prologue_key
@prologue_key
end
|
#required_epilogue ⇒ Object
Returns the value of attribute required_epilogue.
159
160
161
|
# File 'lib/aargs.rb', line 159
def required_epilogue
@required_epilogue
end
|
#required_prologue ⇒ Object
Returns the value of attribute required_prologue.
155
156
157
|
# File 'lib/aargs.rb', line 155
def required_prologue
@required_prologue
end
|
Class Method Details
.boolean?(sym, flag_configs:) ⇒ Boolean
259
260
261
|
# File 'lib/aargs.rb', line 259
def self.boolean?(sym, flag_configs:)
flag_type(sym, flag_configs: flag_configs) == :boolean
end
|
.flag_config(sym, flag_configs:) ⇒ Object
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
# File 'lib/aargs.rb', line 238
def self.flag_config(sym, flag_configs:)
flag_config = flag_configs[sym]
case flag_config
when true
{ type: :anything }
when Symbol
{ type: flag_config }
when nil
nil
when String
{ help: flag_config }
else
flag_config
end
end
|
.flag_type(sym, flag_configs:) ⇒ Object
254
255
256
257
|
# File 'lib/aargs.rb', line 254
def self.flag_type(sym, flag_configs:)
config = flag_config(sym, flag_configs: flag_configs)
config[:type] if config
end
|
.flagify_arg(arg) ⇒ Object
15
16
17
18
19
20
21
22
23
24
|
# File 'lib/aargs.rb', line 15
def self.flagify_arg(arg)
case arg
when Symbol
"--#{kebab(arg)}"
when Hash
arg.map(&method(:flagify_kwarg)).flatten
else
arg
end
end
|
.flagify_kwarg(arg, value) ⇒ Object
26
27
28
29
30
31
32
33
34
35
36
37
|
# File 'lib/aargs.rb', line 26
def self.flagify_kwarg(arg, value)
case value
when TrueClass
"--#{kebab(arg)}"
when FalseClass
"--no-#{kebab(arg)}"
when Array
value.map { |v| "--#{kebab(arg)}=#{v}" }
else
"--#{kebab(arg)}=#{value}"
end
end
|
.kebab(sym) ⇒ Object
7
8
9
|
# File 'lib/aargs.rb', line 7
def self.kebab(sym)
sym.to_s.gsub(/[^[:alnum:]]/, '-')
end
|
.parse(args_or_argv, aliases: {}, flag_configs: {}) ⇒ Object
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
# File 'lib/aargs.rb', line 46
def self.parse(args_or_argv, aliases: {}, flag_configs: {})
argv = to_argv(*args_or_argv)
literal_only = false
prologue = []
epilogue = []
flags = {}
last_sym = nil
last_sym_pending = nil
resolve = lambda do |src|
raise "Missing value after '#{last_sym_pending}'" if last_sym_pending
sym = underscore(src)
aliases[sym] || sym
end
argv.each do |arg|
if literal_only
epilogue << arg
next
end
case arg
when /^--$/
literal_only = true
last_sym = nil
when /^-([[:alnum:]])$/
last_sym = sym = resolve.call(Regexp.last_match(1))
case flags[sym]
when true
flags[sym] = 2
when Integer
flags[sym] += 1
when nil
flags[sym] = true
else
raise "Unexpected boolean '#{arg}' after set to value #{flags[sym].inspect}"
end
when /^--(?<no>no-)?(?<flag>[[:alnum:]-]+)(?:=(?<value>.*))?$/
flag = Regexp.last_match[:flag]
value = Regexp.last_match[:value]
no = Regexp.last_match[:no]
sym = resolve.call(flag)
boolean = boolean?(sym, flag_configs: flag_configs)
if no
raise "Unexpected value specified with no- prefix: #{arg}" unless value.nil?
flags[sym] = false
last_sym = nil
elsif value.nil?
last_sym = boolean ? nil : sym
case flags[sym]
when true
flags[sym] = 2
when Integer
flags[sym] += 1
when nil, false
flags[sym] = true
else
last_sym_pending = arg
end
else
raise "Unexpected value for #{inspect_flag(arg)}: #{value.inspect}" if boolean
last_sym = nil
case flags[sym]
when nil
flags[sym] = value
when Array
flags[sym] << value
else
flags[sym] = [flags[sym], value]
end
end
else
if last_sym
case flags[last_sym]
when true
flags[last_sym] = arg
when Array
flags[last_sym] << arg
else
flags[last_sym] = [flags[last_sym], arg]
end
last_sym_pending = nil
elsif flags.empty?
prologue << arg
else literal_only = true
epilogue << arg
end
end
next if arg.nil?
end
raise "Missing value after '#{last_sym_pending}'" if last_sym_pending
result = {}
result[:prologue] = prologue unless prologue.empty?
result[:flags] = flags unless flags.empty?
result[:epilogue] = epilogue unless epilogue.empty?
result unless result.empty?
end
|
.to_argv(*args) ⇒ Object
Convert symbolic arguments and keyword-arguments into an equivalent ‘ARGV`. Non-symbol argments remain unchanged. Note that to generate a epilogue portion of an ARGV you need to pass keyword arguments as explicit hashes followed by non-hash, non-symbol values.
42
43
44
|
# File 'lib/aargs.rb', line 42
def self.to_argv(*args)
args.map(&method(:flagify_arg)).flatten
end
|
.underscore(src) ⇒ Object
11
12
13
|
# File 'lib/aargs.rb', line 11
def self.underscore(src)
src.gsub(/[^[:alnum:]]/, '_').to_sym
end
|
Instance Method Details
#api_key?(key) ⇒ Boolean
Returns if the given key is a known flag that should appear as part of the object’s API.
346
347
348
|
# File 'lib/aargs.rb', line 346
def api_key?(key)
@values.member?(key) || @optional_prologue.member?(key) || @flag_configs.member?(key)
end
|
#boolean?(sym) ⇒ Boolean
271
272
273
|
# File 'lib/aargs.rb', line 271
def boolean?(sym)
Aargs.boolean?(sym, flag_configs: flag_configs)
end
|
#flag_config(sym) ⇒ Object
263
264
265
|
# File 'lib/aargs.rb', line 263
def flag_config(sym)
Aargs.flag_config(sym, flag_configs: flag_configs)
end
|
#flag_type(sym) ⇒ Object
267
268
269
|
# File 'lib/aargs.rb', line 267
def flag_type(sym)
Aargs.flag_type(sym, flag_configs: flag_configs)
end
|
#help ⇒ Object
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
|
# File 'lib/aargs.rb', line 298
def help
prologue_keys = [required_prologue, optional_prologue, prologue_key ? prologue_key : nil].map(&method(:Array)).flatten
epilogue_keys = [required_epilogue, optional_epilogue, epilogue_key ? epilogue_key : nil].map(&method(:Array)).flatten
flag_keys = flag_configs.keys
flag_keys << :any_key if flag_configs[:any_key]
all_flags = prologue_keys + (flag_keys - prologue_keys) + epilogue_keys
usage = "Usage: #{@program} #{all_flags.map(&method(:inspect_flag)).join(' ')}"
any_real_help = false
lines = all_flags.map do |flag|
config = flag_config(flag)
next unless config
real_help = config[:help]
any_real_help ||= real_help
flag_help = real_help || case config[:type]
when :boolean
'(switch)'
else
"(#{config[:type]})"
end
[inspect_flag(flag), flag_help] if flag_help
end.compact
return [usage] if lines.empty? || !any_real_help
width = lines.map(&:first).map(&:length).max
lines.map! { |(flag, help)| format(" %<flag>-#{width}s : %<help>s", flag: flag, help: help) }
[usage, nil] + lines
end
|
#inspect_flag(sym) ⇒ Object
287
288
289
290
291
292
293
294
295
296
|
# File 'lib/aargs.rb', line 287
def inspect_flag(sym)
arg = Aargs.kebab(sym)
return "#{arg.upcase}" if required?(sym)
return "[#{arg.upcase}]" if optional?(sym)
return "[aargs]" if sym == :any_key
return "[#{arg.to_s.upcase} ... [#{arg.to_s.upcase}]]" if splat?(sym)
return "--[no-]#{arg}" if boolean?(sym)
"--#{arg}=VALUE"
end
|
#optional?(sym) ⇒ Boolean
279
280
281
|
# File 'lib/aargs.rb', line 279
def optional?(sym)
[optional_prologue, optional_epilogue].map(&method(:Array)).flatten.member?(sym)
end
|
#parse(*args) ⇒ Object
331
332
333
334
335
336
337
338
339
340
341
342
343
|
# File 'lib/aargs.rb', line 331
def parse(*args)
raise 'Aargs are frozen once parsed' if @valid
@parsed = Aargs.parse(args, aliases: aliases, flag_configs: flag_configs) || {}
@values = @parsed[:flags] || {}
parsed_prologue = @parsed[:prologue] || []
validate_sufficient_prologue(parsed_prologue)
consumed_prologue = apply_prologue(parsed_prologue)
apply_epilogue(parsed_prologue, consumed_prologue)
@valid = true
self
end
|
#required?(sym) ⇒ Boolean
275
276
277
|
# File 'lib/aargs.rb', line 275
def required?(sym)
[required_prologue, required_epilogue].map(&method(:Array)).flatten.member?(sym)
end
|
#respond_to_missing?(sym, *_) ⇒ Boolean
350
351
352
353
354
355
356
357
|
# File 'lib/aargs.rb', line 350
def respond_to_missing?(sym, *_)
/^(?<key>.*?)(?:(?<_boolean>\?))?$/ =~ sym
key = key.to_sym
return super unless api_key?(key)
true
end
|
#splat?(sym) ⇒ Boolean
283
284
285
|
# File 'lib/aargs.rb', line 283
def splat?(sym)
[prologue_key, epilogue_key].member?(sym)
end
|
#valid? ⇒ Boolean
327
328
329
|
# File 'lib/aargs.rb', line 327
def valid?
@valid
end
|