reek 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,19 +26,19 @@ describe MethodChecker, "(Feature Envy)" do
26
26
  @cchk.check_source('def simple(arga) arga[3] end')
27
27
  @rpt.length.should == 2
28
28
  @rpt[0].should == UtilityFunction.new(@cchk)
29
- @rpt[1].should == FeatureEnvy.new(@cchk, ':arga')
29
+ @rpt[1].should == FeatureEnvy.new(@cchk, :arga)
30
30
  end
31
31
 
32
32
  it 'should report highest affinity' do
33
33
  @cchk.check_source('def simple(arga) slim = ""; s1 = ""; s1.to_s; @m = 34; end')
34
34
  @rpt.length.should == 1
35
- @rpt[0].should == FeatureEnvy.new(@cchk, ':s1')
35
+ @rpt[0].should == FeatureEnvy.new(@cchk, :s1)
36
36
  end
37
37
 
38
38
  it 'should report multiple affinities' do
39
39
  @cchk.check_source('def simple(arga) s1 = ""; s1.to_s; s2 = ""; s2.to_s; @m = 34; end')
40
40
  @rpt.length.should == 1
41
- @rpt[0].should == FeatureEnvy.new(@cchk, ':s1 and :s2')
41
+ @rpt[0].should == FeatureEnvy.new(@cchk, :s1, :s2)
42
42
  end
43
43
 
44
44
  it 'should not reference global variables' do
@@ -57,6 +57,5 @@ describe FeatureEnvy, '#report' do
57
57
  mchk = MethodChecker.new([], 'Class')
58
58
  smell = FeatureEnvy.new(mchk, [:lvar, :fred])
59
59
  smell.report.should match(/fred/)
60
- smell.report.should_not match(/:fred/)
61
60
  end
62
61
  end
