optiflag 0.6 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/ReleaseNotes.txt +35 -0
  2. data/doc/classes/OptiFlag.html +439 -0
  3. data/doc/classes/OptiFlag.src/M000001.html +49 -0
  4. data/doc/classes/OptiFlag.src/M000002.html +49 -0
  5. data/doc/classes/OptiFlag.src/M000003.html +30 -0
  6. data/doc/classes/OptiFlag/Flagset.html +459 -0
  7. data/doc/classes/OptiFlag/Flagset.src/M000003.html +45 -0
  8. data/doc/classes/OptiFlag/Flagset.src/M000004.html +45 -0
  9. data/doc/classes/OptiFlag/Flagset.src/M000005.html +18 -0
  10. data/doc/classes/OptiFlag/Flagset.src/M000006.html +20 -0
  11. data/doc/classes/OptiFlag/Flagset.src/M000007.html +35 -0
  12. data/doc/classes/OptiFlag/Flagset.src/M000008.html +23 -0
  13. data/doc/classes/OptiFlag/Flagset.src/M000009.html +24 -0
  14. data/doc/classes/OptiFlag/Flagset.src/M000010.html +28 -0
  15. data/doc/classes/OptiFlag/Flagset.src/M000011.html +20 -0
  16. data/doc/classes/OptiFlag/Flagset.src/M000012.html +24 -0
  17. data/doc/classes/OptiFlag/Flagset.src/M000013.html +24 -0
  18. data/doc/classes/OptiFlag/Flagset.src/M000014.html +32 -0
  19. data/doc/classes/OptiFlag/Flagset.src/M000015.html +25 -0
  20. data/doc/classes/OptiFlag/Flagset.src/M000016.html +53 -0
  21. data/doc/classes/OptiFlag/Flagset.src/M000017.html +53 -0
  22. data/doc/classes/OptiFlag/Flagset/EachFlag.html +930 -0
  23. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000024.html +36 -0
  24. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000025.html +18 -0
  25. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000026.html +36 -0
  26. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000027.html +36 -0
  27. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000028.html +18 -0
  28. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000029.html +18 -0
  29. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000030.html +19 -0
  30. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000031.html +19 -0
  31. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000032.html +18 -0
  32. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000033.html +18 -0
  33. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000034.html +18 -0
  34. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000035.html +22 -0
  35. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000036.html +19 -0
  36. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000037.html +18 -0
  37. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000038.html +18 -0
  38. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000039.html +18 -0
  39. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000040.html +18 -0
  40. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000041.html +18 -0
  41. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000042.html +18 -0
  42. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000043.html +19 -0
  43. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000044.html +18 -0
  44. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000045.html +19 -0
  45. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000046.html +18 -0
  46. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000047.html +18 -0
  47. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000048.html +18 -0
  48. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000049.html +18 -0
  49. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000050.html +18 -0
  50. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000051.html +18 -0
  51. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000052.html +20 -0
  52. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000053.html +18 -0
  53. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000054.html +18 -0
  54. data/doc/classes/OptiFlag/Flagset/EachFlag.src/M000055.html +22 -0
  55. data/doc/classes/OptiFlag/Flagset/Errors.html +192 -0
  56. data/doc/classes/OptiFlag/Flagset/Errors.src/M000021.html +18 -0
  57. data/doc/classes/OptiFlag/Flagset/Errors.src/M000022.html +19 -0
  58. data/doc/classes/OptiFlag/Flagset/Errors.src/M000023.html +18 -0
  59. data/doc/classes/OptiFlag/Flagset/Errors.src/M000024.html +18 -0
  60. data/doc/classes/OptiFlag/Flagset/Errors.src/M000025.html +19 -0
  61. data/doc/classes/OptiFlag/Flagset/Errors.src/M000026.html +36 -0
  62. data/doc/classes/OptiFlag/Flagset/Help.html +125 -0
  63. data/doc/classes/OptiFlag/Flagset/Help/Bundle.html +160 -0
  64. data/doc/classes/OptiFlag/Flagset/Help/Bundle.src/M000018.html +18 -0
  65. data/doc/classes/OptiFlag/Flagset/Help/Bundle.src/M000019.html +18 -0
  66. data/doc/classes/OptiFlag/Flagset/Help/StandardHelpBundle.html +118 -0
  67. data/doc/classes/OptiFlag/Flagset/NewInterface.html +296 -0
  68. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000017.html +18 -0
  69. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000018.html +18 -0
  70. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000019.html +18 -0
  71. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000020.html +18 -0
  72. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000021.html +18 -0
  73. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000022.html +18 -0
  74. data/doc/classes/OptiFlag/Flagset/NewInterface.src/M000023.html +18 -0
  75. data/doc/created.rid +1 -0
  76. data/doc/files/optiflag-help_rb.html +101 -0
  77. data/doc/files/optiflag-parse_rb.html +101 -0
  78. data/doc/files/optiflag_rb.html +167 -0
  79. data/doc/files/optiflag_rb.src/M000001.html +18 -0
  80. data/doc/fr_class_index.html +34 -0
  81. data/doc/fr_file_index.html +29 -0
  82. data/doc/fr_method_index.html +82 -0
  83. data/doc/index.html +24 -0
  84. data/doc/rdoc-style.css +208 -0
  85. data/examples/example_1.rb +21 -0
  86. data/examples/example_1_1.rb +21 -0
  87. data/{doc/example → examples}/example_1_2.rb +8 -8
  88. data/examples/example_2.rb +31 -0
  89. data/{doc/example → examples}/example_2_1.rb +4 -4
  90. data/{doc/example → examples}/example_2_2.rb +7 -5
  91. data/{doc/example → examples}/example_2_3.rb +9 -9
  92. data/{doc/example → examples}/example_2_4.rb +4 -4
  93. data/{doc/example → examples}/example_2_5.rb +4 -4
  94. data/{doc/example → examples}/example_2_6.rb +6 -6
  95. data/{doc/example → examples}/example_3.rb +7 -6
  96. data/examples/example_6.rb +20 -0
  97. data/{doc/example → examples}/example_7.rb +5 -4
  98. data/examples/example_8.rb +26 -0
  99. data/optiflag-help.rb +42 -0
  100. data/optiflag-parse.rb +281 -0
  101. data/optiflag.gemspec +4 -3
  102. data/optiflag.rb +972 -0
  103. data/quick.rb +12 -0
  104. data/{test → testcases}/tc_advanced_usage_helping.rb +4 -0
  105. data/{test → testcases}/tc_basic_alternate_forms.rb +0 -0
  106. data/{test → testcases}/tc_basic_char_flags.rb +12 -12
  107. data/{test → testcases}/tc_basic_optional_flag.rb +0 -0
  108. data/{test → testcases}/tc_basic_parsing.rb +0 -0
  109. data/{test → testcases}/tc_basic_usage_helping.rb +0 -0
  110. data/{test → testcases}/tc_basic_value_validation.rb +0 -0
  111. data/{test → testcases}/tc_bug_one.rb +0 -0
  112. data/{test → testcases}/tc_bug_two.rb +0 -0
  113. data/{test → testcases}/tc_change_symbols.rb +0 -0
  114. data/{test → testcases}/tc_enumerated_value_validation.rb +0 -0
  115. data/{test → testcases}/tc_flagall.rb +4 -0
  116. data/{test → testcases}/tc_flagless_arg.rb +0 -0
  117. data/{test → testcases}/tc_keyword.rb +0 -0
  118. data/testcases/tc_new_basic_parsing.rb +51 -0
  119. data/testcases/tc_poro_tester.rb +30 -0
  120. data/{test → testcases}/tc_values_as_hash.rb +0 -0
  121. metadata +152 -52
  122. data/doc/example/example_1.rb +0 -30
  123. data/doc/example/example_1_1.rb +0 -27
  124. data/doc/example/example_2.rb +0 -28
  125. data/doc/example/example_4.rb +0 -12
  126. data/doc/example/example_5.rb +0 -26
  127. data/doc/example/example_6.rb +0 -17
  128. data/lib/optiflag.rb +0 -860
