clasp-ruby 0.10.2

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.
@@ -0,0 +1,467 @@
1
+
2
+ # ######################################################################## #
3
+ # File: clasp/arguments.rb
4
+ #
5
+ # Purpose: Definition of the Arguments class, the main class in
6
+ # CLASP.Ruby
7
+ #
8
+ # Created: 14th February 2014
9
+ # Updated: 11th June 2016
10
+ #
11
+ # Home: http://github.com/synesissoftware/CLASP.Ruby
12
+ #
13
+ # Author: Matthew Wilson
14
+ #
15
+ # Copyright (c) 2014-2016, Matthew Wilson and Synesis Software
16
+ # All rights reserved.
17
+ #
18
+ # Redistribution and use in source and binary forms, with or without
19
+ # modification, are permitted provided that the following conditions are
20
+ # met:
21
+ #
22
+ # * Redistributions of source code must retain the above copyright
23
+ # notice, this list of conditions and the following disclaimer.
24
+ #
25
+ # * Redistributions in binary form must reproduce the above copyright
26
+ # notice, this list of conditions and the following disclaimer in the
27
+ # documentation and/or other materials provided with the distribution.
28
+ #
29
+ # * Neither the names of the copyright holder nor the names of its
30
+ # contributors may be used to endorse or promote products derived from
31
+ # this software without specific prior written permission.
32
+ #
33
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
+ #
45
+ # ######################################################################## #
46
+
47
+
48
+
49
+
50
+ =begin
51
+ =end
52
+
53
+ module CLASP
54
+
55
+ # ######################################################################## #
56
+ # classes
57
+
58
+ # The main class for processing command-line arguments
59
+ class Arguments
60
+
61
+ #:stopdoc:
62
+ private
63
+ class Flag #:nodoc: all
64
+
65
+ #:nodoc:
66
+ def initialize(arg, given_index, given_name, resolved_name, argument_alias, given_hyphens, given_label, extras)
67
+
68
+ @arg = arg
69
+ @given_index = given_index
70
+ @given_name = given_name
71
+ @argument_alias = argument_alias
72
+ @given_hyphens = given_hyphens
73
+ @given_label = given_label
74
+ @name = resolved_name || given_name
75
+ @extras = extras.nil? ? {} : extras
76
+ end
77
+
78
+ attr_reader :given_index
79
+ attr_reader :given_name
80
+ attr_reader :argument_alias
81
+ attr_reader :given_hyphens
82
+ attr_reader :given_label
83
+ attr_reader :name
84
+ attr_reader :extras
85
+
86
+ #:nodoc:
87
+ def to_s
88
+
89
+ @name
90
+ end
91
+
92
+ #:nodoc:
93
+ def eql?(rhs)
94
+
95
+ return false if rhs.nil?
96
+
97
+ # check name and aliases
98
+ return true if @name == rhs
99
+ return argument_alias.aliases.include? rhs if argument_alias
100
+ false
101
+ end
102
+
103
+ #:nodoc:
104
+ def ==(rhs)
105
+
106
+ return false if rhs.nil?
107
+ if not rhs.instance_of? String
108
+
109
+ rhs = rhs.name
110
+ end
111
+ eql? rhs
112
+ end
113
+
114
+ #:nodoc:
115
+ def hash
116
+
117
+ @arg.hash
118
+ end
119
+ end
120
+
121
+ class Option #:nodoc: all
122
+
123
+ #:nodoc:
124
+ def initialize(arg, given_index, given_name, resolved_name, argument_alias, given_hyphens, given_label, value, extras)
125
+
126
+ @arg = arg
127
+ @given_index = given_index
128
+ @given_name = given_name
129
+ @argument_alias = argument_alias
130
+ @given_hyphens = given_hyphens
131
+ @given_label = given_label
132
+ @value = value
133
+ @name = resolved_name || given_name
134
+ @extras = extras.nil? ? {} : extras
135
+ end
136
+
137
+ attr_reader :given_index
138
+ attr_reader :given_name
139
+ attr_reader :argument_alias
140
+ attr_reader :given_hyphens
141
+ attr_reader :given_label
142
+ attr_reader :name
143
+ attr_reader :value
144
+ attr_reader :extras
145
+
146
+ #:nodoc:
147
+ def eql?(rhs)
148
+
149
+ return false if rhs.nil?
150
+
151
+ # check name and aliases
152
+ return true if @name == rhs
153
+ return argument_alias.aliases.include? rhs if argument_alias
154
+ false
155
+ end
156
+
157
+ #:nodoc:
158
+ def ==(rhs)
159
+
160
+ return false if rhs.nil?
161
+ if not rhs.instance_of? String
162
+
163
+ rhs = rhs.name
164
+ end
165
+ eql? rhs
166
+ end
167
+
168
+ #:nodoc:
169
+ def hash
170
+
171
+ @arg.hash
172
+ end
173
+
174
+ #:nodoc:
175
+ def to_s
176
+
177
+ "#{name}=#{value}"
178
+ end
179
+ end
180
+
181
+ class ImmutableArray #:nodoc: all
182
+
183
+ include Enumerable
184
+
185
+ #:nodoc:
186
+ def initialize(a)
187
+
188
+ @a = a
189
+ end
190
+
191
+ #:nodoc:
192
+ def each
193
+
194
+ @a.each { |i| yield i }
195
+ end
196
+
197
+ #:nodoc:
198
+ def size
199
+
200
+ @a.size
201
+ end
202
+
203
+ #:nodoc:
204
+ def empty?
205
+
206
+ @a.empty?
207
+ end
208
+
209
+ #:nodoc:
210
+ def [](index)
211
+
212
+ @a[index]
213
+ end
214
+
215
+ #:nodoc:
216
+ def ==(rhs)
217
+
218
+ return rhs == @a if rhs.is_a? self.class
219
+ @a == rhs
220
+ end
221
+ end
222
+
223
+ #:startdoc:
224
+
225
+ # ######################
226
+ # Construction
227
+
228
+ public
229
+ # Constructs an instance of the class, according to the given parameters
230
+ #
231
+ # See the documentation for the ::CLASP module for examples
232
+ #
233
+ # === Signature
234
+ #
235
+ # * *Parameters*:
236
+ # - +argv+:: (+Array+) The arguments array. May not be +nil+. Defaults to +ARGV+.
237
+ # - +aliases+:: (+Array+) The aliases array. Defaults to +nil+. If none supplied, no aliasing will be performed.
238
+ # - +options+:: An options hash, containing any of the following options.
239
+ #
240
+ # * *Options*:
241
+ # - +mutate_arg:+:: (+Boolean+) Determines if the library should mutate +argv+. Defaults to +true+. This is essential when using CLASP in conjunction with <tt>$\<</tt>.
242
+ #
243
+ def initialize(argv = ARGV, aliases = nil, options = {})
244
+
245
+ # have to do this name-swap, as 'options' has CLASP-specific
246
+ # meaning
247
+ init_opts, options = options.dup, nil
248
+
249
+ init_opts[:mutate_argv] = true unless init_opts.has_key? :mutate_argv
250
+
251
+
252
+ @argv = argv
253
+ @argv_original_copy = ImmutableArray.new(argv.dup)
254
+
255
+ @aliases = aliases
256
+
257
+ aliases = nil if aliases and aliases.empty?
258
+
259
+ flags, options, values = Arguments.parse(argv, aliases)
260
+
261
+ @flags = ImmutableArray.new(flags)
262
+ @options = ImmutableArray.new(options)
263
+ @values = ImmutableArray.new(values)
264
+
265
+
266
+ # do argv-mutation, if required
267
+ if init_opts[:mutate_argv]
268
+
269
+ while not argv.empty?
270
+
271
+ argv.shift
272
+ end
273
+
274
+ @values.each do |v|
275
+
276
+ argv << v
277
+ end
278
+ end
279
+ end
280
+
281
+ private
282
+ def self.parse(argv, aliases)
283
+
284
+ flags = []
285
+ options = []
286
+ values = []
287
+
288
+ forced_value = false
289
+ want_option_value = false
290
+
291
+ argv.each_with_index do |arg, index|
292
+
293
+ if not forced_value
294
+
295
+ if '--' == arg
296
+
297
+ # all subsequent arguments are values
298
+ forced_value = true
299
+ next
300
+ end
301
+
302
+ # do regex test to see if option/flag/value
303
+ if arg =~ /^(-+)([^=]+)/
304
+
305
+ hyphens = $1
306
+ given_label = $2
307
+ given_name = "#$1#$2"
308
+ value = ($' and not $'.empty?) ? $'[1 ... $'.size] : nil
309
+ argument_alias = nil
310
+ resolved_name = nil
311
+
312
+ (aliases || []).each do |a|
313
+
314
+ if a.name == given_name or a.aliases.include? given_name
315
+
316
+ argument_alias = a
317
+ resolved_name = a.name
318
+
319
+ # need to check whether the alias is a default-option
320
+ # and, if so, expand out its name and value, and replace
321
+ # the name and (if none previously specified) the value
322
+ if resolved_name =~ /^(-+)([^=]+)=/
323
+
324
+ resolved_name = "#$1#$2"
325
+ value ||= $'
326
+ end
327
+ break
328
+ end
329
+ end
330
+
331
+ # Here we intercept and (potentially) cater to grouped flags
332
+ if not argument_alias and not value and aliases and 1 == hyphens.size
333
+
334
+ # Must match all
335
+ flag_aliases = []
336
+ given_label[0 ... given_label.size].each_char do |c|
337
+
338
+ new_flag = "-#{c.chr}"
339
+
340
+ flag_alias = nil
341
+
342
+ # special case where the flag's actual name is short form and found here
343
+ flag_alias ||= aliases.detect { |a| a.is_a?(CLASP::Flag) && a.name == new_flag }
344
+
345
+ # if not found as a flag, look in each aliases' aliases
346
+ flag_alias ||= aliases.detect { |a| a.aliases.include? new_flag }
347
+
348
+ if not flag_alias
349
+
350
+ flag_aliases = nil
351
+ break
352
+ else
353
+
354
+ flag_aliases << flag_alias
355
+ end
356
+ end
357
+
358
+ if flag_aliases
359
+
360
+ # got them all, so now have to process them all
361
+ # as normal. Note: is this susceptible to
362
+ # infinite recursion
363
+
364
+ # convert to argv and invoke
365
+ flags_argv = flag_aliases.map { |a| a.name }
366
+
367
+ grp_flags, grp_options, grp_value = Arguments.parse flags_argv, aliases
368
+
369
+ grp_flags.map! { |f| Flag.new(arg, index, given_name, f.name, f.argument_alias, hyphens.size, given_label, argument_alias ? argument_alias.extras : nil) }
370
+ grp_options.map! { |o| Option.new(arg, index, given_name, o.name, o.argument_alias, hyphens.size, given_label, o.value, argument_alias ? argument_alias.extras : nil) }
371
+
372
+ flags.push(*grp_flags)
373
+ options.push(*grp_options)
374
+ values.push(*grp_value)
375
+
376
+ next
377
+ end
378
+ end
379
+
380
+ if argument_alias and argument_alias.is_a? CLASP::Option and not value
381
+
382
+ want_option_value = true
383
+ options << Option.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, nil, argument_alias ? argument_alias.extras : nil)
384
+ elsif value
385
+
386
+ want_option_value = false
387
+ options << Option.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, value, argument_alias ? argument_alias.extras : nil)
388
+ else
389
+
390
+ want_option_value = false
391
+ flags << Flag.new(arg, index, given_name, resolved_name, argument_alias, hyphens.size, given_label, argument_alias ? argument_alias.extras : nil)
392
+ end
393
+
394
+ next
395
+ end
396
+ end
397
+
398
+ if want_option_value and not forced_value
399
+
400
+ option = options[-1]
401
+ option.instance_eval("@value='#{arg}'")
402
+ want_option_value = false
403
+ else
404
+
405
+ values << arg
406
+ end
407
+ end
408
+
409
+ return flags, options, values
410
+
411
+ end
412
+
413
+ # ######################
414
+ # Attributes
415
+
416
+ public
417
+ # an immutable array of aliases
418
+ attr_reader :aliases
419
+
420
+ # an immutable array of flags
421
+ attr_reader :flags
422
+
423
+ # an immutable array of options
424
+ attr_reader :options
425
+
426
+ # an immutable array of values
427
+ attr_reader :values
428
+
429
+ # the (possibly mutated) array of arguments instance passed to new
430
+ attr_reader :argv
431
+
432
+ # unchanged copy of the original array of arguments passed to new
433
+ attr_reader :argv_original_copy
434
+ end
435
+
436
+ # ######################################################################## #
437
+ # module
438
+
439
+ end # module CLASP
440
+
441
+ # ######################################################################## #
442
+ # extensions
443
+
444
+ #:nodoc:
445
+ class Array
446
+
447
+ # Monkey-patched Array#== in order to handle comparison with
448
+ # ImmutableArray
449
+ #
450
+ # NOTE: do not do so for +eql?+
451
+
452
+ #:nodoc:
453
+ alias_method :old_equal, :==
454
+
455
+ undef :==
456
+
457
+ #:nodoc:
458
+ def ==(rhs)
459
+
460
+ return rhs == self if rhs.is_a? CLASP::Arguments::ImmutableArray
461
+
462
+ old_equal rhs
463
+ end
464
+ end
465
+
466
+ # ############################## end of file ############################# #
467
+