@@ -0,0 +1,138 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/method_checker'
4
+ require 'reek/report'
5
+
6
+ include Reek
7
+
8
+ describe MethodChecker, "(Long Parameter List)" do
9
+
10
+ before(:each) do
11
+ @rpt = Report.new
12
+ @cchk = MethodChecker.new(@rpt, 'Thing')
13
+ end
14
+
15
+ describe 'for methods with few parameters' do
16
+ it 'should report nothing for no parameters' do
17
+ @cchk.check_source('def simple; f(3);true; end')
18
+ @rpt.should be_empty
19
+ end
20
+
21
+ it 'should report nothing for 1 parameter' do
22
+ @cchk.check_source('def simple(yep) f(3);true end')
23
+ @rpt.should be_empty
24
+ end
25
+
26
+ it 'should report nothing for 2 parameters' do
27
+ @cchk.check_source('def simple(yep,zero) f(3);true end')
28
+ @rpt.should be_empty
29
+ end
30
+
31
+ it 'should not count an optional block' do
32
+ @cchk.check_source('def simple(alpha, yep, zero, &opt) f(3);true end')
33
+ @rpt.should be_empty
34
+ end
35
+
36
+ it 'should not report inner block with too many parameters' do
37
+ @cchk.check_source('def simple(yep,zero); m[3]; rand(34); f.each { |arga, argb, argc, argd| true}; end')
38
+ @rpt.should be_empty
39
+ end
40
+
41
+ describe 'and default values' do
42
+ it 'should report nothing for 1 parameter' do
43
+ @cchk.check_source('def simple(zero=nil) f(3);false end')
44
+ @rpt.should be_empty
45
+ end
46
+
47
+ it 'should report nothing for 2 parameters with 1 default' do
48
+ @cchk.check_source('def simple(yep, zero=nil) f(3);false end')
49
+ @rpt.should be_empty
50
+ end
51
+
52
+ it 'should report nothing for 2 defaulted parameters' do
53
+ @cchk.check_source('def simple(yep=4, zero=nil) f(3);false end')
54
+ @rpt.should be_empty
55
+ end
56
+ end
57
+ end
58
+
59
+ describe 'for methods with too many parameters' do
60
+ it 'should report 4 parameters' do
61
+ @cchk.check_source('def simple(arga, argb, argc, argd) f(3);true end')
62
+ @rpt.length.should == 1
63
+ @rpt[0].should be_instance_of(LongParameterList)
64
+ end
65
+
66
+ it 'should report 8 parameters' do
67
+ @cchk.check_source('def simple(arga, argb, argc, argd,arge, argf, argg, argh) f(3);true end')
68
+ @rpt.length.should == 1
69
+ @rpt[0].should be_instance_of(LongParameterList)
70
+ end
71
+
72
+ describe 'and default values' do
73
+ it 'should report 3 with 1 defaulted' do
74
+ @cchk.check_source('def simple(polly, queue, yep, zero=nil) f(3);false end')
75
+ @rpt.length.should == 1
76
+ @rpt[0].should be_instance_of(LongParameterList)
77
+ end
78
+
79
+ it 'should report with 3 defaulted' do
80
+ @cchk.check_source('def simple(aarg, polly=2, yep=true, zero=nil) f(3);false end')
81
+ @rpt.length.should == 1
82
+ @rpt[0].should be_instance_of(LongParameterList)
83
+ end
84
+ end
85
+
86
+ describe 'in a class' do
87
+ class InnerTest
88
+ def xyzero(arga,argb) f(3);true end
89
+ def abc(argx,yep,zero,argm) f(3);false end
90
+ end
91
+
92
+ it 'should only report long param list' do
93
+ @cchk.check_object(InnerTest)
94
+ @rpt.length.should == 1
95
+ @rpt[0].should be_instance_of(LongParameterList)
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'yield' do
101
+ it 'should not report yield with few parameters' do
102
+ @cchk.check_source('def simple(arga, argb, &blk) f(3);yield a,b; end')
103
+ @rpt.should be_empty
104
+ end
105
+
106
+ it 'should report yield with many parameters' do
107
+ @cchk.check_source('def simple(arga, argb, &blk) f(3);yield a,b,a,b; end')
108
+ @rpt.length.should == 1
109
+ @rpt[0].should be_instance_of(LongYieldList)
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
116
+ describe LongParameterList, 'when given the class name' do
117
+
118
+ before(:each) do
119
+ @rpt = Report.new
120
+ @cchk = MethodChecker.new(@rpt, 'classname')
121
+ end
122
+
123
+ it 'should report the class name' do
124
+ @cchk.check_source('def simple(arga, argb, argc, argd) f(3);true end')
125
+ @rpt.length.should == 1
126
+ @rpt[0].should be_instance_of(LongParameterList)
127
+ @rpt[0].report.should match(/classname#simple/)
128
+ end
129
+ end
130
+
131
+ describe LongParameterList, '#report' do
132
+ it 'should report the method name and num params' do
133
+ mchk = MethodChecker.new([], 'Class')
134
+ smell = LongParameterList.new(mchk)
135
+ smell.report.should match(/Class/)
136
+ end
137
+
138
+ end
@@ -5,159 +5,29 @@ require 'reek/report'
5
5
 
6
6
  include Reek
7
7
 
8
- describe MethodChecker, "(Long Parameter List)" do
9
-
8
+ describe MethodChecker, "with no method definitions" do
10
9
  before(:each) do
11
10
  @rpt = Report.new
12
11
  @cchk = MethodChecker.new(@rpt, 'Thing')
13
12
  end
14
13
 
15
- describe 'with no method definitions' do
16
- it 'should report no problems for empty source code' do
17
- @cchk.check_source('')
18
- @rpt.should be_empty
19
- end
20
-
21
- it 'should report no problems for empty class' do
22
- @cchk.check_source('class Fred; end')
23
- @rpt.should be_empty
24
- end
25
- end
26
-
27
- describe 'for methods with few parameters' do
28
- it 'should report nothing for no parameters' do
29
- @cchk.check_source('def simple; f(3);true; end')
30
- @rpt.should be_empty
31
- end
32
-
33
- it 'should report nothing for 1 parameter' do
34
- @cchk.check_source('def simple(yep) f(3);true end')
35
- @rpt.should be_empty
36
- end
37
-
38
- it 'should report nothing for 2 parameters' do
39
- @cchk.check_source('def simple(yep,zero) f(3);true end')
40
- @rpt.should be_empty
41
- end
42
-
43
- it 'should not count an optional block' do
44
- @cchk.check_source('def simple(alpha, yep, zero, &opt) f(3);true end')
45
- @rpt.should be_empty
46
- end
47
-
48
- it 'should not report inner block with too many parameters' do
49
- @cchk.check_source('def simple(yep,zero); m[3]; rand(34); f.each { |arga, argb, argc, argd| true}; end')
50
- @rpt.should be_empty
51
- end
52
-
53
- describe 'and default values' do
54
- it 'should report nothing for 1 parameter' do
55
- @cchk.check_source('def simple(zero=nil) f(3);false end')
56
- @rpt.should be_empty
57
- end
58
-
59
- it 'should report nothing for 2 parameters with 1 default' do
60
- @cchk.check_source('def simple(yep, zero=nil) f(3);false end')
61
- @rpt.should be_empty
62
- end
63
-
64
- it 'should report nothing for 2 defaulted parameters' do
65
- @cchk.check_source('def simple(yep=4, zero=nil) f(3);false end')
66
- @rpt.should be_empty
67
- end
68
- end
69
- end
70
-
71
- describe 'for methods with too many parameters' do
72
- it 'should report 4 parameters' do
73
- @cchk.check_source('def simple(arga, argb, argc, argd) f(3);true end')
74
- @rpt.length.should == 1
75
- @rpt[0].should == LongParameterList.new(@cchk)
76
- end
77
-
78
- it 'should report 8 parameters' do
79
- @cchk.check_source('def simple(arga, argb, argc, argd,arge, argf, argg, argh) f(3);true end')
80
- @rpt.length.should == 1
81
- @rpt[0].should == LongParameterList.new(@cchk)
82
- end
83
-
84
- describe 'and default values' do
85
- it 'should report 3 with 1 defaulted' do
86
- @cchk.check_source('def simple(polly, queue, yep, zero=nil) f(3);false end')
87
- @rpt.length.should == 1
88
- @rpt[0].should == LongParameterList.new(@cchk)
89
- end
90
-
91
- it 'should report with 3 defaulted' do
92
- @cchk.check_source('def simple(aarg, polly=2, yep=true, zero=nil) f(3);false end')
93
- @rpt.length.should == 1
94
- @rpt[0].should == LongParameterList.new(@cchk)
95
- end
96
- end
97
-
98
- describe 'in a class' do
99
- class InnerTest
100
- def xyzero(arga,argb) f(3);true end
101
- def abc(argx,yep,zero,argm) f(3);false end
102
- end
103
-
104
- it 'should only report long param list' do
105
- @cchk.check_object(InnerTest)
106
- @rpt.length.should == 1
107
- @rpt[0].should == LongParameterList.new(@cchk)
108
- end
109
- end
110
- end
111
-
112
- describe 'yield' do
113
- it 'should not report yield with few parameters' do
114
- @cchk.check_source('def simple(arga, argb, &blk) f(3);yield a,b; end')
115
- @rpt.should be_empty
116
- end
117
-
118
- it 'should report yield with many parameters' do
119
- @cchk.check_source('def simple(arga, argb, &blk) f(3);yield a,b,a,b; end')
120
- @rpt.length.should == 1
121
- @rpt[0].should == LongYieldList.new(@cchk)
122
- end
123
-
14
+ it 'should report no problems for empty source code' do
15
+ @cchk.check_source('')
16
+ @rpt.should be_empty
124
17
  end
125
18
 
126
- end
127
-
128
- describe MethodChecker, 'when given the class name' do
129
-
130
- before(:each) do
131
- @rpt = Report.new
132
- @cchk = MethodChecker.new(@rpt, 'classname')
133
- end
134
-
135
- it 'should report the class name' do
136
- @cchk.check_source('def simple(arga, argb, argc, argd) f(3);true end')
137
- @rpt.length.should == 1
138
- @rpt[0].should == LongParameterList.new(@cchk)
139
- @rpt[0].report.should match(/classname#simple/)
140
- end
141
- end
142
-
143
- describe LongParameterList, '#report' do
144
- it 'should report the method name and num params' do
145
- mchk = MethodChecker.new([], 'Class')
146
- smell = LongParameterList.new(mchk)
147
- smell.report.should match(/Class/)
19
+ it 'should report no problems for empty class' do
20
+ @cchk.check_source('class Fred; end')
21
+ @rpt.should be_empty
148
22
  end
149
-
150
23
  end
151
24
 
152
25
  describe MethodChecker, 'when given a C extension' do
153
-
154
26
  before(:each) do
155
- @rpt = Report.new
156
- @cchk = MethodChecker.new(@rpt, 'Thing')
27
+ @cchk = MethodChecker.new(Report.new, 'Thing')
157
28
  end
158
29
 
159
30
  it 'should ignore :cfunc' do
160
31
  @cchk.check_object(Enumerable)
161
32
  end
162
33
  end
163
-
@@ -50,7 +50,7 @@ end
50
50
 
51
51
  describe SortByContext do
52
52
  before :each do
53
- @sorter = SortByContext.new
53
+ @sorter = SortByContext
54
54
  end
55
55
 
56
56
  it 'should return 0 for identical smells' do
@@ -65,7 +65,7 @@ end
65
65
 
66
66
  describe SortBySmell do
67
67
  before :each do
68
- @sorter = SortBySmell.new
68
+ @sorter = SortBySmell
69
69
  end
70
70
 
71
71
  it 'should return 0 for identical smells' do
@@ -29,4 +29,20 @@ describe MethodChecker, "(Utility Function)" do
29
29
  @cchk.check_object(Fred)
30
30
  @rpt.should be_empty
31
31
  end
32
+
33
+ it 'should count usages of self' do
34
+ @cchk.check_source('def <=>(other) Options[:sort_order].compare(self, other) end')
35
+ @rpt.should be_empty
36
+ end
37
+
38
+ it 'should not report overriding methods' do
39
+ class Father
40
+ def thing(ff); @kids = 0; end
41
+ end
42
+ class Son < Father
43
+ def thing(ff); ff; end
44
+ end
45
+ ClassChecker.new(@rpt).check_object(Son)
46
+ @rpt.should be_empty
47
+ end
32
48
  end
@@ -0,0 +1,1788 @@
1
+ #
2
+ # optparse.rb - command-line option analysis with the OptionParser class.
3
+ #
4
+ # Author:: Nobu Nakada
5
+ # Documentation:: Nobu Nakada and Gavin Sinclair.
6
+ #
7
+ # See OptionParser for documentation.
8
+ #
9
+
10
+
11
+ # == Developer Documentation (not for RDoc output)
12
+ #
13
+ # === Class tree
14
+ #
15
+ # - OptionParser:: front end
16
+ # - OptionParser::Switch:: each switches
17
+ # - OptionParser::List:: options list
18
+ # - OptionParser::ParseError:: errors on parsing
19
+ # - OptionParser::AmbiguousOption
20
+ # - OptionParser::NeedlessArgument
21
+ # - OptionParser::MissingArgument
22
+ # - OptionParser::InvalidOption
23
+ # - OptionParser::InvalidArgument
24
+ # - OptionParser::AmbiguousArgument
25
+ #
26
+ # === Object relationship diagram
27
+ #
28
+ # +--------------+
29
+ # | OptionParser |<>-----+
30
+ # +--------------+ | +--------+
31
+ # | ,-| Switch |
32
+ # on_head -------->+---------------+ / +--------+
33
+ # accept/reject -->| List |<|>-
34
+ # | |<|>- +----------+
35
+ # on ------------->+---------------+ `-| argument |
36
+ # : : | class |
37
+ # +---------------+ |==========|
38
+ # on_tail -------->| | |pattern |
39
+ # +---------------+ |----------|
40
+ # OptionParser.accept ->| DefaultList | |converter |
41
+ # reject |(shared between| +----------+
42
+ # | all instances)|
43
+ # +---------------+
44
+ #
45
+ # == OptionParser
46
+ #
47
+ # === Introduction
48
+ #
49
+ # OptionParser is a class for command-line option analysis. It is much more
50
+ # advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
51
+ # solution.
52
+ #
53
+ # === Features
54
+ #
55
+ # 1. The argument specification and the code to handle it are written in the
56
+ # same place.
57
+ # 2. It can output an option summary; you don't need to maintain this string
58
+ # separately.
59
+ # 3. Optional and mandatory arguments are specified very gracefully.
60
+ # 4. Arguments can be automatically converted to a specified class.
61
+ # 5. Arguments can be restricted to a certain set.
62
+ #
63
+ # All of these features are demonstrated in the examples below.
64
+ #
65
+ # === Minimal example
66
+ #
67
+ # require 'optparse'
68
+ #
69
+ # options = {}
70
+ # OptionParser.new do |opts|
71
+ # opts.banner = "Usage: example.rb [options]"
72
+ #
73
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
74
+ # options[:verbose] = v
75
+ # end
76
+ # end.parse!
77
+ #
78
+ # p options
79
+ # p ARGV
80
+ #
81
+ # === Complete example
82
+ #
83
+ # The following example is a complete Ruby program. You can run it and see the
84
+ # effect of specifying various options. This is probably the best way to learn
85
+ # the features of +optparse+.
86
+ #
87
+ # require 'optparse'
88
+ # require 'optparse/time'
89
+ # require 'ostruct'
90
+ # require 'pp'
91
+ #
92
+ # class OptparseExample
93
+ #
94
+ # CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
95
+ # CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
96
+ #
97
+ # #
98
+ # # Return a structure describing the options.
99
+ # #
100
+ # def self.parse(args)
101
+ # # The options specified on the command line will be collected in *options*.
102
+ # # We set default values here.
103
+ # options = OpenStruct.new
104
+ # options.library = []
105
+ # options.inplace = false
106
+ # options.encoding = "utf8"
107
+ # options.transfer_type = :auto
108
+ # options.verbose = false
109
+ #
110
+ # opts = OptionParser.new do |opts|
111
+ # opts.banner = "Usage: example.rb [options]"
112
+ #
113
+ # opts.separator ""
114
+ # opts.separator "Specific options:"
115
+ #
116
+ # # Mandatory argument.
117
+ # opts.on("-r", "--require LIBRARY",
118
+ # "Require the LIBRARY before executing your script") do |lib|
119
+ # options.library << lib
120
+ # end
121
+ #
122
+ # # Optional argument; multi-line description.
123
+ # opts.on("-i", "--inplace [EXTENSION]",
124
+ # "Edit ARGV files in place",
125
+ # " (make backup if EXTENSION supplied)") do |ext|
126
+ # options.inplace = true
127
+ # options.extension = ext || ''
128
+ # options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
129
+ # end
130
+ #
131
+ # # Cast 'delay' argument to a Float.
132
+ # opts.on("--delay N", Float, "Delay N seconds before executing") do |n|
133
+ # options.delay = n
134
+ # end
135
+ #
136
+ # # Cast 'time' argument to a Time object.
137
+ # opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
138
+ # options.time = time
139
+ # end
140
+ #
141
+ # # Cast to octal integer.
142
+ # opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
143
+ # "Specify record separator (default \\0)") do |rs|
144
+ # options.record_separator = rs
145
+ # end
146
+ #
147
+ # # List of arguments.
148
+ # opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
149
+ # options.list = list
150
+ # end
151
+ #
152
+ # # Keyword completion. We are specifying a specific set of arguments (CODES
153
+ # # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
154
+ # # the shortest unambiguous text.
155
+ # code_list = (CODE_ALIASES.keys + CODES).join(',')
156
+ # opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
157
+ # " (#{code_list})") do |encoding|
158
+ # options.encoding = encoding
159
+ # end
160
+ #
161
+ # # Optional argument with keyword completion.
162
+ # opts.on("--type [TYPE]", [:text, :binary, :auto],
163
+ # "Select transfer type (text, binary, auto)") do |t|
164
+ # options.transfer_type = t
165
+ # end
166
+ #
167
+ # # Boolean switch.
168
+ # opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
169
+ # options.verbose = v
170
+ # end
171
+ #
172
+ # opts.separator ""
173
+ # opts.separator "Common options:"
174
+ #
175
+ # # No argument, shows at tail. This will print an options summary.
176
+ # # Try it and see!
177
+ # opts.on_tail("-h", "--help", "Show this message") do
178
+ # puts opts
179
+ # exit
180
+ # end
181
+ #
182
+ # # Another typical switch to print the version.
183
+ # opts.on_tail("--version", "Show version") do
184
+ # puts OptionParser::Version.join('.')
185
+ # exit
186
+ # end
187
+ # end
188
+ #
189
+ # opts.parse!(args)
190
+ # options
191
+ # end # parse()
192
+ #
193
+ # end # class OptparseExample
194
+ #
195
+ # options = OptparseExample.parse(ARGV)
196
+ # pp options
197
+ #
198
+ # === Further documentation
199
+ #
200
+ # The above examples should be enough to learn how to use this class. If you
201
+ # have any questions, email me (gsinclair@soyabean.com.au) and I will update
202
+ # this document.
203
+ #
204
+ class OptionParser
205
+ # :stopdoc:
206
+ RCSID = %w$Id: optparse.rb 11798 2007-02-20 06:53:16Z knu $[1..-1].each {|s| s.freeze}.freeze
207
+ Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1])
208
+ LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2])
209
+ Release = RCSID[2]
210
+
211
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
212
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
213
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
214
+ # :startdoc:
215
+
216
+ #
217
+ # Keyword completion module. This allows partial arguments to be specified
218
+ # and resolved against a list of acceptable values.
219
+ #
220
+ module Completion
221
+ def complete(key, icase = false, pat = nil)
222
+ pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'),
223
+ icase)
224
+ canon, sw, k, v, cn = nil
225
+ candidates = []
226
+ each do |k, *v|
227
+ (if Regexp === k
228
+ kn = nil
229
+ k === key
230
+ else
231
+ kn = defined?(k.id2name) ? k.id2name : k
232
+ pat === kn
233
+ end) or next
234
+ v << k if v.empty?
235
+ candidates << [k, v, kn]
236
+ end
237
+ candidates = candidates.sort_by {|k, v, kn| kn.size}
238
+ if candidates.size == 1
239
+ canon, sw, * = candidates[0]
240
+ elsif candidates.size > 1
241
+ canon, sw, cn = candidates.shift
242
+ candidates.each do |k, v, kn|
243
+ next if sw == v
244
+ if String === cn and String === kn
245
+ if cn.rindex(kn, 0)
246
+ canon, sw, cn = k, v, kn
247
+ next
248
+ elsif kn.rindex(cn, 0)
249
+ next
250
+ end
251
+ end
252
+ throw :ambiguous, key
253
+ end
254
+ end
255
+ if canon
256
+ block_given? or return key, *sw
257
+ yield(key, *sw)
258
+ end
259
+ end
260
+
261
+ def convert(opt = nil, val = nil, *)
262
+ val
263
+ end
264
+ end
265
+
266
+
267
+ #
268
+ # Map from option/keyword string to object with completion.
269
+ #
270
+ class OptionMap < Hash
271
+ include Completion
272
+ end
273
+
274
+
275
+ #
276
+ # Individual switch class. Not important to the user.
277
+ #
278
+ # Defined within Switch are several Switch-derived classes: NoArgument,
279
+ # RequiredArgument, etc.
280
+ #
281
+ class Switch
282
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
283
+
284
+ #
285
+ # Guesses argument style from +arg+. Returns corresponding
286
+ # OptionParser::Switch class (OptionalArgument, etc.).
287
+ #
288
+ def self.guess(arg)
289
+ case arg
290
+ when ""
291
+ t = self
292
+ when /\A=?\[/
293
+ t = Switch::OptionalArgument
294
+ when /\A\s+\[/
295
+ t = Switch::PlacedArgument
296
+ else
297
+ t = Switch::RequiredArgument
298
+ end
299
+ self >= t or incompatible_argument_styles(arg, t)
300
+ t
301
+ end
302
+
303
+ def self.incompatible_argument_styles(arg, t)
304
+ raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
305
+ end
306
+
307
+ def self.pattern
308
+ NilClass
309
+ end
310
+
311
+ def initialize(pattern = nil, conv = nil,
312
+ short = nil, long = nil, arg = nil,
313
+ desc = ([] if short or long), block = Proc.new)
314
+ raise if Array === pattern
315
+ @pattern, @conv, @short, @long, @arg, @desc, @block =
316
+ pattern, conv, short, long, arg, desc, block
317
+ end
318
+
319
+ #
320
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
321
+ # argument pattern. Yields when the pattern doesn't match substring.
322
+ #
323
+ def parse_arg(arg)
324
+ pattern or return nil, arg
325
+ unless m = pattern.match(arg)
326
+ yield(InvalidArgument, arg)
327
+ return arg, nil
328
+ end
329
+ if String === m
330
+ m = [s = m]
331
+ else
332
+ m = m.to_a
333
+ s = m[0]
334
+ return nil, m unless String === s
335
+ end
336
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
337
+ return nil, m if s.length == arg.length
338
+ yield(InvalidArgument, arg) # didn't match whole arg
339
+ return arg[s.length..-1], m
340
+ end
341
+ private :parse_arg
342
+
343
+ #
344
+ # Parses argument, converts and returns +arg+, +block+ and result of
345
+ # conversion. Yields at semi-error condition instead of raising an
346
+ # exception.
347
+ #
348
+ def conv_arg(arg, val = nil)
349
+ if conv
350
+ val = conv.call(*val)
351
+ else
352
+ val = proc {|val| val}.call(*val)
353
+ end
354
+ return arg, block, val
355
+ end
356
+ private :conv_arg
357
+
358
+ #
359
+ # Produces the summary text. Each line of the summary is yielded to the
360
+ # block (without newline).
361
+ #
362
+ # +sdone+:: Already summarized short style options keyed hash.
363
+ # +ldone+:: Already summarized long style options keyed hash.
364
+ # +width+:: Width of left side (option part). In other words, the right
365
+ # side (description part) starts after +width+ columns.
366
+ # +max+:: Maximum width of left side -> the options are filled within
367
+ # +max+ columns.
368
+ # +indent+:: Prefix string indents all summarized lines.
369
+ #
370
+ def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "")
371
+ sopts, lopts, s = [], [], nil
372
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
373
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
374
+ return if sopts.empty? and lopts.empty? # completely hidden
375
+
376
+ left = [sopts.join(', ')]
377
+ right = desc.dup
378
+
379
+ while s = lopts.shift
380
+ l = left[-1].length + s.length
381
+ l += arg.length if left.size == 1 && arg
382
+ l < max or left << ''
383
+ left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s
384
+ end
385
+
386
+ left[0] << arg if arg
387
+ mlen = left.collect {|s| s.length}.max.to_i
388
+ while mlen > width and l = left.shift
389
+ mlen = left.collect {|s| s.length}.max.to_i if l.length == mlen
390
+ yield(indent + l)
391
+ end
392
+
393
+ while begin l = left.shift; r = right.shift; l or r end
394
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
395
+ yield(indent + l)
396
+ end
397
+
398
+ self
399
+ end
400
+
401
+ def add_banner(to) # :nodoc:
402
+ unless @short or @long
403
+ s = desc.join
404
+ to << " [" + s + "]..." unless s.empty?
405
+ end
406
+ to
407
+ end
408
+
409
+ def match_nonswitch?(str) # :nodoc:
410
+ @pattern =~ str unless @short or @long
411
+ end
412
+
413
+ #
414
+ # Main name of the switch.
415
+ #
416
+ def switch_name
417
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
418
+ end
419
+
420
+ #
421
+ # Switch that takes no arguments.
422
+ #
423
+ class NoArgument < self
424
+
425
+ #
426
+ # Raises an exception if any arguments given.
427
+ #
428
+ def parse(arg, argv)
429
+ yield(NeedlessArgument, arg) if arg
430
+ conv_arg(arg)
431
+ end
432
+
433
+ def self.incompatible_argument_styles(*)
434
+ end
435
+
436
+ def self.pattern
437
+ Object
438
+ end
439
+ end
440
+
441
+ #
442
+ # Switch that takes an argument.
443
+ #
444
+ class RequiredArgument < self
445
+
446
+ #
447
+ # Raises an exception if argument is not present.
448
+ #
449
+ def parse(arg, argv)
450
+ unless arg
451
+ raise MissingArgument if argv.empty?
452
+ arg = argv.shift
453
+ end
454
+ conv_arg(*parse_arg(arg) {|*exc| raise(*exc)})
455
+ end
456
+ end
457
+
458
+ #
459
+ # Switch that can omit argument.
460
+ #
461
+ class OptionalArgument < self
462
+
463
+ #
464
+ # Parses argument if given, or uses default value.
465
+ #
466
+ def parse(arg, argv, &error)
467
+ if arg
468
+ conv_arg(*parse_arg(arg, &error))
469
+ else
470
+ conv_arg(arg)
471
+ end
472
+ end
473
+ end
474
+
475
+ #
476
+ # Switch that takes an argument, which does not begin with '-'.
477
+ #
478
+ class PlacedArgument < self
479
+
480
+ #
481
+ # Returns nil if argument is not present or begins with '-'.
482
+ #
483
+ def parse(arg, argv, &error)
484
+ if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
485
+ return nil, block, nil
486
+ end
487
+ opt = (val = parse_arg(val, &error))[1]
488
+ val = conv_arg(*val)
489
+ if opt and !arg
490
+ argv.shift
491
+ else
492
+ val[0] = nil
493
+ end
494
+ val
495
+ end
496
+ end
497
+ end
498
+
499
+ #
500
+ # Simple option list providing mapping from short and/or long option
501
+ # string to OptionParser::Switch and mapping from acceptable argument to
502
+ # matching pattern and converter pair. Also provides summary feature.
503
+ #
504
+ class List
505
+ # Map from acceptable argument types to pattern and converter pairs.
506
+ attr_reader :atype
507
+
508
+ # Map from short style option switches to actual switch objects.
509
+ attr_reader :short
510
+
511
+ # Map from long style option switches to actual switch objects.
512
+ attr_reader :long
513
+
514
+ # List of all switches and summary string.
515
+ attr_reader :list
516
+
517
+ #
518
+ # Just initializes all instance variables.
519
+ #
520
+ def initialize
521
+ @atype = {}
522
+ @short = OptionMap.new
523
+ @long = OptionMap.new
524
+ @list = []
525
+ end
526
+
527
+ #
528
+ # See OptionParser.accept.
529
+ #
530
+ def accept(t, pat = /.*/nm, &block)
531
+ if pat
532
+ pat.respond_to?(:match) or raise TypeError, "has no `match'"
533
+ else
534
+ pat = t if t.respond_to?(:match)
535
+ end
536
+ unless block
537
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
538
+ end
539
+ @atype[t] = [pat, block]
540
+ end
541
+
542
+ #
543
+ # See OptionParser.reject.
544
+ #
545
+ def reject(t)
546
+ @atype.delete(t)
547
+ end
548
+
549
+ #
550
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
551
+ #
552
+ # +sw+:: OptionParser::Switch instance to be added.
553
+ # +sopts+:: Short style option list.
554
+ # +lopts+:: Long style option list.
555
+ # +nlopts+:: Negated long style options list.
556
+ #
557
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil)
558
+ o = nil
559
+ sopts.each {|o| @short[o] = sw} if sopts
560
+ lopts.each {|o| @long[o] = sw} if lopts
561
+ nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
562
+ used = @short.invert.update(@long.invert)
563
+ @list.delete_if {|o| Switch === o and !used[o]}
564
+ end
565
+ private :update
566
+
567
+ #
568
+ # Inserts +switch+ at the head of the list, and associates short, long
569
+ # and negated long options. Arguments are:
570
+ #
571
+ # +switch+:: OptionParser::Switch instance to be inserted.
572
+ # +short_opts+:: List of short style options.
573
+ # +long_opts+:: List of long style options.
574
+ # +nolong_opts+:: List of long style options with "no-" prefix.
575
+ #
576
+ # prepend(switch, short_opts, long_opts, nolong_opts)
577
+ #
578
+ def prepend(*args)
579
+ update(*args)
580
+ @list.unshift(args[0])
581
+ end
582
+
583
+ #
584
+ # Appends +switch+ at the tail of the list, and associates short, long
585
+ # and negated long options. Arguments are:
586
+ #
587
+ # +switch+:: OptionParser::Switch instance to be inserted.
588
+ # +short_opts+:: List of short style options.
589
+ # +long_opts+:: List of long style options.
590
+ # +nolong_opts+:: List of long style options with "no-" prefix.
591
+ #
592
+ # append(switch, short_opts, long_opts, nolong_opts)
593
+ #
594
+ def append(*args)
595
+ update(*args)
596
+ @list.push(args[0])
597
+ end
598
+
599
+ #
600
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
601
+ # block is given. If it isn't found, nil is returned.
602
+ #
603
+ def search(id, key)
604
+ if list = __send__(id)
605
+ val = list.fetch(key) {return nil}
606
+ block_given? ? yield(val) : val
607
+ end
608
+ end
609
+
610
+ #
611
+ # Searches list +id+ for +opt+ and the optional patterns for completion
612
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
613
+ # is returned or yielded if a block is given. If it isn't found, nil is
614
+ # returned.
615
+ #
616
+ def complete(id, opt, icase = false, *pat, &block)
617
+ __send__(id).complete(opt, icase, *pat, &block)
618
+ end
619
+
620
+ #
621
+ # Iterates over each option, passing the option to the +block+.
622
+ #
623
+ def each_option(&block)
624
+ list.each(&block)
625
+ end
626
+
627
+ #
628
+ # Creates the summary table, passing each line to the +block+ (without
629
+ # newline). The arguments +args+ are passed along to the summarize
630
+ # method which is called on every option.
631
+ #
632
+ def summarize(*args, &block)
633
+ list.each do |opt|
634
+ if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
635
+ opt.summarize(*args, &block)
636
+ elsif !opt or opt.empty?
637
+ yield("")
638
+ else
639
+ opt.each(&block)
640
+ end
641
+ end
642
+ end
643
+
644
+ def add_banner(to) # :nodoc:
645
+ list.each do |opt|
646
+ if opt.respond_to?(:add_banner)
647
+ opt.add_banner(to)
648
+ end
649
+ end
650
+ to
651
+ end
652
+ end
653
+
654
+ #
655
+ # Hash with completion search feature. See OptionParser::Completion.
656
+ #
657
+ class CompletingHash < Hash
658
+ include Completion
659
+
660
+ #
661
+ # Completion for hash key.
662
+ #
663
+ def match(key)
664
+ return key, *fetch(key) {
665
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
666
+ }
667
+ end
668
+ end
669
+
670
+ # :stopdoc:
671
+
672
+ #
673
+ # Enumeration of acceptable argument styles. Possible values are:
674
+ #
675
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
676
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
677
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
678
+ #
679
+ # Use like --switch=argument (long style) or -Xargument (short style). For
680
+ # short style, only portion matched to argument pattern is dealed as
681
+ # argument.
682
+ #
683
+ ArgumentStyle = {}
684
+ NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
685
+ RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
686
+ OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
687
+ ArgumentStyle.freeze
688
+
689
+ #
690
+ # Switches common used such as '--', and also provides default
691
+ # argument classes
692
+ #
693
+ DefaultList = List.new
694
+ DefaultList.short['-'] = Switch::NoArgument.new {}
695
+ DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
696
+
697
+ #
698
+ # Default options for ARGV, which never appear in option summary.
699
+ #
700
+ Officious = {}
701
+
702
+ #
703
+ # --help
704
+ # Shows option summary.
705
+ #
706
+ Officious['help'] = proc do |parser|
707
+ Switch::NoArgument.new do
708
+ puts parser.help
709
+ exit
710
+ end
711
+ end
712
+
713
+ #
714
+ # --version
715
+ # Shows version string if Version is defined.
716
+ #
717
+ Officious['version'] = proc do |parser|
718
+ Switch::OptionalArgument.new do |pkg|
719
+ if pkg
720
+ begin
721
+ require 'optparse/version'
722
+ rescue LoadError
723
+ else
724
+ show_version(*pkg.split(/,/)) or
725
+ abort("#{parser.program_name}: no version found in package #{pkg}")
726
+ exit
727
+ end
728
+ end
729
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
730
+ puts v
731
+ exit
732
+ end
733
+ end
734
+
735
+ # :startdoc:
736
+
737
+ #
738
+ # Class methods
739
+ #
740
+
741
+ #
742
+ # Initializes a new instance and evaluates the optional block in context
743
+ # of the instance. Arguments +args+ are passed to #new, see there for
744
+ # description of parameters.
745
+ #
746
+ # This method is *deprecated*, its behavior corresponds to the older #new
747
+ # method.
748
+ #
749
+ def self.with(*args, &block)
750
+ opts = new(*args)
751
+ opts.instance_eval(&block)
752
+ opts
753
+ end
754
+
755
+ #
756
+ # Returns an incremented value of +default+ according to +arg+.
757
+ #
758
+ def self.inc(arg, default = nil)
759
+ case arg
760
+ when Integer
761
+ arg.nonzero?
762
+ when nil
763
+ default.to_i + 1
764
+ end
765
+ end
766
+ def inc(*args)
767
+ self.class.inc(*args)
768
+ end
769
+
770
+ #
771
+ # Initializes the instance and yields itself if called with a block.
772
+ #
773
+ # +banner+:: Banner message.
774
+ # +width+:: Summary width.
775
+ # +indent+:: Summary indent.
776
+ #
777
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
778
+ @stack = [DefaultList, List.new, List.new]
779
+ @program_name = nil
780
+ @banner = banner
781
+ @summary_width = width
782
+ @summary_indent = indent
783
+ @default_argv = ARGV
784
+ add_officious
785
+ yield self if block_given?
786
+ end
787
+
788
+ def add_officious # :nodoc:
789
+ list = base()
790
+ Officious.each do |opt, block|
791
+ list.long[opt] ||= block.call(self)
792
+ end
793
+ end
794
+
795
+ #
796
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
797
+ # back to be the first non-option argument.
798
+ #
799
+ def terminate(arg = nil)
800
+ self.class.terminate(arg)
801
+ end
802
+ def self.terminate(arg = nil)
803
+ throw :terminate, arg
804
+ end
805
+
806
+ @stack = [DefaultList]
807
+ def self.top() DefaultList end
808
+
809
+ #
810
+ # Directs to accept specified class +t+. The argument string is passed to
811
+ # the block in which it should be converted to the desired class.
812
+ #
813
+ # +t+:: Argument class specifier, any object including Class.
814
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
815
+ #
816
+ # accept(t, pat, &block)
817
+ #
818
+ def accept(*args, &blk) top.accept(*args, &blk) end
819
+ #
820
+ # See #accept.
821
+ #
822
+ def self.accept(*args, &blk) top.accept(*args, &blk) end
823
+
824
+ #
825
+ # Directs to reject specified class argument.
826
+ #
827
+ # +t+:: Argument class speficier, any object including Class.
828
+ #
829
+ # reject(t)
830
+ #
831
+ def reject(*args, &blk) top.reject(*args, &blk) end
832
+ #
833
+ # See #reject.
834
+ #
835
+ def self.reject(*args, &blk) top.reject(*args, &blk) end
836
+
837
+ #
838
+ # Instance methods
839
+ #
840
+
841
+ # Heading banner preceding summary.
842
+ attr_writer :banner
843
+
844
+ # Program name to be emitted in error message and default banner,
845
+ # defaults to $0.
846
+ attr_writer :program_name
847
+
848
+ # Width for option list portion of summary. Must be Numeric.
849
+ attr_accessor :summary_width
850
+
851
+ # Indentation for summary. Must be String (or have + String method).
852
+ attr_accessor :summary_indent
853
+
854
+ # Strings to be parsed in default.
855
+ attr_accessor :default_argv
856
+
857
+ #
858
+ # Heading banner preceding summary.
859
+ #
860
+ def banner
861
+ unless @banner
862
+ @banner = "Usage: #{program_name} [options]"
863
+ visit(:add_banner, @banner)
864
+ end
865
+ @banner
866
+ end
867
+
868
+ #
869
+ # Program name to be emitted in error message and default banner, defaults
870
+ # to $0.
871
+ #
872
+ def program_name
873
+ @program_name || File.basename($0, '.*')
874
+ end
875
+
876
+ # for experimental cascading :-)
877
+ alias set_banner banner=
878
+ alias set_program_name program_name=
879
+ alias set_summary_width summary_width=
880
+ alias set_summary_indent summary_indent=
881
+
882
+ # Version
883
+ attr_writer :version
884
+ # Release code
885
+ attr_writer :release
886
+
887
+ #
888
+ # Version
889
+ #
890
+ def version
891
+ @version || (defined?(::Version) && ::Version)
892
+ end
893
+
894
+ #
895
+ # Release code
896
+ #
897
+ def release
898
+ @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
899
+ end
900
+
901
+ #
902
+ # Returns version string from program_name, version and release.
903
+ #
904
+ def ver
905
+ if v = version
906
+ str = "#{program_name} #{[v].join('.')}"
907
+ str << " (#{v})" if v = release
908
+ str
909
+ end
910
+ end
911
+
912
+ def warn(mesg = $!)
913
+ super("#{program_name}: #{mesg}")
914
+ end
915
+
916
+ def abort(mesg = $!)
917
+ super("#{program_name}: #{mesg}")
918
+ end
919
+
920
+ #
921
+ # Subject of #on / #on_head, #accept / #reject
922
+ #
923
+ def top
924
+ @stack[-1]
925
+ end
926
+
927
+ #
928
+ # Subject of #on_tail.
929
+ #
930
+ def base
931
+ @stack[1]
932
+ end
933
+
934
+ #
935
+ # Pushes a new List.
936
+ #
937
+ def new
938
+ @stack.push(List.new)
939
+ if block_given?
940
+ yield self
941
+ else
942
+ self
943
+ end
944
+ end
945
+
946
+ #
947
+ # Removes the last List.
948
+ #
949
+ def remove
950
+ @stack.pop
951
+ end
952
+
953
+ #
954
+ # Puts option summary into +to+ and returns +to+. Yields each line if
955
+ # a block is given.
956
+ #
957
+ # +to+:: Output destination, which must have method <<. Defaults to [].
958
+ # +width+:: Width of left side, defaults to @summary_width.
959
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
960
+ # +indent+:: Indentation, defaults to @summary_indent.
961
+ #
962
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
963
+ visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/}))
964
+ to
965
+ end
966
+
967
+ #
968
+ # Returns option summary string.
969
+ #
970
+ def help; summarize(banner.to_s.sub(/\n?\z/, "\n")) end
971
+ alias to_s help
972
+
973
+ #
974
+ # Returns option summary list.
975
+ #
976
+ def to_a; summarize(banner.to_a.dup) end
977
+
978
+ #
979
+ # Checks if an argument is given twice, in which case an ArgumentError is
980
+ # raised. Called from OptionParser#switch only.
981
+ #
982
+ # +obj+:: New argument.
983
+ # +prv+:: Previously specified argument.
984
+ # +msg+:: Exception message.
985
+ #
986
+ def notwice(obj, prv, msg)
987
+ unless !prv or prv == obj
988
+ begin
989
+ raise ArgumentError, "argument #{msg} given twice: #{obj}"
990
+ rescue
991
+ $@[0, 2] = nil
992
+ raise
993
+ end
994
+ end
995
+ obj
996
+ end
997
+ private :notwice
998
+
999
+ #
1000
+ # Creates an OptionParser::Switch from the parameters. The parsed argument
1001
+ # value is passed to the given block, where it can be processed.
1002
+ #
1003
+ # See at the beginning of OptionParser for some full examples.
1004
+ #
1005
+ # +opts+ can include the following elements:
1006
+ #
1007
+ # [Argument style:]
1008
+ # One of the following:
1009
+ # :NONE, :REQUIRED, :OPTIONAL
1010
+ #
1011
+ # [Argument pattern:]
1012
+ # Acceptable option argument format, must be pre-defined with
1013
+ # OptionParser.accept or OptionParser#accept, or Regexp. This can appear
1014
+ # once or assigned as String if not present, otherwise causes an
1015
+ # ArgumentError. Examples:
1016
+ # Float, Time, Array
1017
+ #
1018
+ # [Possible argument values:]
1019
+ # Hash or Array.
1020
+ # [:text, :binary, :auto]
1021
+ # %w[iso-2022-jp shift_jis euc-jp utf8 binary]
1022
+ # { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
1023
+ #
1024
+ # [Long style switch:]
1025
+ # Specifies a long style switch which takes a mandatory, optional or no
1026
+ # argument. It's a string of the following form:
1027
+ # "--switch=MANDATORY" or "--switch MANDATORY"
1028
+ # "--switch[=OPTIONAL]"
1029
+ # "--switch"
1030
+ #
1031
+ # [Short style switch:]
1032
+ # Specifies short style switch which takes a mandatory, optional or no
1033
+ # argument. It's a string of the following form:
1034
+ # "-xMANDATORY"
1035
+ # "-x[OPTIONAL]"
1036
+ # "-x"
1037
+ # There is also a special form which matches character range (not full
1038
+ # set of regural expression):
1039
+ # "-[a-z]MANDATORY"
1040
+ # "-[a-z][OPTIONAL]"
1041
+ # "-[a-z]"
1042
+ #
1043
+ # [Argument style and description:]
1044
+ # Instead of specifying mandatory or optional orguments directly in the
1045
+ # switch parameter, this separate parameter can be used.
1046
+ # "=MANDATORY"
1047
+ # "=[OPTIONAL]"
1048
+ #
1049
+ # [Description:]
1050
+ # Description string for the option.
1051
+ # "Run verbosely"
1052
+ #
1053
+ # [Handler:]
1054
+ # Handler for the parsed argument value. Either give a block or pass a
1055
+ # Proc or Method as an argument.
1056
+ #
1057
+ def make_switch(opts, block = nil)
1058
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
1059
+ ldesc, sdesc, desc, arg = [], [], []
1060
+ default_style = Switch::NoArgument
1061
+ default_pattern = nil
1062
+ klass = nil
1063
+ o = nil
1064
+ n, q, a = nil
1065
+
1066
+ opts.each do |o|
1067
+ # argument class
1068
+ next if search(:atype, o) do |pat, c|
1069
+ klass = notwice(o, klass, 'type')
1070
+ if not_style and not_style != Switch::NoArgument
1071
+ not_pattern, not_conv = pat, c
1072
+ else
1073
+ default_pattern, conv = pat, c
1074
+ end
1075
+ end
1076
+
1077
+ # directly specified pattern(any object possible to match)
1078
+ if !(String === o) and o.respond_to?(:match)
1079
+ pattern = notwice(o, pattern, 'pattern')
1080
+ conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
1081
+ next
1082
+ end
1083
+
1084
+ # anything others
1085
+ case o
1086
+ when Proc, Method
1087
+ block = notwice(o, block, 'block')
1088
+ when Array, Hash
1089
+ case pattern
1090
+ when CompletingHash
1091
+ when nil
1092
+ pattern = CompletingHash.new
1093
+ conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
1094
+ else
1095
+ raise ArgumentError, "argument pattern given twice"
1096
+ end
1097
+ o.each {|(o, *v)| pattern[o] = v.fetch(0) {o}}
1098
+ when Module
1099
+ raise ArgumentError, "unsupported argument type: #{o}"
1100
+ when *ArgumentStyle.keys
1101
+ style = notwice(ArgumentStyle[o], style, 'style')
1102
+ when /^--no-([^\[\]=\s]*)(.+)?/
1103
+ q, a = $1, $2
1104
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1105
+ not_pattern, not_conv = search(:atype, o) unless not_style
1106
+ not_style = (not_style || default_style).guess(arg = a) if a
1107
+ default_style = Switch::NoArgument
1108
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
1109
+ ldesc << "--no-#{q}"
1110
+ long << 'no-' + (q = q.downcase)
1111
+ nolong << q
1112
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
1113
+ q, a = $1, $2
1114
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1115
+ if a
1116
+ default_style = default_style.guess(arg = a)
1117
+ default_pattern, conv = search(:atype, o) unless default_pattern
1118
+ end
1119
+ ldesc << "--[no-]#{q}"
1120
+ long << (o = q.downcase)
1121
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
1122
+ not_style = Switch::NoArgument
1123
+ nolong << 'no-' + o
1124
+ when /^--([^\[\]=\s]*)(.+)?/
1125
+ q, a = $1, $2
1126
+ if a
1127
+ o = notwice(NilClass, klass, 'type')
1128
+ default_style = default_style.guess(arg = a)
1129
+ default_pattern, conv = search(:atype, o) unless default_pattern
1130
+ end
1131
+ ldesc << "--#{q}"
1132
+ long << (o = q.downcase)
1133
+ when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
1134
+ q, a = $1, $2
1135
+ o = notwice(Object, klass, 'type')
1136
+ if a
1137
+ default_style = default_style.guess(arg = a)
1138
+ default_pattern, conv = search(:atype, o) unless default_pattern
1139
+ end
1140
+ sdesc << "-#{q}"
1141
+ short << Regexp.new(q)
1142
+ when /^-(.)(.+)?/
1143
+ q, a = $1, $2
1144
+ if a
1145
+ o = notwice(NilClass, klass, 'type')
1146
+ default_style = default_style.guess(arg = a)
1147
+ default_pattern, conv = search(:atype, o) unless default_pattern
1148
+ end
1149
+ sdesc << "-#{q}"
1150
+ short << q
1151
+ when /^=/
1152
+ style = notwice(default_style.guess(arg = o), style, 'style')
1153
+ default_pattern, conv = search(:atype, Object) unless default_pattern
1154
+ else
1155
+ desc.push(o)
1156
+ end
1157
+ end
1158
+
1159
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
1160
+ if !(short.empty? and long.empty?)
1161
+ s = (style || default_style).new(pattern || default_pattern,
1162
+ conv, sdesc, ldesc, arg, desc, block)
1163
+ elsif !block
1164
+ raise ArgumentError, "no switch given" if style or pattern
1165
+ s = desc
1166
+ else
1167
+ short << pattern
1168
+ s = (style || default_style).new(pattern,
1169
+ conv, nil, nil, arg, desc, block)
1170
+ end
1171
+ return s, short, long,
1172
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
1173
+ nolong
1174
+ end
1175
+
1176
+ def define(*opts, &block)
1177
+ top.append(*(sw = make_switch(opts, block)))
1178
+ sw[0]
1179
+ end
1180
+
1181
+ #
1182
+ # Add option switch and handler. See #make_switch for an explanation of
1183
+ # parameters.
1184
+ #
1185
+ def on(*opts, &block)
1186
+ define(*opts, &block)
1187
+ self
1188
+ end
1189
+ alias def_option define
1190
+
1191
+ def define_head(*opts, &block)
1192
+ top.prepend(*(sw = make_switch(opts, block)))
1193
+ sw[0]
1194
+ end
1195
+
1196
+ #
1197
+ # Add option switch like with #on, but at head of summary.
1198
+ #
1199
+ def on_head(*opts, &block)
1200
+ define_head(*opts, &block)
1201
+ self
1202
+ end
1203
+ alias def_head_option define_head
1204
+
1205
+ def define_tail(*opts, &block)
1206
+ base.append(*(sw = make_switch(opts, block)))
1207
+ sw[0]
1208
+ end
1209
+
1210
+ #
1211
+ # Add option switch like with #on, but at tail of summary.
1212
+ #
1213
+ def on_tail(*opts, &block)
1214
+ define_tail(*opts, &block)
1215
+ self
1216
+ end
1217
+ alias def_tail_option define_tail
1218
+
1219
+ #
1220
+ # Add separator in summary.
1221
+ #
1222
+ def separator(string)
1223
+ top.append(string, nil, nil)
1224
+ end
1225
+
1226
+ #
1227
+ # Parses command line arguments +argv+ in order. When a block is given,
1228
+ # each non-option argument is yielded.
1229
+ #
1230
+ # Returns the rest of +argv+ left unparsed.
1231
+ #
1232
+ def order(*argv, &block)
1233
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1234
+ order!(argv, &block)
1235
+ end
1236
+
1237
+ #
1238
+ # Same as #order, but removes switches destructively.
1239
+ #
1240
+ def order!(argv = default_argv, &nonopt)
1241
+ parse_in_order(argv, &nonopt)
1242
+ end
1243
+
1244
+ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
1245
+ opt, arg, sw, val, rest = nil
1246
+ nonopt ||= proc {|arg| throw :terminate, arg}
1247
+ argv.unshift(arg) if arg = catch(:terminate) {
1248
+ while arg = argv.shift
1249
+ case arg
1250
+ # long option
1251
+ when /\A--([^=]*)(?:=(.*))?/nm
1252
+ opt, rest = $1, $2
1253
+ begin
1254
+ sw, = complete(:long, opt, true)
1255
+ rescue ParseError
1256
+ raise $!.set_option(arg, true)
1257
+ end
1258
+ begin
1259
+ opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
1260
+ val = cb.call(val) if cb
1261
+ setter.call(sw.switch_name, val) if setter
1262
+ rescue ParseError
1263
+ raise $!.set_option(arg, rest)
1264
+ end
1265
+
1266
+ # short option
1267
+ when /\A-(.)((=).*|.+)?/nm
1268
+ opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2
1269
+ begin
1270
+ sw, = search(:short, opt)
1271
+ unless sw
1272
+ begin
1273
+ sw, = complete(:short, opt)
1274
+ # short option matched.
1275
+ val = arg.sub(/\A-/, '')
1276
+ has_arg = true
1277
+ rescue InvalidOption
1278
+ # if no short options match, try completion with long
1279
+ # options.
1280
+ sw, = complete(:long, opt)
1281
+ eq ||= !rest
1282
+ end
1283
+ end
1284
+ rescue ParseError
1285
+ raise $!.set_option(arg, true)
1286
+ end
1287
+ begin
1288
+ opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
1289
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
1290
+ argv.unshift(opt) if opt and (opt = opt.sub(/\A-*/, '-')) != '-'
1291
+ val = cb.call(val) if cb
1292
+ setter.call(sw.switch_name, val) if setter
1293
+ rescue ParseError
1294
+ raise $!.set_option(arg, arg.length > 2)
1295
+ end
1296
+
1297
+ # non-option argument
1298
+ else
1299
+ catch(:prune) do
1300
+ visit(:each_option) do |sw|
1301
+ sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
1302
+ end
1303
+ nonopt.call(arg)
1304
+ end
1305
+ end
1306
+ end
1307
+
1308
+ nil
1309
+ }
1310
+
1311
+ visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
1312
+
1313
+ argv
1314
+ end
1315
+ private :parse_in_order
1316
+
1317
+ #
1318
+ # Parses command line arguments +argv+ in permutation mode and returns
1319
+ # list of non-option arguments.
1320
+ #
1321
+ def permute(*argv)
1322
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1323
+ permute!(argv)
1324
+ end
1325
+
1326
+ #
1327
+ # Same as #permute, but removes switches destructively.
1328
+ #
1329
+ def permute!(argv = default_argv)
1330
+ nonopts = []
1331
+ arg = nil
1332
+ order!(argv) {|arg| nonopts << arg}
1333
+ argv[0, 0] = nonopts
1334
+ argv
1335
+ end
1336
+
1337
+ #
1338
+ # Parses command line arguments +argv+ in order when environment variable
1339
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
1340
+ #
1341
+ def parse(*argv)
1342
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1343
+ parse!(argv)
1344
+ end
1345
+
1346
+ #
1347
+ # Same as #parse, but removes switches destructively.
1348
+ #
1349
+ def parse!(argv = default_argv)
1350
+ if ENV.include?('POSIXLY_CORRECT')
1351
+ order!(argv)
1352
+ else
1353
+ permute!(argv)
1354
+ end
1355
+ end
1356
+
1357
+ #
1358
+ # Wrapper method for getopts.rb.
1359
+ #
1360
+ # params = ARGV.getopts("ab:", "foo", "bar:")
1361
+ # # params[:a] = true # -a
1362
+ # # params[:b] = "1" # -b1
1363
+ # # params[:foo] = "1" # --foo
1364
+ # # params[:bar] = "x" # --bar x
1365
+ #
1366
+ def getopts(*args)
1367
+ argv = Array === args.first ? args.shift : default_argv
1368
+ single_options, *long_options = *args
1369
+
1370
+ result = {}
1371
+
1372
+ single_options.scan(/(.)(:)?/) do |opt, val|
1373
+ if val
1374
+ result[opt] = nil
1375
+ define("-#{opt} VAL")
1376
+ else
1377
+ result[opt] = false
1378
+ define("-#{opt}")
1379
+ end
1380
+ end if single_options
1381
+
1382
+ long_options.each do |arg|
1383
+ opt, val = arg.split(':', 2)
1384
+ if val
1385
+ result[opt] = val.empty? ? nil : val
1386
+ define("--#{opt} VAL")
1387
+ else
1388
+ result[opt] = false
1389
+ define("--#{opt}")
1390
+ end
1391
+ end
1392
+
1393
+ parse_in_order(argv, result.method(:[]=))
1394
+ result
1395
+ end
1396
+
1397
+ #
1398
+ # See #getopts.
1399
+ #
1400
+ def self.getopts(*args)
1401
+ new.getopts(*args)
1402
+ end
1403
+
1404
+ #
1405
+ # Traverses @stack, sending each element method +id+ with +args+ and
1406
+ # +block+.
1407
+ #
1408
+ def visit(id, *args, &block)
1409
+ el = nil
1410
+ @stack.reverse_each do |el|
1411
+ el.send(id, *args, &block)
1412
+ end
1413
+ nil
1414
+ end
1415
+ private :visit
1416
+
1417
+ #
1418
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
1419
+ #
1420
+ def search(id, key)
1421
+ block_given = block_given?
1422
+ visit(:search, id, key) do |k|
1423
+ return block_given ? yield(k) : k
1424
+ end
1425
+ end
1426
+ private :search
1427
+
1428
+ #
1429
+ # Completes shortened long style option switch and returns pair of
1430
+ # canonical switch and switch descriptor OptionParser::Switch.
1431
+ #
1432
+ # +id+:: Searching table.
1433
+ # +opt+:: Searching key.
1434
+ # +icase+:: Search case insensitive if true.
1435
+ # +pat+:: Optional pattern for completion.
1436
+ #
1437
+ def complete(typ, opt, icase = false, *pat)
1438
+ if pat.empty?
1439
+ search(typ, opt) {|sw| return [sw, opt]} # exact match or...
1440
+ end
1441
+ raise AmbiguousOption, catch(:ambiguous) {
1442
+ visit(:complete, typ, opt, icase, *pat) {|opt, *sw| return sw}
1443
+ raise InvalidOption, opt
1444
+ }
1445
+ end
1446
+ private :complete
1447
+
1448
+ #
1449
+ # Loads options from file names as +filename+. Does nothing when the file
1450
+ # is not present. Returns whether successfully loaded.
1451
+ #
1452
+ # +filename+ defaults to basename of the program without suffix in a
1453
+ # directory ~/.options.
1454
+ #
1455
+ def load(filename = nil)
1456
+ begin
1457
+ filename ||= File.expand_path(File.basename($0, '.*'), '~/.options')
1458
+ rescue
1459
+ return false
1460
+ end
1461
+ begin
1462
+ parse(*IO.readlines(filename).each {|s| s.chomp!})
1463
+ true
1464
+ rescue Errno::ENOENT, Errno::ENOTDIR
1465
+ false
1466
+ end
1467
+ end
1468
+
1469
+ #
1470
+ # Parses environment variable +env+ or its uppercase with splitting like a
1471
+ # shell.
1472
+ #
1473
+ # +env+ defaults to the basename of the program.
1474
+ #
1475
+ def environment(env = File.basename($0, '.*'))
1476
+ env = ENV[env] || ENV[env.upcase] or return
1477
+ parse(*Shellwords.shellwords(env))
1478
+ end
1479
+
1480
+ #
1481
+ # Acceptable argument classes
1482
+ #
1483
+
1484
+ #
1485
+ # Any string and no conversion. This is fall-back.
1486
+ #
1487
+ accept(Object) {|s,|s or s.nil?}
1488
+
1489
+ accept(NilClass) {|s,|s}
1490
+
1491
+ #
1492
+ # Any non-empty string, and no conversion.
1493
+ #
1494
+ accept(String, /.+/nm) {|s,*|s}
1495
+
1496
+ #
1497
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
1498
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
1499
+ # Integer.
1500
+ #
1501
+ decimal = '\d+(?:_\d+)*'
1502
+ binary = 'b[01]+(?:_[01]+)*'
1503
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
1504
+ octal = "0(?:[0-7]*(?:_[0-7]+)*|#{binary}|#{hex})"
1505
+ integer = "#{octal}|#{decimal}"
1506
+ accept(Integer, %r"\A[-+]?(?:#{integer})"io) {|s,| Integer(s) if s}
1507
+
1508
+ #
1509
+ # Float number format, and converts to Float.
1510
+ #
1511
+ float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
1512
+ floatpat = %r"\A[-+]?#{float}"io
1513
+ accept(Float, floatpat) {|s,| s.to_f if s}
1514
+
1515
+ #
1516
+ # Generic numeric format, converts to Integer for integer format, Float
1517
+ # for float format.
1518
+ #
1519
+ accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s,| eval(s) if s}
1520
+
1521
+ #
1522
+ # Decimal integer format, to be converted to Integer.
1523
+ #
1524
+ DecimalInteger = /\A[-+]?#{decimal}/io
1525
+ accept(DecimalInteger) {|s,| s.to_i if s}
1526
+
1527
+ #
1528
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
1529
+ # Integer.
1530
+ #
1531
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io
1532
+ accept(OctalInteger) {|s,| s.oct if s}
1533
+
1534
+ #
1535
+ # Decimal integer/float number format, to be converted to Integer for
1536
+ # integer format, Float for float format.
1537
+ #
1538
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
1539
+ accept(DecimalNumeric) {|s,| eval(s) if s}
1540
+
1541
+ #
1542
+ # Boolean switch, which means whether it is present or not, whether it is
1543
+ # absent or not with prefix no-, or it takes an argument
1544
+ # yes/no/true/false/+/-.
1545
+ #
1546
+ yesno = CompletingHash.new
1547
+ %w[- no false].each {|el| yesno[el] = false}
1548
+ %w[+ yes true].each {|el| yesno[el] = true}
1549
+ yesno['nil'] = false # shoud be nil?
1550
+ accept(TrueClass, yesno) {|arg, val| val == nil or val}
1551
+ #
1552
+ # Similar to TrueClass, but defaults to false.
1553
+ #
1554
+ accept(FalseClass, yesno) {|arg, val| val != nil and val}
1555
+
1556
+ #
1557
+ # List of strings separated by ",".
1558
+ #
1559
+ accept(Array) do |s,|
1560
+ if s
1561
+ s = s.split(',').collect {|s| s unless s.empty?}
1562
+ end
1563
+ s
1564
+ end
1565
+
1566
+ #
1567
+ # Regular expression with options.
1568
+ #
1569
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
1570
+ f = 0
1571
+ if o
1572
+ f |= Regexp::IGNORECASE if /i/ =~ o
1573
+ f |= Regexp::MULTILINE if /m/ =~ o
1574
+ f |= Regexp::EXTENDED if /x/ =~ o
1575
+ k = o.delete("^imx")
1576
+ end
1577
+ Regexp.new(s || all, f, k)
1578
+ end
1579
+
1580
+ #
1581
+ # Exceptions
1582
+ #
1583
+
1584
+ #
1585
+ # Base class of exceptions from OptionParser.
1586
+ #
1587
+ class ParseError < RuntimeError
1588
+ # Reason which caused the error.
1589
+ Reason = 'parse error'.freeze
1590
+
1591
+ def initialize(*args)
1592
+ @args = args
1593
+ @reason = nil
1594
+ end
1595
+
1596
+ attr_reader :args
1597
+ attr_writer :reason
1598
+
1599
+ #
1600
+ # Pushes back erred argument(s) to +argv+.
1601
+ #
1602
+ def recover(argv)
1603
+ argv[0, 0] = @args
1604
+ argv
1605
+ end
1606
+
1607
+ def set_option(opt, eq)
1608
+ if eq
1609
+ @args[0] = opt
1610
+ else
1611
+ @args.unshift(opt)
1612
+ end
1613
+ self
1614
+ end
1615
+
1616
+ #
1617
+ # Returns error reason. Override this for I18N.
1618
+ #
1619
+ def reason
1620
+ @reason || self.class::Reason
1621
+ end
1622
+
1623
+ def inspect
1624
+ "#<#{self.class.to_s}: #{args.join(' ')}>"
1625
+ end
1626
+
1627
+ #
1628
+ # Default stringizing method to emit standard error message.
1629
+ #
1630
+ def message
1631
+ reason + ': ' + args.join(' ')
1632
+ end
1633
+
1634
+ alias to_s message
1635
+ end
1636
+
1637
+ #
1638
+ # Raises when ambiguously completable string is encountered.
1639
+ #
1640
+ class AmbiguousOption < ParseError
1641
+ const_set(:Reason, 'ambiguous option'.freeze)
1642
+ end
1643
+
1644
+ #
1645
+ # Raises when there is an argument for a switch which takes no argument.
1646
+ #
1647
+ class NeedlessArgument < ParseError
1648
+ const_set(:Reason, 'needless argument'.freeze)
1649
+ end
1650
+
1651
+ #
1652
+ # Raises when a switch with mandatory argument has no argument.
1653
+ #
1654
+ class MissingArgument < ParseError
1655
+ const_set(:Reason, 'missing argument'.freeze)
1656
+ end
1657
+
1658
+ #
1659
+ # Raises when switch is undefined.
1660
+ #
1661
+ class InvalidOption < ParseError
1662
+ const_set(:Reason, 'invalid option'.freeze)
1663
+ end
1664
+
1665
+ #
1666
+ # Raises when the given argument does not match required format.
1667
+ #
1668
+ class InvalidArgument < ParseError
1669
+ const_set(:Reason, 'invalid argument'.freeze)
1670
+ end
1671
+
1672
+ #
1673
+ # Raises when the given argument word can't be completed uniquely.
1674
+ #
1675
+ class AmbiguousArgument < InvalidArgument
1676
+ const_set(:Reason, 'ambiguous argument'.freeze)
1677
+ end
1678
+
1679
+ #
1680
+ # Miscellaneous
1681
+ #
1682
+
1683
+ #
1684
+ # Extends command line arguments array (ARGV) to parse itself.
1685
+ #
1686
+ module Arguable
1687
+
1688
+ #
1689
+ # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods
1690
+ # OptionParser::Arguable#options and OptionParser::Arguable#options= are
1691
+ # undefined. Thus, there is no ways to access the OptionParser object
1692
+ # via the receiver object.
1693
+ #
1694
+ def options=(opt)
1695
+ unless @optparse = opt
1696
+ class << self
1697
+ undef_method(:options)
1698
+ undef_method(:options=)
1699
+ end
1700
+ end
1701
+ end
1702
+
1703
+ #
1704
+ # Actual OptionParser object, automatically created if nonexistent.
1705
+ #
1706
+ # If called with a block, yields the OptionParser object and returns the
1707
+ # result of the block. If an OptionParser::ParseError exception occurs
1708
+ # in the block, it is rescued, a error message printed to STDERR and
1709
+ # +nil+ returned.
1710
+ #
1711
+ def options
1712
+ @optparse ||= OptionParser.new
1713
+ @optparse.default_argv = self
1714
+ block_given? or return @optparse
1715
+ begin
1716
+ yield @optparse
1717
+ rescue ParseError
1718
+ @optparse.warn $!
1719
+ nil
1720
+ end
1721
+ end
1722
+
1723
+ #
1724
+ # Parses +self+ destructively in order and returns +self+ containing the
1725
+ # rest arguments left unparsed.
1726
+ #
1727
+ def order!(&blk) options.order!(self, &blk) end
1728
+
1729
+ #
1730
+ # Parses +self+ destructively in permutation mode and returns +self+
1731
+ # containing the rest arguments left unparsed.
1732
+ #
1733
+ def permute!() options.permute!(self) end
1734
+
1735
+ #
1736
+ # Parses +self+ destructively and returns +self+ containing the
1737
+ # rest arguments left unparsed.
1738
+ #
1739
+ def parse!() options.parse!(self) end
1740
+
1741
+ #
1742
+ # Substitution of getopts is possible as follows. Also see
1743
+ # OptionParser#getopts.
1744
+ #
1745
+ # def getopts(*args)
1746
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
1747
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
1748
+ # end
1749
+ # rescue OptionParser::ParseError
1750
+ # end
1751
+ #
1752
+ def getopts(*args)
1753
+ options.getopts(self, *args)
1754
+ end
1755
+
1756
+ #
1757
+ # Initializes instance variable.
1758
+ #
1759
+ def self.extend_object(obj)
1760
+ super
1761
+ obj.instance_eval {@optparse = nil}
1762
+ end
1763
+ def initialize(*args)
1764
+ super
1765
+ @optparse = nil
1766
+ end
1767
+ end
1768
+
1769
+ #
1770
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
1771
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
1772
+ #
1773
+ module Acceptables
1774
+ const_set(:DecimalInteger, OptionParser::DecimalInteger)
1775
+ const_set(:OctalInteger, OptionParser::OctalInteger)
1776
+ const_set(:DecimalNumeric, OptionParser::DecimalNumeric)
1777
+ end
1778
+ end
1779
+
1780
+ # ARGV is arguable by OptionParser
1781
+ ARGV.extend(OptionParser::Arguable)
1782
+
1783
+ if $0 == __FILE__
1784
+ Version = OptionParser::Version
1785
+ ARGV.options {|q|
1786
+ q.parse!.empty? or puts "what's #{ARGV.join(' ')}?"
1787
+ } or abort(ARGV.options.to_s)
1788
+ end