reek 6.0.3 → 6.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (217) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/CONTRIBUTING.md +3 -0
  4. data/Dockerfile +1 -1
  5. data/Gemfile +6 -6
  6. data/lib/reek/ast/ast_node_class_map.rb +1 -1
  7. data/lib/reek/report/code_climate/code_climate_formatter.rb +1 -3
  8. data/lib/reek/smell_detectors/base_detector.rb +1 -1
  9. data/lib/reek/smell_warning.rb +1 -1
  10. data/lib/reek/source/source_locator.rb +1 -3
  11. data/lib/reek/version.rb +1 -1
  12. data/reek.gemspec +4 -1
  13. metadata +3 -208
  14. data/docs/API.md +0 -174
  15. data/docs/Attribute.md +0 -39
  16. data/docs/Basic-Smell-Options.md +0 -85
  17. data/docs/Boolean-Parameter.md +0 -54
  18. data/docs/Class-Variable.md +0 -40
  19. data/docs/Code-Smells.md +0 -39
  20. data/docs/Command-Line-Options.md +0 -119
  21. data/docs/Control-Couple.md +0 -26
  22. data/docs/Control-Parameter.md +0 -32
  23. data/docs/Data-Clump.md +0 -46
  24. data/docs/Duplicate-Method-Call.md +0 -264
  25. data/docs/Feature-Envy.md +0 -93
  26. data/docs/How-To-Write-New-Detectors.md +0 -132
  27. data/docs/How-reek-works-internally.md +0 -114
  28. data/docs/Instance-Variable-Assumption.md +0 -163
  29. data/docs/Irresponsible-Module.md +0 -47
  30. data/docs/Large-Class.md +0 -16
  31. data/docs/Long-Parameter-List.md +0 -39
  32. data/docs/Long-Yield-List.md +0 -37
  33. data/docs/Manual-Dispatch.md +0 -30
  34. data/docs/Missing-Safe-Method.md +0 -92
  35. data/docs/Module-Initialize.md +0 -62
  36. data/docs/Nested-Iterators.md +0 -59
  37. data/docs/Nil-Check.md +0 -47
  38. data/docs/RSpec-matchers.md +0 -129
  39. data/docs/Rake-Task.md +0 -66
  40. data/docs/Reek-4-to-Reek-5-migration.md +0 -188
  41. data/docs/Reek-Driven-Development.md +0 -46
  42. data/docs/Repeated-Conditional.md +0 -47
  43. data/docs/Simulated-Polymorphism.md +0 -16
  44. data/docs/Smell-Suppression.md +0 -96
  45. data/docs/Style-Guide.md +0 -19
  46. data/docs/Subclassed-From-Core-Class.md +0 -79
  47. data/docs/Too-Many-Constants.md +0 -37
  48. data/docs/Too-Many-Instance-Variables.md +0 -43
  49. data/docs/Too-Many-Methods.md +0 -56
  50. data/docs/Too-Many-Statements.md +0 -54
  51. data/docs/Uncommunicative-Method-Name.md +0 -94
  52. data/docs/Uncommunicative-Module-Name.md +0 -92
  53. data/docs/Uncommunicative-Name.md +0 -18
  54. data/docs/Uncommunicative-Parameter-Name.md +0 -90
  55. data/docs/Uncommunicative-Variable-Name.md +0 -96
  56. data/docs/Unused-Parameters.md +0 -28
  57. data/docs/Unused-Private-Method.md +0 -101
  58. data/docs/Utility-Function.md +0 -56
  59. data/docs/Versioning-Policy.md +0 -7
  60. data/docs/YAML-Reports.md +0 -93
  61. data/docs/defaults.reek.yml +0 -129
  62. data/docs/templates/default/docstring/html/public_api_marker.erb +0 -3
  63. data/docs/templates/default/docstring/setup.rb +0 -35
  64. data/docs/templates/default/fulldoc/html/css/common.css +0 -1
  65. data/docs/yard_plugin.rb +0 -17
  66. data/features/command_line_interface/basic_usage.feature +0 -15
  67. data/features/command_line_interface/options.feature +0 -123
  68. data/features/command_line_interface/show_progress.feature +0 -33
  69. data/features/command_line_interface/smell_selection.feature +0 -15
  70. data/features/command_line_interface/smells_count.feature +0 -38
  71. data/features/command_line_interface/stdin.feature +0 -65
  72. data/features/configuration_files/accept_setting.feature +0 -87
  73. data/features/configuration_files/directory_specific_directives.feature +0 -274
  74. data/features/configuration_files/exclude_directives.feature +0 -35
  75. data/features/configuration_files/exclude_paths_directives.feature +0 -42
  76. data/features/configuration_files/masking_smells.feature +0 -94
  77. data/features/configuration_files/mix_accept_reject_setting.feature +0 -84
  78. data/features/configuration_files/reject_setting.feature +0 -89
  79. data/features/configuration_files/schema_validation.feature +0 -59
  80. data/features/configuration_files/show_configuration_file.feature +0 -44
  81. data/features/configuration_files/unused_private_method.feature +0 -68
  82. data/features/configuration_loading.feature +0 -91
  83. data/features/configuration_via_source_comments/erroneous_source_comments.feature +0 -68
  84. data/features/configuration_via_source_comments/well_formed_source_comments.feature +0 -116
  85. data/features/locales.feature +0 -32
  86. data/features/programmatic_access.feature +0 -41
  87. data/features/rake_task/rake_task.feature +0 -138
  88. data/features/reports/codeclimate.feature +0 -59
  89. data/features/reports/json.feature +0 -59
  90. data/features/reports/reports.feature +0 -219
  91. data/features/reports/yaml.feature +0 -52
  92. data/features/rspec_matcher.feature +0 -41
  93. data/features/samples.feature +0 -305
  94. data/features/step_definitions/.rubocop.yml +0 -5
  95. data/features/step_definitions/reek_steps.rb +0 -102
  96. data/features/step_definitions/sample_file_steps.rb +0 -63
  97. data/features/support/env.rb +0 -33
  98. data/features/todo_list.feature +0 -108
  99. data/samples/checkstyle.xml +0 -7
  100. data/samples/clean_source/clean.rb +0 -6
  101. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +0 -29
  102. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +0 -30
  103. data/samples/configuration/corrupt.reek +0 -1
  104. data/samples/configuration/empty.reek +0 -0
  105. data/samples/configuration/full_configuration.reek +0 -13
  106. data/samples/configuration/full_mask.reek +0 -6
  107. data/samples/configuration/home/home.reek.yml +0 -4
  108. data/samples/configuration/partial_mask.reek +0 -4
  109. data/samples/configuration/regular_configuration/.reek.yml +0 -4
  110. data/samples/configuration/regular_configuration/empty_sub_directory/.gitignore +0 -0
  111. data/samples/configuration/with_excluded_paths.reek +0 -5
  112. data/samples/no_config_file/.keep +0 -0
  113. data/samples/paths.rb +0 -5
  114. data/samples/smelly_source/inline.rb +0 -704
  115. data/samples/smelly_source/optparse.rb +0 -1788
  116. data/samples/smelly_source/redcloth.rb +0 -1130
  117. data/samples/smelly_source/ruby.rb +0 -368
  118. data/samples/smelly_source/smelly.rb +0 -7
  119. data/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +0 -5
  120. data/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +0 -2
  121. data/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +0 -6
  122. data/samples/source_with_exclude_paths/nested/uncommunicative_variable_name.rb +0 -6
  123. data/samples/source_with_hidden_directories/.hidden/hidden.rb +0 -1
  124. data/samples/source_with_hidden_directories/not_hidden.rb +0 -1
  125. data/samples/source_with_non_ruby_files/gibberish +0 -1
  126. data/samples/source_with_non_ruby_files/python_source.py +0 -1
  127. data/samples/source_with_non_ruby_files/ruby.rb +0 -6
  128. data/spec/performance/reek/smell_detectors/runtime_speed_spec.rb +0 -15
  129. data/spec/quality/documentation_spec.rb +0 -41
  130. data/spec/quality/reek_source_spec.rb +0 -11
  131. data/spec/reek/ast/node_spec.rb +0 -211
  132. data/spec/reek/ast/object_refs_spec.rb +0 -83
  133. data/spec/reek/ast/reference_collector_spec.rb +0 -47
  134. data/spec/reek/ast/sexp_extensions_spec.rb +0 -498
  135. data/spec/reek/cli/application_spec.rb +0 -168
  136. data/spec/reek/cli/command/report_command_spec.rb +0 -44
  137. data/spec/reek/cli/command/todo_list_command_spec.rb +0 -86
  138. data/spec/reek/cli/options_spec.rb +0 -51
  139. data/spec/reek/cli/silencer_spec.rb +0 -28
  140. data/spec/reek/code_comment_spec.rb +0 -184
  141. data/spec/reek/configuration/app_configuration_spec.rb +0 -195
  142. data/spec/reek/configuration/configuration_file_finder_spec.rb +0 -230
  143. data/spec/reek/configuration/default_directive_spec.rb +0 -13
  144. data/spec/reek/configuration/directory_directives_spec.rb +0 -122
  145. data/spec/reek/configuration/excluded_paths_spec.rb +0 -25
  146. data/spec/reek/configuration/rake_task_converter_spec.rb +0 -33
  147. data/spec/reek/configuration/schema_validator_spec.rb +0 -165
  148. data/spec/reek/context/code_context_spec.rb +0 -192
  149. data/spec/reek/context/ghost_context_spec.rb +0 -60
  150. data/spec/reek/context/method_context_spec.rb +0 -72
  151. data/spec/reek/context/module_context_spec.rb +0 -55
  152. data/spec/reek/context/root_context_spec.rb +0 -12
  153. data/spec/reek/context/statement_counter_spec.rb +0 -24
  154. data/spec/reek/context_builder_spec.rb +0 -457
  155. data/spec/reek/detector_repository_spec.rb +0 -22
  156. data/spec/reek/documentation_link_spec.rb +0 -20
  157. data/spec/reek/errors/base_error_spec.rb +0 -13
  158. data/spec/reek/examiner_spec.rb +0 -309
  159. data/spec/reek/logging_error_handler_spec.rb +0 -24
  160. data/spec/reek/rake/task_spec.rb +0 -56
  161. data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +0 -22
  162. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +0 -126
  163. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +0 -51
  164. data/spec/reek/report/code_climate/code_climate_report_spec.rb +0 -56
  165. data/spec/reek/report/html_report_spec.rb +0 -19
  166. data/spec/reek/report/json_report_spec.rb +0 -58
  167. data/spec/reek/report/location_formatter_spec.rb +0 -32
  168. data/spec/reek/report/progress_formatter_spec.rb +0 -68
  169. data/spec/reek/report/text_report_spec.rb +0 -89
  170. data/spec/reek/report/xml_report_spec.rb +0 -24
  171. data/spec/reek/report/yaml_report_spec.rb +0 -55
  172. data/spec/reek/report_spec.rb +0 -28
  173. data/spec/reek/smell_configuration_spec.rb +0 -56
  174. data/spec/reek/smell_detectors/attribute_spec.rb +0 -197
  175. data/spec/reek/smell_detectors/base_detector_spec.rb +0 -50
  176. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +0 -93
  177. data/spec/reek/smell_detectors/class_variable_spec.rb +0 -106
  178. data/spec/reek/smell_detectors/control_parameter_spec.rb +0 -300
  179. data/spec/reek/smell_detectors/data_clump_spec.rb +0 -134
  180. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +0 -211
  181. data/spec/reek/smell_detectors/feature_envy_spec.rb +0 -295
  182. data/spec/reek/smell_detectors/instance_variable_assumption_spec.rb +0 -96
  183. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +0 -226
  184. data/spec/reek/smell_detectors/long_parameter_list_spec.rb +0 -61
  185. data/spec/reek/smell_detectors/long_yield_list_spec.rb +0 -49
  186. data/spec/reek/smell_detectors/manual_dispatch_spec.rb +0 -75
  187. data/spec/reek/smell_detectors/missing_safe_method_spec.rb +0 -68
  188. data/spec/reek/smell_detectors/module_initialize_spec.rb +0 -77
  189. data/spec/reek/smell_detectors/nested_iterators_spec.rb +0 -333
  190. data/spec/reek/smell_detectors/nil_check_spec.rb +0 -100
  191. data/spec/reek/smell_detectors/repeated_conditional_spec.rb +0 -100
  192. data/spec/reek/smell_detectors/subclassed_from_core_class_spec.rb +0 -77
  193. data/spec/reek/smell_detectors/too_many_constants_spec.rb +0 -144
  194. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +0 -132
  195. data/spec/reek/smell_detectors/too_many_methods_spec.rb +0 -54
  196. data/spec/reek/smell_detectors/too_many_statements_spec.rb +0 -90
  197. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +0 -78
  198. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +0 -78
  199. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +0 -147
  200. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +0 -201
  201. data/spec/reek/smell_detectors/unused_parameters_spec.rb +0 -114
  202. data/spec/reek/smell_detectors/unused_private_method_spec.rb +0 -205
  203. data/spec/reek/smell_detectors/utility_function_spec.rb +0 -309
  204. data/spec/reek/smell_warning_spec.rb +0 -137
  205. data/spec/reek/source/source_code_spec.rb +0 -79
  206. data/spec/reek/source/source_locator_spec.rb +0 -166
  207. data/spec/reek/spec/should_reek_of_spec.rb +0 -153
  208. data/spec/reek/spec/should_reek_only_of_spec.rb +0 -91
  209. data/spec/reek/spec/should_reek_spec.rb +0 -52
  210. data/spec/reek/spec/smell_matcher_spec.rb +0 -87
  211. data/spec/reek/tree_dresser_spec.rb +0 -46
  212. data/spec/spec_helper.rb +0 -110
  213. data/tasks/configuration.rake +0 -18
  214. data/tasks/console.rake +0 -5
  215. data/tasks/reek.rake +0 -6
  216. data/tasks/rubocop.rake +0 -11
  217. data/tasks/test.rake +0 -32
@@ -1,1788 +0,0 @@
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