data/optiflag.gemspec CHANGED
@@ -1,13 +1,14 @@
1
1
  require 'rake'
2
2
  Gem::Specification.new do |spec|
3
3
  spec.name = "optiflag"
4
- spec.version = "0.6"
5
- spec.summary = "Optiflag is yet-another library for handling command-line options in an arbitrary Ruby program."
4
+ spec.require_path = '.'
5
+ spec.version = "0.6.5"
6
+ spec.summary = "OptiFlag is an embeddable DSL library for declaring and using command-line options/flags in any Ruby program."
6
7
  spec.author = "Daniel Eklund"
7
8
  spec.email = "doeklund@gmail.com"
8
9
  spec.homepage = "http://rubyforge.org/projects/optiflag/"
9
10
  spec.files = FileList['**/*'].to_a
10
- spec.test_files = FileList['test/tc*'].to_a - ["test/tc_flagall.rb"]
11
+ spec.test_files = FileList['testcases/tc*'].to_a - ["testcases/tc_flagall.rb"]
11
12
  spec.has_rdoc = true
12
13
  spec.rubyforge_project = 'optiflag'
13
14
  end
data/optiflag.rb ADDED
@@ -0,0 +1,972 @@
1
+ # == OptiFlag "Command Line DSL" Parser
2
+ # Please see:
3
+ # http://optiflag.rubyforge.org
4
+ # for useful examples and discussion.
5
+ #
6
+ # Author:: Daniel O. Eklund
7
+ # Copyright:: Copyright (c) 2006 Daniel O. Eklund. All rights reserved.
8
+ # License:: Ruby license.
9
+ module OptiFlag
10
+ VERSION = "0.6.5"
11
+ end
12
+
13
+ load 'optiflag-help.rb'
14
+ load 'optiflag-parse.rb'
15
+
16
+ module OptiFlag
17
+ module Flagset
18
+ @dash_symbol = "-"
19
+ attr_reader :dash_symbol
20
+ module_function :dash_symbol
21
+ # method called by 'send' using a hash of
22
+ # values, the key being the name
23
+ # of the method (this method) and the
24
+ # value being the parameter to be passed
25
+ # to the method. Therefore,
26
+ # module Example extend OptiFlagSet(:flag_symbol => "/")
27
+ # is invoking this method 'flag_symbol' and passing
28
+ # the value "/" as the parameter.
29
+ # See the RDoc for the *method* 'OptiFlag::Flagset()'
30
+ # in the OptiFlag module,
31
+ # *not* the module OptiFlag::Flagset which it resembles.
32
+ #
33
+ # This method, when invoked using the above expression
34
+ # changes the default flag symbol from "-" to whatever
35
+ # is passed in. Thus, if we wanted to simulate
36
+ # the MSDos flags, we would use:
37
+ # module Example extend OptiFlagSet(:flag_symbol => "/")
38
+ # which would then parse a command line looking like:
39
+ # /h /renew /username daniel /password fluffy
40
+ def self.flag_symbol(val)
41
+ @dash_symbol = val
42
+ end
43
+ def self.increment_order_counter()
44
+ @counter ||= 0
45
+ @counter = @counter + 1
46
+ return @counter -1
47
+ end
48
+ end
49
+ end
50
+
51
+ module OptiFlag
52
+ module Flagset
53
+ # The class *EachFlag* is the template for each flag
54
+ # instantiated by a <i>flag set declaration</i>.
55
+ # One EachFlag is instantiated for each <i>flag
56
+ # declaration</i>. For example, the following <i>flag-set
57
+ # declaration</i>:
58
+ #
59
+ # module Example extend OptiFlagSet
60
+ # flag "dir"
61
+ # optional_flag "log"
62
+ # flag "username"
63
+ # flag "password"
64
+ # end
65
+ #
66
+ # will instantiate four EachFlag instances.
67
+ #
68
+ # The <i>flag declarations</i> are methods on the OptiFlag::Flagset
69
+ # module that actually instantiate each
70
+ # one of these EachFlag instances. They are listed in the
71
+ # RDoc for the OptiFlag::Flagset sub-module and are labeled
72
+ # as top-level flag-declarers.
73
+ #
74
+ # An EachFlag object has many methods belonging to
75
+ # different categories of usage (though nothing enforced
76
+ # by the language or the code itself).
77
+ #
78
+ # == Clause Level Modifiers
79
+ # These are methods that are used by the API _user_ of the
80
+ # OptiFlag suite,(i.e. a programmer of other Ruby code but not
81
+ # the OptiFlag code itself). These methods are used *within*
82
+ # a <i>flag declaration</i>. For example, in the following code:
83
+ #
84
+ # module Example extend OptiFlagSet
85
+ # flag "dir" do
86
+ # alternate_forms "directory","D","d"
87
+ # description "The Appliction Directory"
88
+ # end
89
+ # optional_flag "log" do
90
+ # description "The directory in which to find the log files"
91
+ # long_form "logging-directory" # long form is keyed after the '--' symbol
92
+ # end
93
+ # end
94
+ #
95
+ # There will be two EachFlag instances instantiated. For the
96
+ # first EachFlag instance, the one named "dir", two of these
97
+ # <i>clause-level modifiers</i> are invoked *on* the Eachflag instance
98
+ # by the OptiFlag *user*. These clause-level modifiers modify the
99
+ # expected behavior of each
100
+ #
101
+ # == List (clause API) of <i>clause level modifiers</i>
102
+ # * 'description' -- the description of the
103
+ # * 'required' -- indicates whether this flag is required
104
+ #
105
+ # == Internal Implementation Notes
106
+ # This RDoc section is of concern only to implementors
107
+ # of OptiFlag. If you are using OptiFlag in your
108
+ # application, chances are this section is of little use to you.
109
+ #
110
+ # === 'the' members.
111
+ # Because OptiFlag seeks to provide a nice DSL to the user,
112
+ # many of the names of the clause-level modifiers are also
113
+ # useful names for methods which could access the internal
114
+ # field.
115
+ #
116
+ # So, we have a problem. For instance, in the following
117
+ # code:
118
+ # module Example extend OptiFlagSet
119
+ # flag "username", :description => "Database username." # alternate form
120
+ #
121
+ # and_process!
122
+ # end
123
+ # the method 'description' has been written to modify the EachClass
124
+ # instance appropriately. But now, if you are accessing this
125
+ # EachClass instance in some other part of the OptiFlag internals
126
+ # (as, for instance, the help functionality would), then the use
127
+ # of the attr syntax would clash with this method.
128
+ # attr_accessor :description
129
+ # # this meta-code would generate both the
130
+ # # getter and the setter, where the getter method
131
+ # # def description()
132
+ # # return @desciption
133
+ # # end
134
+ # # would conflict with the description method we
135
+ # # have provided for the user
136
+ # The solution to this problem is to leave the standard (useful)
137
+ # method names for the <i>clause level modifiers</i> of the
138
+ # EachFlag instance and introduce another *consistent* name
139
+ # for <b>the actual</b> internal field.
140
+ # For a while, these internal fields and their attr's were
141
+ # named the_actual_description etc. But this proved to
142
+ # be a mouthful. Thereafter, the consistent naming scheme was
143
+ # to place 'the_' in front of the field.
144
+ #
145
+ # Therefore, for most of the data based <i>clause level modifiers</i>
146
+ # (live 'description' which saves the description the user
147
+ # has provided), there is provided a parallel 'the_description'
148
+ # accessor ( or reader or writer, depending on the needs).
149
+ #
150
+ # In summary, 'description' is a public API (for use by
151
+ # users of the OptiFlag suite) and 'the_description' is a
152
+ # a package-protected API.
153
+ class EachFlag
154
+ attr_reader :name, :flag, :order_added,:validation_error,:enclosing_module,:default_used
155
+ # the 'the' values.. the actual means by which to access the values
156
+ # set by the clause-level modifiers.
157
+ attr_reader :the_pretranslate,:the_posttranslate,
158
+ :the_posttranslate_all,:the_pretranslate_all,:the_description,:the_long_dash_symbol,:the_dash_symbol,
159
+ :the_arity,:the_long_form,:the_alternate_forms,:the_validation_rules,
160
+ :the_is_required
161
+ attr_writer :the_form_that_is_actually_used
162
+ attr_accessor :value,:for_help,:for_extended_help,:proxied_bound_method
163
+ def initialize(name,flag,enclosing_module)
164
+ # these next two lines are a highly complicated hack needed to make
165
+ # the use of two module definitions in one file, one with a flag_symbol
166
+ # and one without.. See tc_change_symbols.rb for the two tests that used to
167
+ # cause the problem... also see the changes as part of the definition of
168
+ # OptiFlag.Flagset().... -- D.O.E 5/30/06
169
+ # Search for 'def OptiFlag.Flagset(hash)' in this
170
+ singleton_class_of_enclosing_module = class << enclosing_module; self; end;
171
+ x = singleton_class_of_enclosing_module.included_modules.select do |x|
172
+ (x.to_s =~ /OptiF/) or (x.to_s =~ /#<Modu/)
173
+ end[0]
174
+ # the following batch of code is pure initialization
175
+ @the_compresses,@enclosing_module = false,enclosing_module
176
+ @validation_error,@the_validation_rules = [],[]
177
+ @order_added = OptiFlag::Flagset::increment_order_counter()
178
+ @name,@flag,@the_long_form = name,flag,flag
179
+ @the_dash_symbol, @the_arity, @the_alternate_forms,
180
+ @the_is_required, @the_long_dash_symbol = x.dash_symbol, 1, [], true, "--"
181
+ # puts "#{ @order_added } --- #{ @name }"
182
+ end
183
+ # translate() is a clause-level flag-modifier.
184
+ # It can be used in the following form:
185
+ #
186
+ def translate(position=0,&theblock)
187
+ pretranslate(position,&theblock)
188
+ end
189
+ # clause-level flag-modifier
190
+ def translate_all(&theblock)
191
+ pretranslate_all(&theblock)
192
+ end
193
+ # clause-level flag-modifier
194
+ def pretranslate(position=0,&theblock)
195
+ @the_pretranslate ||= []
196
+ @the_pretranslate[position] = theblock
197
+ end
198
+ # clause-level flag-modifier
199
+ def posttranslate(position=0,&theblock)
200
+ @the_posttranslate ||= []
201
+ @the_posttranslate[position] = theblock
202
+ end
203
+ # clause-level flag-modifier
204
+ def pretranslate_all(&theblock)
205
+ @the_pretranslate_all = theblock
206
+ end
207
+ # clause-level flag-modifier
208
+ def posttranslate_all(&theblock)
209
+ @the_posttranslate_all = theblock
210
+ end
211
+ # clause-level flag-modifier
212
+ def is_required(yes_or_no)
213
+ @the_is_required = yes_or_no
214
+ end
215
+ # clause-level flag-modifier
216
+ def value_matches(desc_regexp_pair,arg_position=0)
217
+ if desc_regexp_pair.class == Regexp
218
+ desc_regexp_pair =
219
+ ["This value does not match the pattern: #{ desc_regexp_pair.source }",
220
+ desc_regexp_pair]
221
+ end
222
+ validates_against do |flag,errors|
223
+ value = [flag.value] if value.class != Array
224
+ desc,regexp = desc_regexp_pair
225
+ if ! value[arg_position].match regexp
226
+ problem = "For the flag: '#{ flag.as_string_basic }' the value you gave was '#{ value[arg_position] }'."
227
+ problem << "\n #{ desc }"
228
+ errors << problem
229
+ end
230
+ end
231
+ end
232
+ # clause-level flag-modifier
233
+ def value_in_set(array_of_acceptable_values,arg_position=0)
234
+ # refactored to use validates_against
235
+ validates_against do |flag,errors|
236
+ value = [flag.value] if value.class != Array
237
+ something_matches =
238
+ array_of_acceptable_values.select{|x| x.to_s == value[arg_position] }
239
+ if something_matches.length == 0
240
+ problem = <<-PROBLEMO
241
+ For the flag: '#{ flag.as_string_basic }' the value you gave was '#{ value[arg_position] }'.
242
+ But the value must be one of the following: [#{ array_of_acceptable_values.join(', ') }]
243
+ PROBLEMO
244
+ errors << problem
245
+ end
246
+ end
247
+ end
248
+ # <b>Clause-level modifier.</b>
249
+ # The user of this construct will pass a block that accepts two arguments:
250
+ # * the flag (of class EachFlag) and
251
+ # * an errors array.
252
+ # If the user wants to indicate that a validation error has occurred
253
+ # (and that further processing should stop) he/she needs to add a
254
+ # string of the problem to the errrors array.<br/>
255
+ # The following (silly) example validates that the username is 'daniel'.
256
+ # If it's not, the code adds to the errors array, and OptiFlag will
257
+ # indicate a validation error.
258
+ # flag "username"
259
+ # description "The username"
260
+ # validates_against do |flag,errors|
261
+ # if flag.value != "daniel"
262
+ # errors << "You are NOT DANIEL!"
263
+ # end
264
+ # end
265
+ # end
266
+ def validates_against(&theblock)
267
+ @the_validation_rules ||= []
268
+ @the_validation_rules << theblock
269
+ end
270
+ # clause-level flag-modifier
271
+ def required
272
+ is_required(true)
273
+ end
274
+ # clause-level flag-modifier
275
+ def optional
276
+ is_required(false)
277
+ end
278
+ # * Clause-level flag-modifier.
279
+ # * Synonym for 'no_args'.
280
+ # * Takes no arguments (for itself).
281
+ # * Indicates that the flag takes no arguments. (i.e.
282
+ # the flag has zero arity).
283
+ # * Sample usage (embedded within a <i>flag set declaration</i>):
284
+ # flag "log" do
285
+ # no_arg
286
+ # end
287
+ def no_arg
288
+ no_args
289
+ end
290
+ # Clause-level flag-modifier.
291
+ def no_args
292
+ arity 0
293
+ end
294
+ # clause-level flag-modifier
295
+ def one_arg
296
+ arity 1
297
+ end
298
+ # clause-level flag-modifier
299
+ def two_args
300
+ arity 2
301
+ end
302
+ # clause-level flag-modifier
303
+ def alternate_forms(*args)
304
+ @the_alternate_forms = *args if args[0].class == Array
305
+ @the_alternate_forms = @the_alternate_forms + args if args[0].class != Array
306
+ end
307
+ # clause-level flag-modifier
308
+ def long_form(form)
309
+ @the_long_form = form
310
+ end
311
+
312
+ # clause-level flag-modifier
313
+ def default(default_value)
314
+ @default_used = true
315
+ @value = default_value
316
+ end
317
+
318
+ # clause-level flag-modifier
319
+ def arity(num)
320
+ @the_arity = num
321
+ end
322
+ # clause-level flag-modifier
323
+ def dash_symbol(symb)
324
+ @the_dash_symbol = symb
325
+ end
326
+ # clause-level flag-modifier
327
+ def long_dash_symbol(symb)
328
+ @the_long_dash_symbol = symb
329
+ end
330
+ # clause-level flag-modifier
331
+ def description(desc)
332
+ @the_description = desc
333
+ end
334
+ # clause-level flag-modifier
335
+ def compresses(val=true)
336
+ @the_compresses = val
337
+ end
338
+ def as_string_basic
339
+ "#{ self.the_dash_symbol }#{ self.flag }"
340
+ end
341
+ def as_alternate_forms
342
+ ret = @the_alternate_forms.collect do |x|
343
+ "#{ self.the_dash_symbol }#{ x }"
344
+ end
345
+ end
346
+ def as_string_extended
347
+ "#{ self.the_long_dash_symbol }#{self.the_long_form }"
348
+ end
349
+ def as_the_form_that_is_actually_used
350
+ @the_form_that_is_actually_used
351
+ end
352
+ def value=(val)
353
+ @default_used = false
354
+ @value = val
355
+ if pbMeth = self.proxied_bound_method
356
+ pbMeth.call val
357
+ end
358
+ end
359
+ alias :with_long_form :long_form
360
+ end # end of EachFlag class
361
+ end
362
+ end
363
+
364
+ module OptiFlag
365
+ # testing to see if this shows (1)
366
+ module Flagset
367
+ # top-level flag-declarer
368
+ def flag(flag_name_pair,hash={},&the_block)
369
+ if flag_name_pair.class == String or flag_name_pair.class == Symbol
370
+ flag_name_pair = [flag_name_pair.to_s,flag_name_pair.to_sym]
371
+ end
372
+ flag = flag_name_pair[0]
373
+ if flag_name_pair[1]
374
+ name = flag_name_pair[1]
375
+ else
376
+ name = flag.to_sym
377
+ end
378
+ @all_flags ||= {}
379
+ obj = @all_flags[name]
380
+ obj ||= EachFlag.new(name,flag,self)
381
+ obj.instance_eval &the_block if block_given?
382
+ hash.each_pair do |fxn,val|
383
+ obj.send(fxn,val)
384
+ end
385
+ @all_flags[name] = obj
386
+ return obj
387
+ end
388
+ # top-level flag-declarer
389
+ def optional_flag(flag_name_pair,hash={},&the_block)
390
+ flag(flag_name_pair,hash,&the_block)
391
+ flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
392
+ name = flag_name_pair[1] || flag_name_pair[0]
393
+ flag_properties name.to_sym do
394
+ optional
395
+ end
396
+ end
397
+ # top-level flag-declarer
398
+ def optional_switch_flag(flag_name_pair,hash={},&the_block)
399
+ flag(flag_name_pair,hash,&the_block)
400
+ flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
401
+ name = flag_name_pair[1] || flag_name_pair[0]
402
+ flag_properties name.to_sym do
403
+ optional
404
+ arity 0
405
+ end
406
+ end
407
+ # top-level flag-declarer
408
+ def keyword(flag_name_pair,hash={},&the_block)
409
+ @all_keywords ||= []
410
+ @all_keywords << name
411
+ flag(flag_name_pair,hash,&the_block)
412
+ flag_name_pair = [flag_name_pair] if flag_name_pair.class == String
413
+ name = flag_name_pair[1] || flag_name_pair[0]
414
+ flag_properties name.to_sym do
415
+ dash_symbol ""
416
+ long_dash_symbol ""
417
+ arity 0
418
+ optional
419
+ end
420
+ end
421
+ # top-level flag-declarer
422
+ def flagless_arg(name)
423
+ @all_flagless_arg ||= []
424
+ mine = flag(name.to_sym)
425
+ @all_flagless_arg << mine
426
+ end
427
+ # top-level flag-declarer
428
+ def usage_flag(*args)
429
+ # to ensure the existence of the @all_flags
430
+ @all_flags ||= {}
431
+ first,*rest = args
432
+
433
+ # the next two statements are necessary because
434
+ # a user might declare a usage flag, but so might
435
+ # the and_process! method. We need to make
436
+ # sure that they both refer to the same EachFlag
437
+ any_help_already_set = @all_flags.select {|key,val| val.for_help == true}
438
+ if any_help_already_set[0] != nil
439
+ # reassign rest to be the orginal first plus the rest
440
+ # UNLESS the first is the exact same one as your previous
441
+ # first -- GOTCHA!!
442
+ rest = [first] + rest if first.to_sym != any_help_already_set[0][0].to_sym
443
+ # and now use the discovered existing help flag key as our new first
444
+ # remember that the optional_flag statement below will
445
+ # merely reopen the existing object stored in the hash
446
+ # instead of creating a new one
447
+ first = any_help_already_set[0][0]
448
+ end
449
+ optional_flag [first] do
450
+ self.for_help = true
451
+ description "Help"
452
+ no_args
453
+ alternate_forms *rest if rest.length > 0
454
+ end
455
+ end
456
+ # top-level flag-declarer
457
+ def extended_help_flag(*args)
458
+ first,*rest = args
459
+ optional_flag [first] do
460
+ self.for_extended_help = true
461
+ description "Extended Help"
462
+ no_args
463
+ alternate_forms *rest if rest.length > 0
464
+ end
465
+ end
466
+ # top-level flag-declarer
467
+ def character_flag(switch,group="default",&the_block)
468
+ throw "Character switches can only be 1 character long" if switch.to_s.length > 1
469
+ flag(switch.to_sym,&the_block)
470
+ @group ||= {}
471
+ the_flag_we_just_added = @all_flags[switch.to_sym]
472
+
473
+ key = [group.to_sym,the_flag_we_just_added.the_dash_symbol]
474
+ # puts "#{ key.join(',') }"
475
+ @group[key] ||= []
476
+ @group[key] << the_flag_we_just_added
477
+ # re-assert ourselves
478
+ flag [switch.to_s, switch] do
479
+ optional
480
+ arity 0
481
+ compresses
482
+ end
483
+ end
484
+ # top-level flag-declarer
485
+ def flag_properties(symb,hash={},&the_block)
486
+ raise "Block needed for flag_properties" if not block_given? and hash=={}
487
+ @all_flags ||= {}
488
+ obj = @all_flags[symb.to_sym]
489
+ return if obj==nil
490
+ obj.instance_eval &the_block if block_given?
491
+ hash.each_pair do |fxn,val|
492
+ obj.send(fxn,val)
493
+ end
494
+ end
495
+ alias :properties :flag_properties
496
+ end
497
+ end
498
+
499
+ # defining the callable client-interface
500
+ module OptiFlag
501
+ module Flagset
502
+ # The NewInterface module is used to augment the ARGV
503
+ # constant with some special methods that it never used
504
+ # to have before.
505
+ #
506
+ # This is one of two hallmark ideas in making the
507
+ # OptiFlag suite easier to use:
508
+ # - easy declarative DSL syntax (all the <i>flag set and flag declarations</i>)
509
+ # - and this, an easy to use interface for accessing
510
+ # the results *after* the processor has finished processing.
511
+ #
512
+ # To emphasize this point, consider the following declaration:
513
+ #
514
+ # module AppArgs extend OptiFlagSet
515
+ # flag "dir"
516
+ # optional_flag "log"
517
+ # flag "username"
518
+ # flag "password"
519
+ #
520
+ # and_process!
521
+ # end
522
+ #
523
+ # Note the <i>special command</i> 'and_process!'
524
+ # This method, which should, by rote, be placed at the end
525
+ # of the DSL-zone (the code internal to the module block)
526
+ # is augmenting the ARGV constant so that the results of the
527
+ # parsing are now available. Thus:
528
+ # ARGV.flag_value.dir
529
+ # has a value of whatever the user passed in on the command
530
+ # line for the '-dir' flag.
531
+ # And
532
+ # ARGV.flag_value.log?
533
+ # tells us whether the user supplied the log flag and if
534
+ # she did, we can access it using
535
+ #
536
+ # ARGV.flag_value.log
537
+ # Better looking code would be:
538
+ #
539
+ # log_file = ARGV.flag_value.log if ARGV.flag_value.log?
540
+ module NewInterface
541
+ attr_accessor :errors,:flag_value,:specification_errors,:help_requested_on,:warnings
542
+ attr_writer :help_requested,:extended_help_requested
543
+ def errors?
544
+ self.errors != nil
545
+ end
546
+ def warnings?
547
+ self.warnings != nil
548
+ end
549
+ def help_requested?
550
+ @help_requested != nil
551
+ end
552
+ def extended_help_requested?
553
+ @extended_help_requested !=nil
554
+ end
555
+ alias :flags :flag_value
556
+ end
557
+ class Errors
558
+ attr_accessor :missing_flags,:other_errors,:validation_errors
559
+ def initialize
560
+ @missing_flags,@other_errors,@validation_errors = [],[],[]
561
+ end
562
+ def any_errors?
563
+ @missing_flags.length >0 or @other_errors.length >0 or
564
+ @validation_errors.length > 0
565
+ end
566
+ def divulge_problems(output=$stdout)
567
+ output.puts "Errors found:"
568
+ if @missing_flags.length >0
569
+ output.puts "Missing Flags:"
570
+ @missing_flags.each do |x|
571
+ output.puts " #{ x }"
572
+ end
573
+ end
574
+ if @other_errors.length >0
575
+ output.puts "Other Errors:"
576
+ @other_errors.each do |x|
577
+ output.puts " #{ x }"
578
+ end
579
+ end
580
+ if @validation_errors.length >0
581
+ output.puts "Validation Errors:"
582
+ @validation_errors.each do |x|
583
+ output.puts " #{ x }"
584
+ end
585
+ end
586
+ end
587
+ end
588
+ private
589
+ def create_new_value_class()
590
+ klass = Hash.new
591
+ klass.instance_eval do
592
+ def init_with_these(all_objs)
593
+ @all_flags = all_objs
594
+ end
595
+ end
596
+ klass.init_with_these(@all_flags)
597
+ @all_flags.each_value do |y|
598
+ # only allow alphabetic symbols to create methods
599
+ if (y.name.to_s =~ /^[a-zA-Z]+/)
600
+ klass.instance_eval %{
601
+ def #{y.name}()
602
+ @all_flags[:#{ y.name }].value if @all_flags[:#{ y.name }]
603
+ end
604
+ def #{y.name}_details()
605
+ @all_flags[:#{ y.name }] if @all_flags[:#{ y.name }]
606
+ end}
607
+ end
608
+ all_names = [y.name]
609
+ all_names << y.the_alternate_forms if y.the_alternate_forms.length > 0
610
+ all_names.flatten!
611
+ all_names = all_names.select{|x| x.to_s =~ /^[a-zA-Z]+/}
612
+ all_names.each do |x|
613
+ klass.instance_eval %{
614
+ def #{x}?()
615
+ ret = @all_flags[:#{ y.name }].value
616
+ return false if ret == nil
617
+ if @all_flags[:#{ y.name }] and !@all_flags[:#{ y.name }].default_used
618
+ return @all_flags[:#{ y.name }].value
619
+ end
620
+ end}
621
+ end
622
+ end
623
+ return klass
624
+ end
625
+
626
+ end # end of Flagset module
627
+ end # end of OptiFlag module
628
+
629
+
630
+
631
+ module OptiFlag
632
+ # Special command (at the same level as a <i>flag declaration</i>)
633
+ def OptiFlag.using_object(a_single_object,&the_block)
634
+ class_hierarchy = [a_single_object.class]
635
+ clazz = a_single_object.class
636
+ begin
637
+ clazz = clazz.superclass
638
+ class_hierarchy << clazz
639
+ end until clazz != Object
640
+ potential_methods =
641
+ class_hierarchy.collect{|x| x.instance_methods(false)}.flatten.sort
642
+
643
+ require 'enumerator'
644
+ valid_instance_var = []
645
+ potential_opt_switch_flags, potential_methods =
646
+ potential_methods.partition {|x| x =~ /\?$/ }
647
+ potential_methods.each_slice(2) do |getter,setter|
648
+ if setter == (getter + "=")
649
+ valid_instance_var << [getter,setter];
650
+ end
651
+ end
652
+ mod = Module.new
653
+ mod.extend Flagset
654
+ valid_instance_var.each do |getter,setter|
655
+ bound_method = a_single_object.method(setter)
656
+ mod.instance_eval do
657
+ flag getter do
658
+ self.proxied_bound_method = bound_method
659
+ end
660
+ end
661
+ end
662
+ mod.instance_eval &the_block if the_block
663
+ mod.instance_eval do
664
+ handle_errors_and_help
665
+ end
666
+ end
667
+
668
+ # This is a method that looks like a module.
669
+ # It is an excellent example of the syntactic tricks
670
+ # that ruby permits us.
671
+ # This method allows us to provide code like
672
+ # the following:
673
+ # module Example extend OptiFlagSet(:flag_symbol => "/")
674
+ # flag "dir"
675
+ # flag "log"
676
+ # flag "username"
677
+ # flag "password"
678
+ #
679
+ # and_process!
680
+ # end
681
+ # You will note that the top line looks a lot like
682
+ # the standard top line
683
+ # # our top line
684
+ # module Example extend OptiFlagSet(:flag_symbol => "/")
685
+ # versus
686
+ # # standard top line
687
+ # module Example extend OptiFlagSet
688
+ # The difference is that while the latter (a standard
689
+ # top line) is a reference to a module, the former is
690
+ # a call to this method, that *returns* the OptiFlag::Flagset
691
+ # module with some defaults changed.
692
+ #
693
+ # As of now the only symbol/method supported by this
694
+ # method that looks like a module, is the
695
+ # 'OptiFlag::Flagset.flag_symbol' class method.
696
+ #
697
+ # For those still not understanding the syntactic trick,
698
+ # or who find it odd, consider that something
699
+ # similar to this is done
700
+ # in the ruby core language using the proxy/delegate
701
+ # pattern. (See delegate.rb in the Ruby core library)
702
+ # class Tempfile < DelegateClass(File)
703
+ # # ...
704
+ # # ...
705
+ # end
706
+ #
707
+ # You will note that the DelegateClass() is also
708
+ # a method that superficially resembles a class
709
+ # that parameterizes itself.
710
+ # This can be done because Ruby expects:
711
+ # class <identifier> < <expression>
712
+ # and not
713
+ # class <identifier> < <identifier>
714
+ # like some other languages. And likewise
715
+ # module <identifier> extend <expression>
716
+ # which allows us to create this method which
717
+ # has the exact same name as the module.
718
+ # Clever ruby.
719
+ def OptiFlag.Flagset(hash)
720
+ # changed this from just returning Flagset...
721
+ # Reason Being: a user can specify two modules in one file
722
+ # one with this method, and one just using Flagset...
723
+ # if you don't clone at this point, you are left with
724
+ # a global change... BUT to get the cloning working
725
+ # I had to do some singleton_class trickeration as part of
726
+ # the initialize method for EachFlag... I am not
727
+ # 100% sure I understand what I just did. -- D.O.E 5/30/06
728
+ mod = Flagset.clone
729
+ hash.each_pair do |symb,val|
730
+ mod.send(symb.to_sym,val)
731
+ end
732
+ return mod
733
+ end
734
+
735
+ end
736
+
737
+ module OptiFlag
738
+ module Flagset
739
+ # Special command (at the same level as a <i>flag declaration</i>)
740
+ def handle_errors_and_help(options={})
741
+ return if !@all_flags
742
+ # the next three lines allow me
743
+ # to increase testability of
744
+ # this function, allowing client code to
745
+ # simulate ARGV passing without actually
746
+ # depending on ARGV being there
747
+ options[:argv] ||= ARGV # set it, only if not set
748
+ options[:level] ||= :not_strict # breaks backwards compatability
749
+ argv = options[:argv]
750
+ # the next two lines add the help
751
+ # flag to the list.
752
+ usage_flag "h","?" # make this part of the standard config
753
+ properties "h", :long_form=>"help"
754
+ parse(argv,false)
755
+ if argv.help_requested?
756
+ if !argv.help_requested_on
757
+ show_help
758
+ elsif the_on = argv.help_requested_on
759
+ show_individual_extended_help(the_on.to_sym)
760
+ end
761
+ exit
762
+ end
763
+ if argv.extended_help_requested?
764
+ show_extended_help
765
+ exit
766
+ end
767
+ if argv.errors?
768
+ argv.errors.divulge_problems
769
+ # show_help
770
+ exit
771
+ end
772
+ if argv.warnings? and options[:level] == :with_no_warnings
773
+ puts "In strict warning handling mode. Warnings will cause process to exit."
774
+ argv.warnings.each do |x|
775
+ puts " #{ x }"
776
+ end
777
+ puts "Please fix these warnings and try again."
778
+ exit
779
+ end
780
+ # the next three lines augment the
781
+ # name of the module that the user
782
+ # has declared... not just argv.
783
+ self.extend NewInterface
784
+ self.flag_value = argv.flag_value
785
+ self.errors = argv.errors
786
+ argv
787
+ end # end of method handle_errors_and_help
788
+ alias :and_process! :handle_errors_and_help
789
+ end # end of Flagset
790
+ end # end of OptiFlag
791
+
792
+ module OptiFlag
793
+ # testing to see if this shows (2)
794
+ module Flagset
795
+ attr_accessor :help_bundle;
796
+
797
+ private
798
+ def render_help(stdo=$stdout)
799
+ @help_bundle.banner.call stdo
800
+ @all_flags.each_pair do |name_of_flag,flag|
801
+ @help_bundle.help.call stdo, flag
802
+ end
803
+ end
804
+ def show_help(start_message="",stdo=$stdout)
805
+ stdo.puts start_message
806
+ render_help(stdo)
807
+ @all_keywords ||= []
808
+ end
809
+ def show_extended_help(start_message="",stdo=$stdout)
810
+ @all_flags.keys.each do |name|
811
+ show_individual_extended_help(name,stdo)
812
+ end
813
+ end
814
+ def show_individual_extended_help(name,stdo=$stdout)
815
+ the_flag = @all_flags[name]
816
+ return if !the_flag
817
+ extended_help = @help_bundle.extended_help
818
+ extended_help.call stdo, the_flag
819
+ end
820
+ end
821
+ end
822
+
823
+
824
+
825
+ # Specification error possibilities (at FlagsetDefinition)
826
+ # 1) a value_matches and a value_in_set on the same argument
827
+ # 2) ambiguous flag names (e.g. two flags both with name -help)
828
+ # 3) short symbol flag and long symbol flags match each other
829
+ # Specification error possibilities (at DefinitionSwitcher)
830
+ # 1)
831
+ # 2)
832
+ # Warning conditions
833
+ # 1) Left-over arguments
834
+
835
+
836
+ # OptiFlag is the module that provides namespacing for the entire
837
+ # optiflag functionality. For usage and examples, see
838
+ # http://optiflag.rubyforge.org
839
+ #
840
+ # = Terminology
841
+ # Please treat the following terminology as specific only
842
+ # to the OptiFlag code suite. In the remaining RDoc, we shall
843
+ # try to emphasize this consistent terminology with italics.
844
+ # If you see an italicized phrase, chances are that it
845
+ # is defined here.
846
+ #
847
+ # == Flag Set Declaration
848
+ # A <i>flag set declaration</i> is a set of flag declarations
849
+ # created by the user of the OptiFlag suite and corresponds
850
+ # to the following code snippet:
851
+ # module AppArgs extend OptiFlagSet
852
+ # # individual flag declaration goes here ...
853
+ # # ... and here
854
+ # end
855
+ # In this case, the module *AppArgs* is a <i>flag set declaration</i>,
856
+ # and all code within the module definition (i.e. up to the end of
857
+ # *end* of the module) is either a <i>flag declaration</i> or a
858
+ # special command.
859
+ #
860
+ # Another way to treat this declaration is as a demarcation
861
+ # between the normal Ruby world and the mini-DSL (Domain Specific
862
+ # Language) that OptiFlag offers. In this view,
863
+ # the declaration provides a DSL-zone in which the DSL
864
+ # is allowed.
865
+ # module AppArgs extend OptiFlagSet
866
+ # # DSL-zone
867
+ # # DSL-zone (for declaring and modifying flags)
868
+ # # DSL-zone
869
+ # end
870
+ # the first line
871
+ # module AppArgs extend OptiFlagSet
872
+ # is really just rote.
873
+ #
874
+ # Supply your own module
875
+ # argument name and make sure it extends this
876
+ # OptiFlag::Flagset as is written.
877
+ # You will then have a scoped space to write
878
+ # in a command line DSL.
879
+ #
880
+ # == Flag Declaration
881
+ # A <i>flag declaration</i> is placed within a <i>flag set declaration</i>
882
+ # to indicate that one input parameter per declaration is requested
883
+ # from the command line. A <i>flag declaration</i> is the mini-DSL's main
884
+ # programming construct. In the following code, four <i>flag declarations</i>
885
+ # are placed within the overall <i>flag *set* declaration</i> named AppArgs:
886
+ # module AppArgs extend OptiFlagSet
887
+ # flag "dir"
888
+ # flag "log"
889
+ # flag "username"
890
+ # flag "password"
891
+ # end
892
+ # Please note that there are other <i>flag set declaration</i> nouns
893
+ # other than flag. For instance in the following snippet:
894
+ # module Example extend OptiFlagSet
895
+ # flag "dir" do
896
+ # alternate_forms "directory","D","d"
897
+ # description "The Appliction Directory"
898
+ # end
899
+ # optional_flag "log" do
900
+ # description "The directory in which to find the log files"
901
+ # long_form "logging-directory" # long form is keyed after the '--' symbol
902
+ # end
903
+ # flag "username", :description => "Database username." # alternate form
904
+ # flag "password" do
905
+ # description "Database password."
906
+ # end
907
+ # usage_flag "h","help","?"
908
+ # extended_help_flag "superhelp"
909
+ #
910
+ # and_process!
911
+ # end
912
+ # there are six <i>flag declarations</i> in total:
913
+ # * a flag named "dir"
914
+ # * an optional flag named "log"
915
+ # * a flag named "username"
916
+ # * a flag named "password"
917
+ # * a usage flag for summoning help on how to use the flags
918
+ # * an extended help flag for summoning detailed usage for the flags
919
+ # Everything else is either a <i>clause level modifier</i> (e.g.
920
+ # alternate_forms, description, etc.) or a <i>special command</i>.
921
+ #
922
+ # <b>For a list of all <i>flag set declarations</i> and how to
923
+ # use them, see the RDoc for the OptiFlag::Flagset module.</b>
924
+ #
925
+ # == Clause Level Modifier
926
+ # As seen above, a <i>clause level modifier</i> is a word
927
+ # used to modify the basic flag, usually within a nested do
928
+ # block. It it this nesting which has inspired the use
929
+ # of the word clause. For instance:
930
+ # module Example extend OptiFlagSet
931
+ # flag "dir" do
932
+ # alternate_forms "directory","D","d"
933
+ # description "The Appliction Directory"
934
+ # end
935
+ # end
936
+ # We could read this as a sentence:
937
+ # 'Create a flag "dir" which has three alternate forms "directory", "D", and "d"
938
+ # and which has a description that reads "The Application Directory'.
939
+ #
940
+ # For the most part, these <i>clause level modifiers</i> are nested
941
+ # within a do-block, though OptiFlag allows us to conserve
942
+ # vertical space by using symbol hashes. (It is recommended
943
+ # that one only uses these in simple cases). For instance,
944
+ # the following two <i>flag set declarations</i> are
945
+ # equivalent:
946
+ # module Example extend OptiFlagSet
947
+ # flag "username", :description => "Database username." # alternate form
948
+ #
949
+ # and_process!
950
+ # end
951
+ # and
952
+ # module Example extend OptiFlagSet
953
+ # flag "username" do
954
+ # description "Database username."
955
+ # end
956
+ #
957
+ # and_process!
958
+ # end
959
+ # with the first being a syntactically friendly
960
+ # hash of the <i>clause level modifier</i> as a
961
+ # symbol pointing to the value of the modification.
962
+ #
963
+ # For a complete list of <i>clause level modifiers</i>
964
+ # see the RDoc for OptiFlag::Flagset::EachFlag
965
+ module OptiFlag
966
+
967
+ end
968
+
969
+ OptiFlagSet = OptiFlag::Flagset
970
+ def OptiFlagSet(hash)
971
+ OptiFlag::Flagset(hash)
972
+ end