reek 0.2.0 → 0.2.1

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.
@@ -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