optparse-plus 3.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CHANGES.md +66 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +201 -0
- data/README.rdoc +173 -0
- data/Rakefile +94 -0
- data/bin/optparse_plus +130 -0
- data/fix.rb +29 -0
- data/lib/optparse-plus.rb +1 -0
- data/lib/optparse_plus.rb +15 -0
- data/lib/optparse_plus/argv_parser.rb +50 -0
- data/lib/optparse_plus/cli.rb +116 -0
- data/lib/optparse_plus/cli_logger.rb +133 -0
- data/lib/optparse_plus/cli_logging.rb +138 -0
- data/lib/optparse_plus/cucumber.rb +119 -0
- data/lib/optparse_plus/error.rb +32 -0
- data/lib/optparse_plus/execution_strategy/base.rb +34 -0
- data/lib/optparse_plus/execution_strategy/jvm.rb +37 -0
- data/lib/optparse_plus/execution_strategy/mri.rb +16 -0
- data/lib/optparse_plus/execution_strategy/open_3.rb +16 -0
- data/lib/optparse_plus/execution_strategy/open_4.rb +22 -0
- data/lib/optparse_plus/execution_strategy/rbx_open_4.rb +12 -0
- data/lib/optparse_plus/exit_now.rb +40 -0
- data/lib/optparse_plus/main.rb +603 -0
- data/lib/optparse_plus/process_status.rb +45 -0
- data/lib/optparse_plus/sh.rb +223 -0
- data/lib/optparse_plus/test/base_integration_test.rb +31 -0
- data/lib/optparse_plus/test/integration_test_assertions.rb +65 -0
- data/lib/optparse_plus/version.rb +3 -0
- data/optparse_plus.gemspec +28 -0
- data/templates/full/.gitignore.erb +4 -0
- data/templates/full/README.rdoc.erb +24 -0
- data/templates/full/Rakefile.erb +71 -0
- data/templates/full/_license_head.txt.erb +2 -0
- data/templates/full/apache_LICENSE.txt.erb +203 -0
- data/templates/full/bin/executable.erb +45 -0
- data/templates/full/custom_LICENSE.txt.erb +0 -0
- data/templates/full/gplv2_LICENSE.txt.erb +14 -0
- data/templates/full/gplv3_LICENSE.txt.erb +14 -0
- data/templates/full/mit_LICENSE.txt.erb +7 -0
- data/templates/rspec/spec/something_spec.rb.erb +5 -0
- data/templates/test_unit/test/integration/test_cli.rb.erb +11 -0
- data/templates/test_unit/test/unit/test_something.rb.erb +7 -0
- data/test/integration/base_integration_test.rb +60 -0
- data/test/integration/test_bootstrap.rb +150 -0
- data/test/integration/test_cli.rb +21 -0
- data/test/integration/test_license.rb +56 -0
- data/test/integration/test_readme.rb +53 -0
- data/test/integration/test_rspec.rb +28 -0
- data/test/integration/test_version.rb +21 -0
- data/test/unit/base_test.rb +19 -0
- data/test/unit/command_for_tests.sh +7 -0
- data/test/unit/execution_strategy/test_base.rb +24 -0
- data/test/unit/execution_strategy/test_jvm.rb +77 -0
- data/test/unit/execution_strategy/test_mri.rb +32 -0
- data/test/unit/execution_strategy/test_open_3.rb +70 -0
- data/test/unit/execution_strategy/test_open_4.rb +86 -0
- data/test/unit/execution_strategy/test_rbx_open_4.rb +25 -0
- data/test/unit/test/test_integration_test_assertions.rb +211 -0
- data/test/unit/test_cli_logger.rb +219 -0
- data/test/unit/test_cli_logging.rb +243 -0
- data/test/unit/test_exit_now.rb +37 -0
- data/test/unit/test_main.rb +840 -0
- data/test/unit/test_sh.rb +404 -0
- metadata +260 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
# Provides easier access to a shared OptparsePlus::CLILogger instance.
|
3
|
+
#
|
4
|
+
# Include this module into your class, and #logger provides access to a shared logger.
|
5
|
+
# This is handy if you want all of your clases to have access to the same logger, but
|
6
|
+
# don't want to (or aren't able to) pass it around to each class.
|
7
|
+
#
|
8
|
+
# This also provides methods for direct logging without going through the #logger
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# class MyClass
|
13
|
+
# include OptparsePlus::CLILogging
|
14
|
+
#
|
15
|
+
# def doit
|
16
|
+
# debug("About to doit!")
|
17
|
+
# if results
|
18
|
+
# info("We did it!"
|
19
|
+
# else
|
20
|
+
# error("Something went wrong")
|
21
|
+
# end
|
22
|
+
# debug("Done doing it")
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# Note that every class that mixes this in shares the *same logger instance*, so if you call #change_logger, this
|
27
|
+
# will change the logger for all classes that mix this in. This is likely what you want.
|
28
|
+
module CLILogging
|
29
|
+
|
30
|
+
def self.included(k)
|
31
|
+
k.extend(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Access the shared logger. All classes that include this module
|
35
|
+
# will get the same logger via this method.
|
36
|
+
def logger
|
37
|
+
@@logger ||= CLILogger.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# Change the global logger that includers will use. Useful if you
|
41
|
+
# don't want the default configured logger. Note that the +change_logger+
|
42
|
+
# version is preferred because Ruby will often parse <tt>logger = Logger.new</tt> as
|
43
|
+
# the declaration of, and assignment to, of a local variable. You'd need to
|
44
|
+
# do <tt>self.logger=Logger.new</tt> to be sure. This method
|
45
|
+
# is a bit easier.
|
46
|
+
#
|
47
|
+
# +new_logger+:: the new logger. May not be nil and should be a logger of some kind
|
48
|
+
def change_logger(new_logger)
|
49
|
+
raise ArgumentError,"Logger may not be nil" if new_logger.nil?
|
50
|
+
@@logger = new_logger
|
51
|
+
@@logger.level = @log_level if defined?(@log_level) && @log_level
|
52
|
+
end
|
53
|
+
|
54
|
+
alias logger= change_logger
|
55
|
+
|
56
|
+
|
57
|
+
# pass-through to <tt>logger.debug(progname,&block)</tt>
|
58
|
+
def debug(progname = nil, &block); logger.debug(progname,&block); end
|
59
|
+
# pass-through to <tt>logger.info(progname,&block)</tt>
|
60
|
+
def info(progname = nil, &block); logger.info(progname,&block); end
|
61
|
+
# pass-through to <tt>logger.warn(progname,&block)</tt>
|
62
|
+
def warn(progname = nil, &block); logger.warn(progname,&block); end
|
63
|
+
# pass-through to <tt>logger.error(progname,&block)</tt>
|
64
|
+
def error(progname = nil, &block); logger.error(progname,&block); end
|
65
|
+
# pass-through to <tt>logger.fatal(progname,&block)</tt>
|
66
|
+
def fatal(progname = nil, &block); logger.fatal(progname,&block); end
|
67
|
+
|
68
|
+
LOG_LEVELS = {
|
69
|
+
'debug' => Logger::DEBUG,
|
70
|
+
'info' => Logger::INFO,
|
71
|
+
'warn' => Logger::WARN,
|
72
|
+
'error' => Logger::ERROR,
|
73
|
+
'fatal' => Logger::FATAL,
|
74
|
+
}
|
75
|
+
|
76
|
+
# Call this *if* you've included OptparsePlus::Main to set up a <tt>--log-level</tt> option for your app
|
77
|
+
# that will allow the user to configure the logging level. You can pass an optional hash with
|
78
|
+
# <tt>:toggle_debug_on_signal => <SIGNAME></tt> to enable runtime toggling of the log level by sending the
|
79
|
+
# signal <tt><SIGNAME></tt> to your app
|
80
|
+
#
|
81
|
+
# +args+:: optional hash
|
82
|
+
#
|
83
|
+
# Example:
|
84
|
+
#
|
85
|
+
# main do
|
86
|
+
# # your app
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# use_log_level_option
|
90
|
+
#
|
91
|
+
# go!
|
92
|
+
#
|
93
|
+
# Example with runtime toggling:
|
94
|
+
#
|
95
|
+
#
|
96
|
+
# main do
|
97
|
+
# # your app
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# use_log_level_option :toggle_debug_on_signal => 'USR1'
|
101
|
+
#
|
102
|
+
# go!
|
103
|
+
def use_log_level_option(args = {})
|
104
|
+
on("--log-level LEVEL",LOG_LEVELS,'Set the logging level',
|
105
|
+
'(' + LOG_LEVELS.keys.join('|') + ')',
|
106
|
+
'(Default: info)') do |level|
|
107
|
+
@log_level = level
|
108
|
+
@log_level_original = level
|
109
|
+
@log_level_toggled = false
|
110
|
+
logger.level = level
|
111
|
+
|
112
|
+
setup_toggle_trap(args[:toggle_debug_on_signal])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
# Call this to toggle the log level between <tt>debug</tt> and its initial value
|
119
|
+
def toggle_log_level
|
120
|
+
@log_level_original = logger.level unless @log_level_toggled
|
121
|
+
logger.level = if @log_level_toggled
|
122
|
+
@log_level_original
|
123
|
+
else
|
124
|
+
LOG_LEVELS['debug']
|
125
|
+
end
|
126
|
+
@log_level_toggled = !@log_level_toggled
|
127
|
+
@log_level = logger.level
|
128
|
+
end
|
129
|
+
|
130
|
+
def setup_toggle_trap(signal)
|
131
|
+
if signal
|
132
|
+
Signal.trap(signal) do
|
133
|
+
toggle_log_level
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
#
|
3
|
+
# **NOTE!** Cucumber is not recommened or supported by optparse_plus, as Aruba has diverged too much. This
|
4
|
+
# file is left here to allow you to update optparse_plus but still use Cucumber & Aruba on older versions.
|
5
|
+
#
|
6
|
+
# By <tt>require</tt>'ing <tt>optparse_plus/cucumber</tt> in your Cucumber setup (e.g. in <tt>env.rb</tt>), you
|
7
|
+
# gain access to the steps defined in this file. They provide you with the following:
|
8
|
+
#
|
9
|
+
# * Run <tt>command_to_run --help</tt> using aruba
|
10
|
+
#
|
11
|
+
# When I get help for "command_to_run"
|
12
|
+
#
|
13
|
+
# * Make sure that each option shows up in the help and has *some* sort of documentation. By default,
|
14
|
+
# the options won't be required to be negatable.
|
15
|
+
#
|
16
|
+
# Then the following options should be documented:
|
17
|
+
# |--force|
|
18
|
+
# |-x |
|
19
|
+
#
|
20
|
+
# Then the following options should be documented:
|
21
|
+
# |--force| which is negatable |
|
22
|
+
# |-x | which is not negatable |
|
23
|
+
#
|
24
|
+
# * Check an individual option for documentation:
|
25
|
+
#
|
26
|
+
# Then the option "--force" should be documented
|
27
|
+
# Then the option "--force" should be documented which is negatable
|
28
|
+
#
|
29
|
+
# * Checks that the help has a proper usage banner
|
30
|
+
#
|
31
|
+
# Then the banner should be present
|
32
|
+
#
|
33
|
+
# * Checks that the banner includes the version
|
34
|
+
#
|
35
|
+
# Then the banner should include the version
|
36
|
+
#
|
37
|
+
# * Checks that the usage banner indicates it takes options via <tt>[options]</tt>
|
38
|
+
#
|
39
|
+
# Then the banner should document that this app takes options
|
40
|
+
#
|
41
|
+
# * Do the opposite; check that you don't indicate options are accepted
|
42
|
+
#
|
43
|
+
# Then the banner should document that this app takes no options
|
44
|
+
#
|
45
|
+
# * Checks that the app's usage banner documents that its arguments are <tt>args</tt>
|
46
|
+
#
|
47
|
+
# Then the banner should document that this app's arguments are
|
48
|
+
# |foo|which is optional|
|
49
|
+
# |bar|which is required|
|
50
|
+
#
|
51
|
+
# * Do the opposite; check that your app doesn't take any arguments
|
52
|
+
#
|
53
|
+
# Then the banner should document that this app takes no arguments
|
54
|
+
#
|
55
|
+
# * Check for a usage description which occurs after the banner and a blank line
|
56
|
+
#
|
57
|
+
# Then there should be a one line summary of what the app does
|
58
|
+
#
|
59
|
+
module Cucumber
|
60
|
+
end
|
61
|
+
end
|
62
|
+
When /^I get help for "([^"]*)"$/ do |app_name|
|
63
|
+
@app_name = app_name
|
64
|
+
step %(I run `#{app_name} --help`)
|
65
|
+
end
|
66
|
+
|
67
|
+
Then /^the following options should be documented:$/ do |options|
|
68
|
+
options.raw.each do |option|
|
69
|
+
step %(the option "#{option[0]}" should be documented #{option[1]})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^the option "([^"]*)" should be documented(.*)$/ do |options,qualifiers|
|
74
|
+
options.split(',').map(&:strip).each do |option|
|
75
|
+
if qualifiers.strip == "which is negatable"
|
76
|
+
option = option.gsub(/^--/,"--[no-]")
|
77
|
+
end
|
78
|
+
step %(the output should match /\\s*#{Regexp.escape(option)}[\\s\\W]+\\w[\\s\\w][\\s\\w]+/)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Then /^the banner should be present$/ do
|
83
|
+
step %(the output should match /Usage: #{@app_name}/)
|
84
|
+
end
|
85
|
+
|
86
|
+
Then /^the banner should document that this app takes options$/ do
|
87
|
+
step %(the output should match /\[options\]/)
|
88
|
+
step %(the output should contain "Options")
|
89
|
+
end
|
90
|
+
|
91
|
+
Then /^the banner should document that this app's arguments are:$/ do |table|
|
92
|
+
expected_arguments = table.raw.map { |row|
|
93
|
+
option = row[0]
|
94
|
+
option = "[#{option}]" if row[1] == 'optional' || row[1] == 'which is optional'
|
95
|
+
option
|
96
|
+
}.join(' ')
|
97
|
+
step %(the output should contain "#{expected_arguments}")
|
98
|
+
end
|
99
|
+
|
100
|
+
Then /^the banner should document that this app takes no options$/ do
|
101
|
+
step %(the output should not contain "[options]")
|
102
|
+
step %(the output should not contain "Options")
|
103
|
+
end
|
104
|
+
|
105
|
+
Then /^the banner should document that this app takes no arguments$/ do
|
106
|
+
step %(the output should match /Usage: #{@app_name}\\s*\(\\[options\\]\)?$/)
|
107
|
+
end
|
108
|
+
|
109
|
+
Then /^the banner should include the version$/ do
|
110
|
+
step %(the output should match /v\\d+\\.\\d+\\.\\d+/)
|
111
|
+
end
|
112
|
+
|
113
|
+
Then /^there should be a one line summary of what the app does$/ do
|
114
|
+
output_lines = all_output.split(/\n/)
|
115
|
+
output_lines.size.should >= 3
|
116
|
+
# [0] is our banner, which we've checked for
|
117
|
+
output_lines[1].should match(/^\s*$/)
|
118
|
+
output_lines[2].should match(/^\w+\s+\w+/)
|
119
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
# Standard exception you can throw to exit with a given
|
3
|
+
# status code. Generally, you should prefer OptparsePlus::Main#exit_now! over using
|
4
|
+
# this directly, however you may wish to create a rich hierarchy of exceptions that extend from
|
5
|
+
# this in your app, so this is provided if you wish to do so.
|
6
|
+
class Error < StandardError
|
7
|
+
attr_reader :exit_code
|
8
|
+
# Create an Error with the given status code and message
|
9
|
+
def initialize(exit_code,message=nil)
|
10
|
+
super(message)
|
11
|
+
@exit_code = exit_code
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Thrown by certain methods when an externally-called command exits nonzero
|
16
|
+
class FailedCommandError < Error
|
17
|
+
|
18
|
+
# The command that caused the failure
|
19
|
+
attr_reader :command
|
20
|
+
|
21
|
+
# exit_code:: exit code of the command that caused this
|
22
|
+
# command:: the entire command-line that caused this
|
23
|
+
# custom_error_message:: an error message to show the user instead of the boilerplate one. Useful
|
24
|
+
# for allowing this exception to bubble up and exit the program, but to give
|
25
|
+
# the user something actionable.
|
26
|
+
def initialize(exit_code,command,custom_error_message = nil)
|
27
|
+
error_message = String(custom_error_message).empty? ? "Command '#{command}' exited #{exit_code}" : custom_error_message
|
28
|
+
super(exit_code,error_message)
|
29
|
+
@command = command
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# Base for any ExecutionStrategy implementation. Currently, this is nothing more than an interface
|
4
|
+
# specification.
|
5
|
+
class Base
|
6
|
+
# Executes the command and returns the results back. This
|
7
|
+
# should do no logging or other logic other than to execute the
|
8
|
+
# command and return the required results. If command is an
|
9
|
+
# array, use exec directly bypassing any tokenization, shell or
|
10
|
+
# otherwise; otherwise use the normal shell interpretation of
|
11
|
+
# the command string.
|
12
|
+
#
|
13
|
+
# command:: the command-line to run, as an Array or a String
|
14
|
+
#
|
15
|
+
# Returns an array of size 3:
|
16
|
+
# <tt>[0]</tt>:: The standard output of the command as a String, never nil
|
17
|
+
# <tt>[1]</tt>:: The standard error output of the command as a String, never nil
|
18
|
+
# <tt>[2]</tt>:: A Process::Status-like objects that responds to <tt>exitstatus</tt> which returns
|
19
|
+
# the exit code of the command (e.g. 0 for success).
|
20
|
+
def run_command(command)
|
21
|
+
subclass_must_implement!
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the class that, if caught by calling #run_command, represents the underlying command
|
25
|
+
# not existing. For example, in MRI Ruby, if you try to execute a non-existent command,
|
26
|
+
# you get a Errno::ENOENT.
|
27
|
+
def exception_meaning_command_not_found
|
28
|
+
subclass_must_implement!
|
29
|
+
end
|
30
|
+
protected
|
31
|
+
def subclass_must_implement!; raise "subclass must implement"; end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
4
|
+
#
|
5
|
+
# OptparsePlus::ExecutionStrategy for the JVM that uses JVM classes to run the command and get its results.
|
6
|
+
class JVM < Base
|
7
|
+
def run_command(command)
|
8
|
+
process = case command
|
9
|
+
when String then
|
10
|
+
java.lang.Runtime.get_runtime.exec(command)
|
11
|
+
else
|
12
|
+
java.lang.Runtime.get_runtime.exec(*command)
|
13
|
+
end
|
14
|
+
process.get_output_stream.close
|
15
|
+
stdout = input_stream_to_string(process.get_input_stream)
|
16
|
+
stderr = input_stream_to_string(process.get_error_stream)
|
17
|
+
exitstatus = process.wait_for
|
18
|
+
[stdout.chomp,stderr.chomp,OpenStruct.new(:exitstatus => exitstatus)]
|
19
|
+
end
|
20
|
+
|
21
|
+
def exception_meaning_command_not_found
|
22
|
+
NativeException
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def input_stream_to_string(is)
|
27
|
+
''.tap do |string|
|
28
|
+
ch = is.read
|
29
|
+
while ch != -1
|
30
|
+
string << ch
|
31
|
+
ch = is.read
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
4
|
+
#
|
5
|
+
# Base strategy for MRI rubies.
|
6
|
+
class MRI < Base
|
7
|
+
def run_command(command)
|
8
|
+
raise "subclass must implement"
|
9
|
+
end
|
10
|
+
|
11
|
+
def exception_meaning_command_not_found
|
12
|
+
Errno::ENOENT
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
4
|
+
#
|
5
|
+
# Implementation for modern Rubies that uses the built-in Open3 library
|
6
|
+
class Open_3 < MRI
|
7
|
+
def run_command(command)
|
8
|
+
stdout,stderr,status = case command
|
9
|
+
when String then Open3.capture3(command)
|
10
|
+
else Open3.capture3(*command)
|
11
|
+
end
|
12
|
+
[stdout.chomp,stderr.chomp,status]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
4
|
+
#
|
5
|
+
# ExecutionStrategy for non-modern Rubies that must rely on
|
6
|
+
# Open4 to get access to the standard output AND error.
|
7
|
+
class Open_4 < MRI
|
8
|
+
def run_command(command)
|
9
|
+
pid, stdin_io, stdout_io, stderr_io =
|
10
|
+
case command
|
11
|
+
when String then Open4::popen4(command)
|
12
|
+
else Open4::popen4(*command)
|
13
|
+
end
|
14
|
+
stdin_io.close
|
15
|
+
stdout = stdout_io.read
|
16
|
+
stderr = stderr_io.read
|
17
|
+
_ , status = Process::waitpid2(pid)
|
18
|
+
[stdout.chomp,stderr.chomp,status]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
module ExecutionStrategy
|
3
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
4
|
+
#
|
5
|
+
# For RBX; it throws a different exception when a command isn't found, so we override that here.
|
6
|
+
class RBXOpen_4 < Open_4
|
7
|
+
def exception_meaning_command_not_found
|
8
|
+
[Errno::EINVAL] + Array(super)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module OptparsePlus
|
2
|
+
# Provides #exit_now! and #help_now!. You might mix this into your business logic classes if they will
|
3
|
+
# need to exit the program with a human-readable error message.
|
4
|
+
module ExitNow
|
5
|
+
def self.included(k)
|
6
|
+
k.extend(self)
|
7
|
+
end
|
8
|
+
# Call this to exit the program immediately
|
9
|
+
# with the given error code and message.
|
10
|
+
#
|
11
|
+
# +exit_code+:: exit status you'd like to exit with
|
12
|
+
# +message+:: message to display to the user explaining the problem
|
13
|
+
#
|
14
|
+
# If +exit_code+ is a String and +message+ is omitted, +exit_code+ will be used as the message
|
15
|
+
# and the actual exit code will be 1.
|
16
|
+
#
|
17
|
+
# === Examples
|
18
|
+
#
|
19
|
+
# exit_now!(4,"Oh noes!")
|
20
|
+
# # => exit app with status 4 and show the user "Oh noes!" on stderr
|
21
|
+
# exit_now!("Oh noes!")
|
22
|
+
# # => exit app with status 1 and show the user "Oh noes!" on stderr
|
23
|
+
# exit_now!(4)
|
24
|
+
# # => exit app with status 4 and dont' give the user a message (how rude of you)
|
25
|
+
def exit_now!(exit_code,message=nil)
|
26
|
+
if exit_code.kind_of?(String) && message.nil?
|
27
|
+
raise OptparsePlus::Error.new(1,exit_code)
|
28
|
+
else
|
29
|
+
raise OptparsePlus::Error.new(exit_code,message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Exit the program as if the user made an error invoking your app, providing
|
34
|
+
# them the message as well as printing the help. This is useful if
|
35
|
+
# you have complex UI validation that can't be done by OptionParser.
|
36
|
+
def help_now!(message)
|
37
|
+
raise OptionParser::ParseError.new(message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|