optiflag 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ReleaseNotes.txt +110 -0
- data/doc/example/example_1.rb +30 -0
- data/doc/example/example_1_1.rb +27 -0
- data/doc/example/example_1_2.rb +28 -0
- data/doc/example/example_2.rb +28 -0
- data/doc/example/example_2_1.rb +23 -0
- data/doc/example/example_2_2.rb +31 -0
- data/doc/example/example_2_3.rb +37 -0
- data/doc/example/example_2_4.rb +37 -0
- data/doc/example/example_2_5.rb +28 -0
- data/doc/example/example_2_6.rb +22 -0
- data/doc/example/example_3.rb +33 -0
- data/doc/example/example_4.rb +12 -0
- data/doc/example/example_5.rb +26 -0
- data/doc/example/example_6.rb +17 -0
- data/doc/example/example_7.rb +39 -0
- data/lib/optiflag.rb +860 -0
- data/optiflag.gemspec +13 -0
- data/test/tc_advanced_usage_helping.rb +42 -0
- data/test/tc_basic_alternate_forms.rb +30 -0
- data/test/tc_basic_char_flags.rb +103 -0
- data/test/tc_basic_optional_flag.rb +74 -0
- data/test/tc_basic_parsing.rb +51 -0
- data/test/tc_basic_usage_helping.rb +40 -0
- data/test/tc_basic_value_validation.rb +40 -0
- data/test/tc_bug_one.rb +25 -0
- data/test/tc_bug_two.rb +33 -0
- data/test/tc_change_symbols.rb +61 -0
- data/test/tc_enumerated_value_validation.rb +35 -0
- data/test/tc_flagall.rb +17 -0
- data/test/tc_flagless_arg.rb +37 -0
- data/test/tc_keyword.rb +37 -0
- data/test/tc_values_as_hash.rb +46 -0
- metadata +95 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'optiflag'
|
2
|
+
|
3
|
+
# Example 4: Disallowing warnings to proceed
|
4
|
+
module Example extend OptiFlag::Flagset
|
5
|
+
flag "dir"
|
6
|
+
flag "username"
|
7
|
+
handle_errors_and_help :with_no_warnings
|
8
|
+
end
|
9
|
+
|
10
|
+
#h# ruby example_4.rb -garbage -dir c:/temp -username daniel -this-is-garbage that.is.left.over
|
11
|
+
|
12
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'optiflag'
|
2
|
+
|
3
|
+
# Example 5: Using keyword
|
4
|
+
module Example extend OptiFlag::Flagset
|
5
|
+
keyword "checkin" do
|
6
|
+
alternate_forms "ci"
|
7
|
+
end
|
8
|
+
keyword "checkout" do
|
9
|
+
alternate_forms "co"
|
10
|
+
end
|
11
|
+
flag "file"
|
12
|
+
flag "m" do
|
13
|
+
alternate_forms "message"
|
14
|
+
end
|
15
|
+
|
16
|
+
handle_errors_and_help
|
17
|
+
end
|
18
|
+
|
19
|
+
puts "User has chosen to checkin #{ARGV.flag_value.file } " if ARGV.flag_value.ci?
|
20
|
+
puts "User has chosen to check out #{ARGV.flag_value.file }" if ARGV.flag_value.checkout?
|
21
|
+
|
22
|
+
#h# ruby example_5.rb ci -file c:/StronglyTyped.java -m done
|
23
|
+
#h# ruby example_5.rb co -file c:/StronglyTyped.java -m done
|
24
|
+
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'optiflag'
|
2
|
+
|
3
|
+
# Example 6: Using Hash Access
|
4
|
+
module HashAcess extend OptiFlag::Flagset
|
5
|
+
flag "dir"
|
6
|
+
flag "log_level"
|
7
|
+
|
8
|
+
handle_errors_and_help
|
9
|
+
end
|
10
|
+
|
11
|
+
puts "Dir is: #{ ARGV.flag_value[:dir] }"
|
12
|
+
puts "Log Level is: #{ ARGV.flag_value[:log_level] }"
|
13
|
+
|
14
|
+
#h# ruby example_6.rb -dir "c:/Program Files/Apache Software Foundation/Tomcat 5/" -log_level 3
|
15
|
+
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'optiflag'
|
2
|
+
|
3
|
+
# Example 7: Character Flags
|
4
|
+
module HashAcess extend OptiFlag::Flagset
|
5
|
+
|
6
|
+
character_flag :l, :list_group
|
7
|
+
character_flag :s, :list_group
|
8
|
+
character_flag :a, :list_group
|
9
|
+
|
10
|
+
character_flag :x do
|
11
|
+
description "Extract"
|
12
|
+
end
|
13
|
+
character_flag :v do
|
14
|
+
description "Verbose"
|
15
|
+
end
|
16
|
+
character_flag :f do
|
17
|
+
description "Archive File"
|
18
|
+
end
|
19
|
+
|
20
|
+
handle_errors_and_help
|
21
|
+
end
|
22
|
+
|
23
|
+
f = ARGV.flag_value
|
24
|
+
puts "'l' flag set?: #{ f.l? }"
|
25
|
+
puts "'s' flag set?: #{ f.s? }"
|
26
|
+
puts "'a' flag set?: #{ f.a? }"
|
27
|
+
puts "'x' flag set?: #{ f.x? }"
|
28
|
+
puts "'f' flag set?: #{ f.f? }"
|
29
|
+
puts "'v' flag set?: #{ f.v? }"
|
30
|
+
|
31
|
+
#h# ruby example_7.rb -lsa
|
32
|
+
#h# ruby example_7.rb -ls -a
|
33
|
+
#h# ruby example_7.rb -a -s -l
|
34
|
+
#h# ruby example_7.rb -als -x -vf
|
35
|
+
#h# ruby example_7.rb -s -xvf
|
36
|
+
#h# ruby example_7.rb -lsaxvf
|
37
|
+
|
38
|
+
|
39
|
+
|
data/lib/optiflag.rb
ADDED
@@ -0,0 +1,860 @@
|
|
1
|
+
#
|
2
|
+
# http://optiflag.rubyforge.org/
|
3
|
+
#
|
4
|
+
# Author:: Daniel O. Eklund
|
5
|
+
# Copyright:: Copyright (c) 2006 Daniel O. Eklund. All rights reserved.
|
6
|
+
# License:: Ruby license.
|
7
|
+
|
8
|
+
module OptiFlag
|
9
|
+
VERSION = "0.6"
|
10
|
+
end
|
11
|
+
|
12
|
+
#------------------------------------------------------------------------------------------
|
13
|
+
#------------------------------------------------------------------------------------------
|
14
|
+
#------------------------------------------------------------------------------------------
|
15
|
+
#------------- Clause-Level Flag-Modifiers via the EachFlag class -----------------------
|
16
|
+
#------------------------------------------------------------------------------------------
|
17
|
+
#------------------------------------------------------------------------------------------
|
18
|
+
#------------------------------------------------------------------------------------------
|
19
|
+
module OptiFlag
|
20
|
+
module Flagset
|
21
|
+
@dash_symbol = "-"
|
22
|
+
attr_reader :dash_symbol
|
23
|
+
module_function :dash_symbol
|
24
|
+
def self.flag_symbol(val)
|
25
|
+
@dash_symbol = val
|
26
|
+
end
|
27
|
+
def self.increment_order_counter()
|
28
|
+
@counter ||= 0
|
29
|
+
@counter = @counter + 1
|
30
|
+
return @counter -1
|
31
|
+
end
|
32
|
+
class EachFlag
|
33
|
+
attr_reader :name, :flag, :ordered_added,
|
34
|
+
:position_validator,:validation_error,
|
35
|
+
:position_enumerated_values_validator,:the_pretranslate,:the_posttranslate,
|
36
|
+
:the_posttranslate_all,:the_pretranslate_all,:enclosing_module,
|
37
|
+
:the_description,:the_long_dash_symbol,:the_dash_symbol,
|
38
|
+
:the_arity,:the_long_form,:the_alternate_forms,:the_is_required
|
39
|
+
attr_writer :the_form_that_is_actually_used
|
40
|
+
attr_accessor :value,:for_help,:for_extended_help,:proxied_bound_method
|
41
|
+
def initialize(name,flag,enclosing_module)
|
42
|
+
# these next two lines are a highly complicated hack needed to make
|
43
|
+
# the use of two module definitions in one file, one with a flag_symbol
|
44
|
+
# and one without.. See tc_change_symbols.rb for the two tests that used to
|
45
|
+
# cause the problem... also see the changes as part of the definition of
|
46
|
+
# OptiFlag.Flagset().... -- D.O.E 5/30/06
|
47
|
+
# Search for 'def OptiFlag.Flagset(hash)' in this file
|
48
|
+
singleton_class_of_enclosing_module = class << enclosing_module; self; end;
|
49
|
+
x = singleton_class_of_enclosing_module.included_modules.select do |x|
|
50
|
+
(x.to_s =~ /OptiF/) or (x.to_s =~ /#<Modu/)
|
51
|
+
end[0]
|
52
|
+
|
53
|
+
@the_compresses,@enclosing_module = false,enclosing_module
|
54
|
+
@position_validator,@validation_error = [],[]
|
55
|
+
@position_enumerated_values_validator = []
|
56
|
+
@ordered_added = OptiFlag::Flagset::increment_order_counter()
|
57
|
+
@name,@flag,@the_long_form = name,flag,flag
|
58
|
+
@the_dash_symbol, @the_arity, @the_alternate_forms,
|
59
|
+
@the_is_required, @the_long_dash_symbol = x.dash_symbol, 1, [], true, "--"
|
60
|
+
end
|
61
|
+
# clause-level flag-modifier
|
62
|
+
def translate(position=0,&theblock)
|
63
|
+
pretranslate(position,&theblock)
|
64
|
+
end
|
65
|
+
# clause-level flag-modifier
|
66
|
+
def translate_all(&theblock)
|
67
|
+
pretranslate_all(&theblock)
|
68
|
+
end
|
69
|
+
# clause-level flag-modifier
|
70
|
+
def pretranslate(position=0,&theblock)
|
71
|
+
@the_pretranslate ||= []
|
72
|
+
@the_pretranslate[position] = theblock
|
73
|
+
end
|
74
|
+
# clause-level flag-modifier
|
75
|
+
def posttranslate(position=0,&theblock)
|
76
|
+
@the_posttranslate ||= []
|
77
|
+
@the_posttranslate[position] = theblock
|
78
|
+
end
|
79
|
+
# clause-level flag-modifier
|
80
|
+
def pretranslate_all(&theblock)
|
81
|
+
@the_pretranslate_all = theblock
|
82
|
+
end
|
83
|
+
# clause-level flag-modifier
|
84
|
+
def posttranslate_all(&theblock)
|
85
|
+
@the_posttranslate_all = theblock
|
86
|
+
end
|
87
|
+
# clause-level flag-modifier
|
88
|
+
def is_required(yes_or_no)
|
89
|
+
@the_is_required = yes_or_no
|
90
|
+
end
|
91
|
+
# clause-level flag-modifier
|
92
|
+
def value_matches(desc_regexp_pair,arg_position=0)
|
93
|
+
if desc_regexp_pair.class == Regexp
|
94
|
+
desc_regexp_pair = ["This value does not match the pattern: #{ desc_regexp_pair.source }",
|
95
|
+
desc_regexp_pair]
|
96
|
+
end
|
97
|
+
@position_validator[arg_position] = desc_regexp_pair
|
98
|
+
end
|
99
|
+
# clause-level flag-modifier
|
100
|
+
def value_in_set(array_of_acceptable_values,arg_position=0)
|
101
|
+
@position_enumerated_values_validator[arg_position] =
|
102
|
+
array_of_acceptable_values
|
103
|
+
end
|
104
|
+
# clause-level flag-modifier
|
105
|
+
def required
|
106
|
+
is_required(true)
|
107
|
+
end
|
108
|
+
# clause-level flag-modifier
|
109
|
+
def optional
|
110
|
+
is_required(false)
|
111
|
+
end
|
112
|
+
# clause-level flag-modifier
|
113
|
+
def no_arg
|
114
|
+
no_args
|
115
|
+
end
|
116
|
+
# clause-level flag-modifier
|
117
|
+
def no_args
|
118
|
+
arity 0
|
119
|
+
end
|
120
|
+
# clause-level flag-modifier
|
121
|
+
def one_arg
|
122
|
+
arity 1
|
123
|
+
end
|
124
|
+
# clause-level flag-modifier
|
125
|
+
def two_args
|
126
|
+
arity 2
|
127
|
+
end
|
128
|
+
# clause-level flag-modifier
|
129
|
+
def alternate_forms(*args)
|
130
|
+
@the_alternate_forms = *args
|
131
|
+
end
|
132
|
+
# clause-level flag-modifier
|
133
|
+
def long_form(form)
|
134
|
+
@the_long_form = form
|
135
|
+
end
|
136
|
+
# clause-level flag-modifier
|
137
|
+
def arity(num)
|
138
|
+
@the_arity = num
|
139
|
+
end
|
140
|
+
# clause-level flag-modifier
|
141
|
+
def dash_symbol(symb)
|
142
|
+
@the_dash_symbol = symb
|
143
|
+
end
|
144
|
+
# clause-level flag-modifier
|
145
|
+
def long_dash_symbol(symb)
|
146
|
+
@the_long_dash_symbol = symb
|
147
|
+
end
|
148
|
+
# clause-level flag-modifier
|
149
|
+
def description(desc)
|
150
|
+
@the_description = desc
|
151
|
+
end
|
152
|
+
# clause-level flag-modifier
|
153
|
+
def compresses(val=true)
|
154
|
+
@the_compresses = val
|
155
|
+
end
|
156
|
+
def as_string_basic
|
157
|
+
"#{ self.the_dash_symbol }#{ self.flag }"
|
158
|
+
end
|
159
|
+
def as_alternate_forms
|
160
|
+
ret = @the_alternate_forms.collect do |x|
|
161
|
+
"#{ self.the_dash_symbol }#{ x }"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
def as_string_extended
|
165
|
+
"#{ self.the_long_dash_symbol }#{self.the_long_form }"
|
166
|
+
end
|
167
|
+
def as_the_form_that_is_actually_used
|
168
|
+
@the_form_that_is_actually_used
|
169
|
+
end
|
170
|
+
def value=(val)
|
171
|
+
@value = val
|
172
|
+
if pbMeth = self.proxied_bound_method
|
173
|
+
pbMeth.call val
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
#----------------------------------------------------------------------------------
|
181
|
+
#--------------------------------------- -------------------
|
182
|
+
#--------------------------------------- A clause is the -------------------
|
183
|
+
#------------- Clause Definitions ----- main statement -------------------
|
184
|
+
#------------ top-level flag-declarer -- within the module -------------------
|
185
|
+
#--------------------------------------- -------------------
|
186
|
+
#----------------------------------------------------------------------------------
|
187
|
+
module OptiFlag
|
188
|
+
module Flagset
|
189
|
+
# top-level flag-declarer
|
190
|
+
def flag(flag_name_pair,hash={},&the_block)
|
191
|
+
if flag_name_pair.class == String or flag_name_pair.class == Symbol
|
192
|
+
flag_name_pair = [flag_name_pair.to_s,flag_name_pair.to_sym]
|
193
|
+
end
|
194
|
+
flag = flag_name_pair[0]
|
195
|
+
if flag_name_pair[1]
|
196
|
+
name = flag_name_pair[1]
|
197
|
+
else
|
198
|
+
name = flag.to_sym
|
199
|
+
end
|
200
|
+
@all_flags ||= {}
|
201
|
+
obj = @all_flags[name]
|
202
|
+
obj ||= EachFlag.new(name,flag,self)
|
203
|
+
obj.instance_eval &the_block if block_given?
|
204
|
+
hash.each_pair do |fxn,val|
|
205
|
+
obj.send(fxn,val)
|
206
|
+
end
|
207
|
+
@all_flags[name] = obj
|
208
|
+
end
|
209
|
+
# top-level flag-declarer
|
210
|
+
def optional_flag(flag_name_pair,hash={},&the_block)
|
211
|
+
flag(flag_name_pair,hash,&the_block)
|
212
|
+
flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
|
213
|
+
name = flag_name_pair[1] || flag_name_pair[0]
|
214
|
+
flag_properties name.to_sym do
|
215
|
+
optional
|
216
|
+
end
|
217
|
+
end
|
218
|
+
# top-level flag-declarer
|
219
|
+
def optional_switch_flag(flag_name_pair,hash={},&the_block)
|
220
|
+
flag(flag_name_pair,hash,&the_block)
|
221
|
+
flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
|
222
|
+
name = flag_name_pair[1] || flag_name_pair[0]
|
223
|
+
flag_properties name.to_sym do
|
224
|
+
optional
|
225
|
+
arity 0
|
226
|
+
end
|
227
|
+
end
|
228
|
+
# top-level flag-declarer
|
229
|
+
def keyword(flag_name_pair,hash={},&the_block)
|
230
|
+
@all_keywords ||= []
|
231
|
+
@all_keywords << name
|
232
|
+
flag(flag_name_pair,hash,&the_block)
|
233
|
+
flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
|
234
|
+
name = flag_name_pair[1] || flag_name_pair[0]
|
235
|
+
flag_properties name.to_sym do
|
236
|
+
dash_symbol ""
|
237
|
+
long_dash_symbol ""
|
238
|
+
arity 0
|
239
|
+
optional
|
240
|
+
end
|
241
|
+
end
|
242
|
+
# top-level flag-declarer
|
243
|
+
def using_object(a_single_object)
|
244
|
+
potential_methods = (a_single_object.methods - Object.methods)
|
245
|
+
require 'enumerator'
|
246
|
+
valid_instance_var = []
|
247
|
+
potential_opt_switch_flags, potential_methods =
|
248
|
+
potential_methods.partition {|x| x =~ /\?$/ }
|
249
|
+
potential_methods.each_slice(2) do |getter,setter|
|
250
|
+
if setter == (getter + "=")
|
251
|
+
valid_instance_var << [getter,setter];
|
252
|
+
end
|
253
|
+
end
|
254
|
+
valid_instance_var.each do |getter,setter|
|
255
|
+
bound_method = a_single_object.method(setter)
|
256
|
+
flag getter do
|
257
|
+
self.proxied_bound_method = bound_method
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
# top-level flag-declarer
|
262
|
+
def usage_flag(*args)
|
263
|
+
first,*rest = args
|
264
|
+
optional_flag [first] do
|
265
|
+
self.for_help = true
|
266
|
+
description "Help"
|
267
|
+
no_args
|
268
|
+
alternate_forms *rest if rest.length > 0
|
269
|
+
end
|
270
|
+
end
|
271
|
+
# top-level flag-declarer
|
272
|
+
def extended_help_flag(*args)
|
273
|
+
first,*rest = args
|
274
|
+
optional_flag [first] do
|
275
|
+
self.for_extended_help = true
|
276
|
+
description "Extended Help"
|
277
|
+
no_args
|
278
|
+
alternate_forms *rest if rest.length > 0
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# top-level flag-declarer
|
282
|
+
def character_flag(switch,group="default",&the_block)
|
283
|
+
throw "Character switches can only be 1 character long" if switch.to_s.length > 1
|
284
|
+
flag(switch.to_sym,&the_block)
|
285
|
+
@group ||= {}
|
286
|
+
the_flag_we_just_added = @all_flags[switch.to_sym]
|
287
|
+
|
288
|
+
key = [group.to_sym,the_flag_we_just_added.the_dash_symbol]
|
289
|
+
# puts "#{ key.join(',') }"
|
290
|
+
@group[key] ||= []
|
291
|
+
@group[key] << the_flag_we_just_added
|
292
|
+
# re-assert ourselves
|
293
|
+
flag [switch.to_s, switch] do
|
294
|
+
optional
|
295
|
+
arity 0
|
296
|
+
compresses
|
297
|
+
end
|
298
|
+
end
|
299
|
+
# top-level flag-declarer
|
300
|
+
def flag_properties(symb,&the_block)
|
301
|
+
raise "Block needed for flag_properties" if not block_given?
|
302
|
+
@all_flags ||= {}
|
303
|
+
obj = @all_flags[symb.to_sym]
|
304
|
+
return if obj==nil
|
305
|
+
obj.instance_eval &the_block if block_given?
|
306
|
+
end
|
307
|
+
alias :properties :flag_properties
|
308
|
+
def argument_order(*args)
|
309
|
+
puts "You want the arguments in this order: #{ args }"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# defining the callable client-interface
|
315
|
+
module OptiFlag
|
316
|
+
module Flagset
|
317
|
+
module NewInterface
|
318
|
+
attr_accessor :errors,:flag_value,:specification_errors,:help_requested_on,:warnings
|
319
|
+
attr_writer :help_requested,:extended_help_requested
|
320
|
+
def errors?
|
321
|
+
self.errors != nil
|
322
|
+
end
|
323
|
+
def warnings?
|
324
|
+
self.warnings != nil
|
325
|
+
end
|
326
|
+
def help_requested?
|
327
|
+
@help_requested != nil
|
328
|
+
end
|
329
|
+
def extended_help_requested?
|
330
|
+
@extended_help_requested !=nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
class Errors
|
334
|
+
attr_accessor :missing_flags,:other_errors,:validation_errors
|
335
|
+
def initialize
|
336
|
+
@missing_flags,@other_errors,@validation_errors = [],[],[]
|
337
|
+
end
|
338
|
+
def any_errors?
|
339
|
+
@missing_flags.length >0 or @other_errors.length >0 or
|
340
|
+
@validation_errors.length > 0
|
341
|
+
end
|
342
|
+
def divulge_problems(output=$stdout)
|
343
|
+
output.puts "Errors found:"
|
344
|
+
if @missing_flags.length >0
|
345
|
+
output.puts "Missing Flags:"
|
346
|
+
@missing_flags.each do |x|
|
347
|
+
output.puts " #{ x }"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
if @other_errors.length >0
|
351
|
+
output.puts "Other Errors:"
|
352
|
+
@other_errors.each do |x|
|
353
|
+
output.puts " #{ x }"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
if @validation_errors.length >0
|
357
|
+
output.puts "Validation Errors:"
|
358
|
+
@validation_errors.each do |x|
|
359
|
+
output.puts " #{ x }"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
def create_new_value_class()
|
365
|
+
klass = Hash.new
|
366
|
+
klass.instance_eval do
|
367
|
+
def init_with_these(all_objs)
|
368
|
+
@all_flags = all_objs
|
369
|
+
end
|
370
|
+
end
|
371
|
+
klass.init_with_these(@all_flags)
|
372
|
+
@all_flags.each_value do |y|
|
373
|
+
# only allow alphabetic symbols to create methods
|
374
|
+
if (y.name.to_s =~ /^[a-zA-Z]+/)
|
375
|
+
klass.instance_eval %{
|
376
|
+
def #{y.name}()
|
377
|
+
@all_flags[:#{ y.name }].value if @all_flags[:#{ y.name }]
|
378
|
+
end
|
379
|
+
def #{y.name}_details()
|
380
|
+
@all_flags[:#{ y.name }] if @all_flags[:#{ y.name }]
|
381
|
+
end}
|
382
|
+
end
|
383
|
+
all_names = [y.name]
|
384
|
+
all_names << y.the_alternate_forms if y.the_alternate_forms.length > 0
|
385
|
+
all_names.flatten!
|
386
|
+
all_names = all_names.select{|x| x.to_s =~ /^[a-zA-Z]+/}
|
387
|
+
all_names.each do |x|
|
388
|
+
klass.instance_eval %{
|
389
|
+
def #{x}?()
|
390
|
+
ret = @all_flags[:#{ y.name }].value
|
391
|
+
return false if ret == nil
|
392
|
+
return @all_flags[:#{ y.name }].value if @all_flags[:#{ y.name }]
|
393
|
+
end}
|
394
|
+
end
|
395
|
+
end
|
396
|
+
return klass
|
397
|
+
end
|
398
|
+
|
399
|
+
end # end of Flagset module
|
400
|
+
end # end of OptiFlag module
|
401
|
+
|
402
|
+
|
403
|
+
#------------------------------------------------------------------------------------------
|
404
|
+
#------------------------------------------------------------------------------------------
|
405
|
+
#------------------------------------------------------------------------------------------
|
406
|
+
#------------- The actual Parsing and Error Finding Code -----------------------
|
407
|
+
#------------------------------------------------------------------------------------------
|
408
|
+
#------------------------------------------------------------------------------------------
|
409
|
+
#------------------------------------------------------------------------------------------
|
410
|
+
module OptiFlag
|
411
|
+
module Flagset
|
412
|
+
def parse(args,clone=true)
|
413
|
+
safe_args = args.clone if clone
|
414
|
+
safe_args = args if ! clone
|
415
|
+
# the following 10 lines were changed so
|
416
|
+
# that a module could reparse a command-line
|
417
|
+
# and not have a global-state change for
|
418
|
+
# everyone... I had mulled just mandating that
|
419
|
+
# a SomeArgModule::parse(ARGV) statement
|
420
|
+
# could only occur once, but since everything else
|
421
|
+
# is getting ugly, might as well allow this
|
422
|
+
# --D.O.E 5/30/2006
|
423
|
+
new_self = self.clone
|
424
|
+
new_flags = {}
|
425
|
+
@all_flags.each_pair do |key,val|
|
426
|
+
val = val.clone
|
427
|
+
new_flags[key] = val
|
428
|
+
end
|
429
|
+
new_self.module_eval do
|
430
|
+
@all_flags = new_flags
|
431
|
+
safe_args = search_for_missing_character_switches(safe_args)
|
432
|
+
|
433
|
+
safe_args = create_api(safe_args)
|
434
|
+
|
435
|
+
safe_args = search_for_missing_flags(safe_args)
|
436
|
+
populate_values(safe_args)
|
437
|
+
now_populate_hash(@all_flags,safe_args)
|
438
|
+
end
|
439
|
+
return safe_args
|
440
|
+
end
|
441
|
+
|
442
|
+
private
|
443
|
+
def now_populate_hash(all_flags,safe_args)
|
444
|
+
all_flags.each_pair do |k,v|
|
445
|
+
safe_args.flag_value[k.to_sym] = v.value
|
446
|
+
v.the_alternate_forms.each do |x|
|
447
|
+
safe_args.flag_value[x.to_sym] = v.value
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
def find_help_flags(safe_args)
|
452
|
+
arg_copy = safe_args.clone
|
453
|
+
|
454
|
+
#first we get rid of all non-help flags...
|
455
|
+
non_help_flags = @all_flags.values.select{|value| !value.for_help }
|
456
|
+
non_help_flags.each do |val|
|
457
|
+
flag_finder_and_stripper(val.as_the_form_that_is_actually_used,val.the_arity,arg_copy)
|
458
|
+
end
|
459
|
+
# ...because the help flag is overloaded... it can have an arity of 0 or 1
|
460
|
+
help_flag = @all_flags.values.select{|value| value.for_help }
|
461
|
+
if help_flag.length > 0
|
462
|
+
flag = help_flag[0].as_the_form_that_is_actually_used
|
463
|
+
|
464
|
+
found,discard = flag_finder_and_stripper(flag,1,arg_copy)
|
465
|
+
if found.length > 0
|
466
|
+
safe_args.help_requested = true
|
467
|
+
if found.length == 2
|
468
|
+
safe_args.help_requested_on = found[1]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
arg_copy = safe_args.clone
|
473
|
+
|
474
|
+
ext_help_flag = @all_flags.values.select{|value| value.for_extended_help }
|
475
|
+
if ext_help_flag.length > 0
|
476
|
+
flag = ext_help_flag[0].as_the_form_that_is_actually_used
|
477
|
+
|
478
|
+
found,discard = flag_finder_and_stripper(flag,0,arg_copy)
|
479
|
+
if found.length > 0
|
480
|
+
safe_args.extended_help_requested = true
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
def flag_finder_and_stripper(flag,arity,args)
|
485
|
+
idx = args.index(flag)
|
486
|
+
if idx == nil
|
487
|
+
return [], args
|
488
|
+
end
|
489
|
+
the_range = idx..(idx + arity)
|
490
|
+
flag_and_values = args[the_range]
|
491
|
+
fragment = args[the_range].clone
|
492
|
+
args[the_range] = nil
|
493
|
+
return fragment, args
|
494
|
+
end
|
495
|
+
def populate_values(safe_args)
|
496
|
+
find_the_flag_that_is_actually_used(safe_args)
|
497
|
+
arg_copy = safe_args.clone
|
498
|
+
@all_flags.values.each do |flag_obj|
|
499
|
+
the_string_flag = flag_obj.as_the_form_that_is_actually_used
|
500
|
+
flag_and_values, arg_copy =
|
501
|
+
flag_finder_and_stripper(the_string_flag,flag_obj.the_arity,arg_copy)
|
502
|
+
if flag_and_values.length >2
|
503
|
+
discard,*theRest = flag_and_values
|
504
|
+
flag_obj.value = theRest
|
505
|
+
end
|
506
|
+
flag_obj.value = flag_and_values[1] if flag_and_values.length ==2
|
507
|
+
flag_obj.value = true if flag_and_values.length == 1 and flag_obj.the_arity == 0
|
508
|
+
if flag_and_values.length == 1 and flag_obj.the_arity >0
|
509
|
+
problem = "Argument(s) missing for flag #{ flag_obj.as_the_form_that_is_actually_used }"
|
510
|
+
safe_args.errors ||= Errors.new
|
511
|
+
safe_args.errors.validation_errors << problem
|
512
|
+
end
|
513
|
+
end
|
514
|
+
if arg_copy.length > 0 # is there anything left over
|
515
|
+
safe_args.warnings ||= []
|
516
|
+
safe_args.warnings <<
|
517
|
+
"There are extra arguments left over: [#{ arg_copy.join(', ') }]. "
|
518
|
+
end
|
519
|
+
validate_values(safe_args)
|
520
|
+
find_help_flags(safe_args)
|
521
|
+
|
522
|
+
return safe_args
|
523
|
+
end
|
524
|
+
def validate_values(safe_args)
|
525
|
+
run_pre_translate(safe_args)
|
526
|
+
#TODO: the two following helper methods
|
527
|
+
# are essentially the same... maybe, we can pull the
|
528
|
+
# logic into the EachFlag object itself...
|
529
|
+
# something to think about (D.O.E. 5/22/2006)
|
530
|
+
validate_values_by_regexp(safe_args)
|
531
|
+
validate_values_by_enumerated_values(safe_args)
|
532
|
+
run_post_translate(safe_args)
|
533
|
+
end
|
534
|
+
def validate_values_by_enumerated_values(safe_args)
|
535
|
+
flags_requiring_validation = @all_flags.values.select do |x|
|
536
|
+
x.position_enumerated_values_validator.length > 0 && x.value
|
537
|
+
end
|
538
|
+
flags_requiring_validation.each do |flag_obj|
|
539
|
+
value = flag_obj.value
|
540
|
+
value = [value] if value.class != Array
|
541
|
+
flag_obj.position_enumerated_values_validator.each_with_index do |enum_vals,idx|
|
542
|
+
something_matches = enum_vals.select{|x| x.to_s == value[idx] }
|
543
|
+
if something_matches.length == 0
|
544
|
+
problem = "For the flag: '#{ flag_obj.as_string_basic }' the value you gave was '#{ value[idx] }'."
|
545
|
+
problem << "\n But the value must be one of the following: [#{ enum_vals.join(', ') }]"
|
546
|
+
flag_obj.validation_error << problem
|
547
|
+
safe_args.errors ||= Errors.new
|
548
|
+
safe_args.errors.validation_errors << problem
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
def validate_values_by_regexp(safe_args)
|
554
|
+
flags_requiring_validation = @all_flags.values.select do |x|
|
555
|
+
x.position_validator.length > 0 && x.value
|
556
|
+
end
|
557
|
+
flags_requiring_validation.each do |flag_obj|
|
558
|
+
value = flag_obj.value
|
559
|
+
value = [value] if value.class != Array
|
560
|
+
flag_obj.position_validator.each_with_index do |(desc,regex),idx|
|
561
|
+
if ! value[idx].match regex
|
562
|
+
problem = "For the flag: '#{ flag_obj.as_string_basic }' the value you gave was '#{ value[idx] }'."
|
563
|
+
problem << "\n #{ desc }"
|
564
|
+
flag_obj.validation_error << problem
|
565
|
+
safe_args.errors ||= Errors.new
|
566
|
+
safe_args.errors.validation_errors << problem
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
def run_pre_translate(safe_args)
|
572
|
+
flags_requiring_pre_translating = @all_flags.values.select do |x|
|
573
|
+
x.the_pretranslate && x.value
|
574
|
+
end
|
575
|
+
standard_translating(flags_requiring_pre_translating,:the_pretranslate)
|
576
|
+
end
|
577
|
+
def run_post_translate(safe_args)
|
578
|
+
flags_requiring_post_translating = @all_flags.values.select do |x|
|
579
|
+
x.the_posttranslate && x.value
|
580
|
+
end
|
581
|
+
standard_translating(flags_requiring_post_translating,:the_posttranslate)
|
582
|
+
end
|
583
|
+
def standard_translating(arr,pre_or_post)
|
584
|
+
arr.each do |flag|
|
585
|
+
flag.send(pre_or_post).each_with_index do |translate,idx|
|
586
|
+
the_value = flag.value
|
587
|
+
the_value = [the_value] if the_value.class != Array
|
588
|
+
if translate.arity > 1
|
589
|
+
retVal = translate.call *the_value
|
590
|
+
retVal ||= []
|
591
|
+
retVal = [retVal] if retVal.class !=Array
|
592
|
+
if retVal.length != translate.arity
|
593
|
+
raise "Error: the translate block you used had #{ translate.arity } arguments, but your block returned #{ retVal.length } values. They must be equal."
|
594
|
+
end
|
595
|
+
flag.value = retVal
|
596
|
+
elsif translate.arity == 1
|
597
|
+
retVal = translate.call(the_value[idx])
|
598
|
+
flag.value[idx] = retVal
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
def find_the_flag_that_is_actually_used(safe_args)
|
604
|
+
there_might_be_errors = safe_args.errors || Errors.new
|
605
|
+
args_copy = safe_args.clone
|
606
|
+
@all_flags.values.each do |x|
|
607
|
+
shortform,longform = x.as_string_basic,x.as_string_extended
|
608
|
+
all_forms = [shortform,longform] + x.as_alternate_forms
|
609
|
+
form_found_mask = all_forms.collect do |form|
|
610
|
+
is_form_found, args_copy =
|
611
|
+
flag_finder_and_stripper(form,x.the_arity,args_copy)
|
612
|
+
[ (is_form_found.length > 0), is_form_found ]
|
613
|
+
end
|
614
|
+
any_found = form_found_mask.select{|(found,parms)| found}
|
615
|
+
if any_found.length > 1
|
616
|
+
there_might_be_errors.other_errors <<
|
617
|
+
"More than one flag form of -- is present. This is ambiguous. Choose one only."
|
618
|
+
end
|
619
|
+
if any_found.length == 1
|
620
|
+
x.the_form_that_is_actually_used = any_found[0][1][0]
|
621
|
+
end
|
622
|
+
end
|
623
|
+
if there_might_be_errors.any_errors?
|
624
|
+
safe_args.errors = there_might_be_errors
|
625
|
+
end
|
626
|
+
return safe_args
|
627
|
+
end
|
628
|
+
def search_for_missing_character_switches(safe_args)
|
629
|
+
these_args = safe_args.clone
|
630
|
+
return safe_args if @group == nil
|
631
|
+
chars_found_for_this_group = {}
|
632
|
+
all_chars = ""
|
633
|
+
@group.each_pair do |k,val|
|
634
|
+
name_of_flag = val.collect{|x| x.name}
|
635
|
+
all_chars_alphabetical = name_of_flag.join('').unpack('c*').sort.pack('c*')
|
636
|
+
args_namespaced = these_args.select{|x| x.match("^#{ k[1] }") }
|
637
|
+
seems_to_match = []
|
638
|
+
args_namespaced.each do |flag|
|
639
|
+
flag_value = flag.match("^#{ k[1]}").post_match
|
640
|
+
potential = all_chars_alphabetical.tr(flag_value,"")
|
641
|
+
if potential.length == all_chars_alphabetical.length - flag_value.length
|
642
|
+
seems_to_match << flag_value
|
643
|
+
all_chars_alphabetical = all_chars_alphabetical.tr(flag_value,"")
|
644
|
+
these_args = these_args - [flag]
|
645
|
+
end
|
646
|
+
end
|
647
|
+
seems_to_match = seems_to_match.flatten.join('')
|
648
|
+
chars_found_for_this_group[k] = seems_to_match
|
649
|
+
all_chars << seems_to_match
|
650
|
+
end
|
651
|
+
|
652
|
+
all_chars.split(//).each do |x|
|
653
|
+
opt_flag = @all_flags[x.to_sym]
|
654
|
+
opt_flag.value = true
|
655
|
+
end
|
656
|
+
|
657
|
+
return safe_args
|
658
|
+
end
|
659
|
+
|
660
|
+
def search_for_missing_flags(safe_args)
|
661
|
+
there_might_be_errors = Errors.new
|
662
|
+
required_flags = @all_flags.values.sort{|x,y| x.ordered_added <=> y.ordered_added }.select{|x| x.the_is_required }
|
663
|
+
args_copy = safe_args.clone
|
664
|
+
required_flags.each do |x|
|
665
|
+
shortform,longform = x.as_string_basic,x.as_string_extended
|
666
|
+
all_forms = [shortform,longform] + x.as_alternate_forms
|
667
|
+
|
668
|
+
form_found_mask = all_forms.collect do |form|
|
669
|
+
is_form_found, args_copy =
|
670
|
+
flag_finder_and_stripper(form,x.the_arity,args_copy)
|
671
|
+
[ (is_form_found.length > 0), is_form_found ]
|
672
|
+
end
|
673
|
+
is_first_found,is_second_found = form_found_mask
|
674
|
+
any_found = form_found_mask.select{|(found,parms)| found}
|
675
|
+
if any_found.length == 0
|
676
|
+
there_might_be_errors.missing_flags << x.as_string_basic
|
677
|
+
end
|
678
|
+
if is_second_found[0] && is_first_found[0]
|
679
|
+
there_might_be_errors.other_errors <<
|
680
|
+
"Both forms #{ x.as_string_basic } and #{x.as_string_extended } are present. This is ambiguous. Choose one only."
|
681
|
+
end
|
682
|
+
if any_found.length == 1
|
683
|
+
x.the_form_that_is_actually_used = any_found[0][1][0]
|
684
|
+
end
|
685
|
+
end
|
686
|
+
if there_might_be_errors.any_errors?
|
687
|
+
safe_args.errors = there_might_be_errors
|
688
|
+
end
|
689
|
+
return safe_args
|
690
|
+
end
|
691
|
+
def create_api(safe_args)
|
692
|
+
|
693
|
+
safe_args.extend NewInterface
|
694
|
+
safe_args.flag_value = create_new_value_class()
|
695
|
+
return safe_args
|
696
|
+
end
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
module OptiFlag
|
701
|
+
def OptiFlag.Flagset(hash)
|
702
|
+
# changed this from just returning Flagset...
|
703
|
+
# Reason Being: a user can specify two modules in one file
|
704
|
+
# one with this method, and one just using Flagset...
|
705
|
+
# if you don't clone at this point, you are left with
|
706
|
+
# a global change... BUT to get the cloning working
|
707
|
+
# I had to do some singleton_class trickeration as part of
|
708
|
+
# the initialize method for EachFlag... I am not
|
709
|
+
# 100% sure I understand what I just did. -- D.O.E 5/30/06
|
710
|
+
mod = Flagset.clone
|
711
|
+
hash.each_pair do |symb,val|
|
712
|
+
mod.send(symb.to_sym,val)
|
713
|
+
end
|
714
|
+
return mod
|
715
|
+
end
|
716
|
+
|
717
|
+
end
|
718
|
+
|
719
|
+
module OptiFlag
|
720
|
+
module Flagset
|
721
|
+
def handle_errors_and_help(level=:not_strict)
|
722
|
+
return if !@all_flags
|
723
|
+
parse(ARGV,false)
|
724
|
+
if ARGV.help_requested?
|
725
|
+
if !ARGV.help_requested_on
|
726
|
+
show_help
|
727
|
+
elsif the_on = ARGV.help_requested_on
|
728
|
+
show_individual_extended_help(the_on.to_sym)
|
729
|
+
end
|
730
|
+
exit
|
731
|
+
end
|
732
|
+
if ARGV.extended_help_requested?
|
733
|
+
show_extended_help
|
734
|
+
exit
|
735
|
+
end
|
736
|
+
if ARGV.errors?
|
737
|
+
ARGV.errors.divulge_problems
|
738
|
+
show_help
|
739
|
+
exit
|
740
|
+
end
|
741
|
+
if ARGV.warnings? and level == :with_no_warnings
|
742
|
+
puts "In strict warning handling mode. Warnings will cause process to exit."
|
743
|
+
ARGV.warnings.each do |x|
|
744
|
+
puts " #{ x }"
|
745
|
+
end
|
746
|
+
puts "Please fix these warnings and try again."
|
747
|
+
exit
|
748
|
+
end
|
749
|
+
ARGV
|
750
|
+
end # end of method handle_errors_and_help
|
751
|
+
end # end of Flagset
|
752
|
+
end # end of OptiFlag
|
753
|
+
|
754
|
+
|
755
|
+
|
756
|
+
# Specification error possibilities (at FlagsetDefinition)
|
757
|
+
# 1) a value_matches and a value_in_set on the same argument
|
758
|
+
# 2) ambiguous flag names (e.g. two flags both with name -help)
|
759
|
+
# 3) short symbol flag and long symbol flags match each other
|
760
|
+
|
761
|
+
# Specification error possibilities (at DefinitionSwitcher)
|
762
|
+
# 1)
|
763
|
+
# 2)
|
764
|
+
|
765
|
+
# Warning conditions
|
766
|
+
# 1) Left-over arguments
|
767
|
+
|
768
|
+
|
769
|
+
#---------------------------------------------------------------------------------------------------------------
|
770
|
+
#---------------------------------------------------------------------------------------------------------------
|
771
|
+
#---------------------------------------------------------------------------------------------------------------
|
772
|
+
#---------------------------------- help and error rendering stuff --------------------------------------------
|
773
|
+
#---------------------------------------------------------------------------------------------------------------
|
774
|
+
#---------------------------------------------------------------------------------------------------------------
|
775
|
+
#---------------------------------------------------------------------------------------------------------------
|
776
|
+
module OptiFlag
|
777
|
+
module Flagset
|
778
|
+
module Help
|
779
|
+
class Bundle
|
780
|
+
attr_accessor :help,:extended_help,:banner;
|
781
|
+
end
|
782
|
+
module StandardHelpBundle
|
783
|
+
STANDARD_HELP_BANNER = proc do |render_on|
|
784
|
+
render_on.printf(" %-10s %-15s %5s\n","Flag","Name","Is Required?")
|
785
|
+
end
|
786
|
+
STANDARD_HELP = proc do |render_on,short,long,dash_short,dash_long,required_or_not,description|
|
787
|
+
render_on.printf(" #{ dash_short }%-10s %-15s %5s\n",
|
788
|
+
short,short,required_or_not)
|
789
|
+
end
|
790
|
+
STANDARD_EXTENDED_HELP = proc do |render_on,short,long,dash_short,dash_long,required_or_not,description,arity,alternate_forms,name|
|
791
|
+
render_on.puts "--------------------------------"
|
792
|
+
desc = <<-EOF
|
793
|
+
Name: #{ name }
|
794
|
+
Simple Flag: #{ dash_short }#{ short }
|
795
|
+
Required?: #{ required_or_not }
|
796
|
+
# of Arguments: #{ (arity > 0) ? arity : 'None' }
|
797
|
+
EOF
|
798
|
+
desc << <<-EOF if description
|
799
|
+
Description: #{ description }
|
800
|
+
EOF
|
801
|
+
desc << <<-EOF if long
|
802
|
+
Long Form Flag: #{ dash_long }#{ long }
|
803
|
+
EOF
|
804
|
+
desc << <<-EOF if alternate_forms.length > 0
|
805
|
+
Alternate Flags: #{ dash_short }[#{ alternate_forms.join(', ') }]
|
806
|
+
EOF
|
807
|
+
render_on.puts desc
|
808
|
+
end
|
809
|
+
end
|
810
|
+
end
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
|
815
|
+
module OptiFlag
|
816
|
+
module Flagset
|
817
|
+
attr_accessor :registered_help,:help_banner,:registered_extended_help;
|
818
|
+
def register_bundle(bundle)
|
819
|
+
self.registered_extended_help = bundle.extended_help
|
820
|
+
self.registered_help = bundle.help
|
821
|
+
self.help_banner = bundle.banner
|
822
|
+
end
|
823
|
+
|
824
|
+
def registered_help; Help::StandardHelpBundle::STANDARD_HELP; end
|
825
|
+
def registered_extended_help; Help::StandardHelpBundle::STANDARD_EXTENDED_HELP; end
|
826
|
+
def help_banner; Help::StandardHelpBundle::STANDARD_HELP_BANNER; end
|
827
|
+
|
828
|
+
def render_help(stdo=$stdout)
|
829
|
+
help = self.registered_help
|
830
|
+
help_banner = self.help_banner
|
831
|
+
|
832
|
+
help_banner.call stdo
|
833
|
+
@all_flags.each_pair do |key,val|
|
834
|
+
help.call stdo,val.flag,val.the_long_form,val.the_dash_symbol,val.the_long_dash_symbol,val.the_is_required,val.the_description
|
835
|
+
end
|
836
|
+
end
|
837
|
+
def show_help(start_message="",stdo=$stdout)
|
838
|
+
stdo.puts start_message
|
839
|
+
render_help(stdo)
|
840
|
+
@all_keywords ||= []
|
841
|
+
@all_keywords.each do |x|
|
842
|
+
stdo.puts "Flagless Argument: #{ x }"
|
843
|
+
end
|
844
|
+
end
|
845
|
+
def show_extended_help(start_message="",stdo=$stdout)
|
846
|
+
show_help(start_message,stdo)
|
847
|
+
@all_flags.keys.each do |name|
|
848
|
+
show_individual_extended_help(name,stdo)
|
849
|
+
end
|
850
|
+
end
|
851
|
+
def show_individual_extended_help(name,stdo=$stdout)
|
852
|
+
x = @all_flags[name]
|
853
|
+
return if !x
|
854
|
+
extended_help = self.registered_extended_help
|
855
|
+
extended_help.call stdo, x.flag, x.the_long_form,x.the_dash_symbol,x.the_long_dash_symbol,
|
856
|
+
x.the_is_required,x.the_description,x.the_arity,x.the_alternate_forms,x.name
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|